Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 130 additions & 12 deletions docs/hub/scheduler.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,132 @@
# HUB · Scheduler

Access with `norbix.hub.scheduler`.

| Method | Verb | Path | Scope |
| --- | --- | --- | --- |
| `disable_scheduler` | `GET` | `/{version}/scheduler/disable` | `project` |
| `enable_scheduler` | `GET` | `/{version}/scheduler/enable` | `project` |
| `delete_scheduler_task` | `DELETE` | `/{version}/scheduler/tasks/{Id}` | `project` |
| `disable_scheduler_task` | `PUT` | `/{version}/scheduler/tasks/{Id}/disable` | `project` |
| `enable_scheduler_task` | `PUT` | `/{version}/scheduler/tasks/{Id}/enable` | `project` |
| `get_scheduler_task` | `GET` | `/{version}/scheduler/tasks/{id}` | `project` |
| `get_scheduler_tasks` | `GET` | `/{version}/scheduler/tasks` | `project` |
| `save_scheduler_task` | `POST` | `/{version}/scheduler/tasks` | `project` |
Access with `norbix.hub.scheduler` (and the same methods, awaited, on
`AsyncNorbix`). The scheduler runs a task on a cron schedule.

| Method | Verb | Path | Scope | Parameters go in |
| --- | --- | --- | --- | --- |
| `enable_scheduler` | `PUT` | `/{version}/scheduler/enable` | `project` | — |
| `disable_scheduler` | `PUT` | `/{version}/scheduler/disable` | `project` | — |
| `get_scheduler_tasks` | `GET` | `/{version}/scheduler/tasks` | `project` | query: `pageSize`, `startingAfter`, `endingBefore`, `type`, `enabled` |
| `get_scheduler_task` | `GET` | `/{version}/scheduler/tasks/{id}` | `project` | path: `id` |
| `save_scheduler_task` | `POST` | `/{version}/scheduler/tasks` | `project` | JSON body (see below) |
| `enable_scheduler_task` | `PUT` | `/{version}/scheduler/tasks/{Id}/enable` | `project` | path: `id` |
| `disable_scheduler_task` | `PUT` | `/{version}/scheduler/tasks/{Id}/disable` | `project` | path: `id` |
| `delete_scheduler_task` | `DELETE` | `/{version}/scheduler/tasks/{Id}` | `project` | path: `id` |

Keyword arguments other than `id`, `timeout` and `bearer_token` are the
request: the query string for `GET` / `DELETE`, the JSON body otherwise. Task
ids look like `tsk_…`. Every method, its path, its query and its body are
asserted in `tests/hub/test_scheduler.py`, for the sync and the async client.

> **Module enable / disable are `PUT`.** Versions up to 3.5 sent `GET`; the
> gateway now answers only `PUT` on `/scheduler/enable` and
> `/scheduler/disable`, so the call from an older SDK fails. Upgrade the SDK.

## Turn the module on

```python
norbix.hub.scheduler.enable_scheduler()
```

## Save a task (create or update)

**Only `EmailCampaign` tasks run today.** `PushCampaign`, `SmsCampaign`,
`CodeFunctionalCall` and `WebhookCall` exist in the gateway's type list, but
it refuses them on save.

The body:

| field | required | meaning |
| --- | --- | --- |
| `initiatorUserId` | yes | the user the task runs as (`usr_…`): you, or a service user of this project |
| `name` | yes | display name |
| `cron` | yes | exactly **5 fields** (minute hour day-of-month month day-of-week), evaluated in **UTC** — `"0 9 * * 1"` is every Monday 09:00 UTC |
| `isEnabled` | yes | start enabled or not |
| `stopOnError` | yes | disable the task after a failed run |
| `task` | yes | what to run (below) |
| `taskId` | on update | the `tsk_…` id to update; leave it out to create |
| `description` | no | notes |

`task` is a dict:

| field | required | meaning |
| --- | --- | --- |
| `type` | yes | `"EmailCampaign"` — the name, as a string |
| `campaign` | yes | the email campaign to send: the same body the email campaign endpoints take — `source`, `templateId`, and the audience fields of that source |
| `databaseIntegrationId` | no | the database integration the campaign reads from |

`campaign["source"]` picks the audience: `AllUsers` (optional `rolesNames`,
`userTags`), `SpecifiedUsers` (`userRecipients`), `AccountUsers`
(`userRecipients`), `Email`, `Collection`. Every source also takes the optional
`integrationId`, `language`, `notes` and `mappedTokens`.

```python
saved = norbix.hub.scheduler.save_scheduler_task(
initiatorUserId="usr_123",
name="Weekly digest",
cron="0 9 * * 1", # Mondays 09:00 UTC
isEnabled=True,
stopOnError=False,
task={
"type": "EmailCampaign",
"campaign": {
"source": "AllUsers",
"templateId": "etpl_123",
},
},
)
task_id = saved["id"] # tsk_…
```

This goes on the wire as:

```json
{
"initiatorUserId": "usr_123",
"name": "Weekly digest",
"cron": "0 9 * * 1",
"isEnabled": true,
"stopOnError": false,
"task": {
"type": "EmailCampaign",
"campaign": { "source": "AllUsers", "templateId": "etpl_123" }
}
}
```

To update, send the same body with `taskId="tsk_…"`.

## Read tasks

```python
page = norbix.hub.scheduler.get_scheduler_tasks(pageSize=20, type="EmailCampaign", enabled=True)
for row in page["list"]["items"]:
print(row["taskId"], row["name"], row["cron"], row["isEnabled"])

one = norbix.hub.scheduler.get_scheduler_task(id="tsk_123")
# one["item"]["payloadJson"] holds the saved task body as JSON.
```

## Pause, resume, delete

```python
norbix.hub.scheduler.disable_scheduler_task(id="tsk_123")
norbix.hub.scheduler.enable_scheduler_task(id="tsk_123")
norbix.hub.scheduler.delete_scheduler_task(id="tsk_123")
```

## Async

```python
async with AsyncNorbix(project_id="proj_123", api_key="sk_...") as norbix:
body = {
"initiatorUserId": "usr_123",
"name": "Weekly digest",
"cron": "0 9 * * 1",
"isEnabled": True,
"stopOnError": False,
"task": {"type": "EmailCampaign", "campaign": {"source": "AllUsers", "templateId": "etpl_123"}},
}
saved = await norbix.hub.scheduler.save_scheduler_task(**body)
```
40 changes: 30 additions & 10 deletions src/norbix_python/hub/scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ def __init__(self, transport: Transport) -> None:
self._transport = transport

def disable_scheduler(self, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any:
"""GET /{version}/scheduler/disable"""
"""PUT /{version}/scheduler/disable"""
return self._transport.send(
target="hub",
path="/{version}/scheduler/disable",
method="GET",
method="PUT",
path_params={},
request=request,
scope="project",
Expand All @@ -23,11 +23,11 @@ def disable_scheduler(self, *, timeout: float | None = None, bearer_token: str |
)

def enable_scheduler(self, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any:
"""GET /{version}/scheduler/enable"""
"""PUT /{version}/scheduler/enable"""
return self._transport.send(
target="hub",
path="/{version}/scheduler/enable",
method="GET",
method="PUT",
path_params={},
request=request,
scope="project",
Expand Down Expand Up @@ -101,7 +101,17 @@ def get_scheduler_tasks(self, *, timeout: float | None = None, bearer_token: str
)

def save_scheduler_task(self, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any:
"""POST /{version}/scheduler/tasks"""
"""POST /{version}/scheduler/tasks

Create (no ``taskId``) or update (``taskId="tsk_..."``) a scheduled task.
Keyword arguments become the JSON body: ``initiatorUserId`` (the caller or a
project service user, ``usr_...``), ``name``, ``cron`` (5 fields, UTC),
``isEnabled``, ``stopOnError``, optional ``description`` and ``task``.
``task`` is ``{"type": "EmailCampaign", "campaign": {...}, "databaseIntegrationId": ...}``
— ``EmailCampaign`` is the only task type the gateway runs today; ``campaign``
is the email campaign body (``source``, ``templateId`` and that source's
audience fields). See docs/hub/scheduler.md.
"""
return self._transport.send(
target="hub",
path="/{version}/scheduler/tasks",
Expand All @@ -119,11 +129,11 @@ def __init__(self, transport: AsyncTransport) -> None:
self._transport = transport

async def disable_scheduler(self, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any:
"""GET /{version}/scheduler/disable"""
"""PUT /{version}/scheduler/disable"""
return await self._transport.send(
target="hub",
path="/{version}/scheduler/disable",
method="GET",
method="PUT",
path_params={},
request=request,
scope="project",
Expand All @@ -132,11 +142,11 @@ async def disable_scheduler(self, *, timeout: float | None = None, bearer_token:
)

async def enable_scheduler(self, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any:
"""GET /{version}/scheduler/enable"""
"""PUT /{version}/scheduler/enable"""
return await self._transport.send(
target="hub",
path="/{version}/scheduler/enable",
method="GET",
method="PUT",
path_params={},
request=request,
scope="project",
Expand Down Expand Up @@ -210,7 +220,17 @@ async def get_scheduler_tasks(self, *, timeout: float | None = None, bearer_toke
)

async def save_scheduler_task(self, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any:
"""POST /{version}/scheduler/tasks"""
"""POST /{version}/scheduler/tasks

Create (no ``taskId``) or update (``taskId="tsk_..."``) a scheduled task.
Keyword arguments become the JSON body: ``initiatorUserId`` (the caller or a
project service user, ``usr_...``), ``name``, ``cron`` (5 fields, UTC),
``isEnabled``, ``stopOnError``, optional ``description`` and ``task``.
``task`` is ``{"type": "EmailCampaign", "campaign": {...}, "databaseIntegrationId": ...}``
— ``EmailCampaign`` is the only task type the gateway runs today; ``campaign``
is the email campaign body (``source``, ``templateId`` and that source's
audience fields). See docs/hub/scheduler.md.
"""
return await self._transport.send(
target="hub",
path="/{version}/scheduler/tasks",
Expand Down
Loading
Loading