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
69 changes: 57 additions & 12 deletions docs/hub/push.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,13 @@ field plus the audience's own fields:
| everyone in the project | `allUsers` | `rolesNames`, `userTags` (both optional filters) |
| a named list of project users | `specifiedUsers` | `userRecipients` |
| a named list of account users | `accountUsers` | `userRecipients` |
| rows of a database collection | `collection` | `schemaName`, `fields`, `fieldType` |
| raw device tokens | `devices` | `devices` |
| rows of a database collection | `collection` | `schemaName`, `fields` (the record fields that hold the recipient), `fieldType` (`User` or `Email`), optional `roleNames`, `languages` |
| raw device tokens | `devices` | `devices`: a list of `{ token, deliveryFamily }`, `deliveryFamily` one of `Ios`, `Android`, `Chrome`, `Safari`, `Expo` |

Every target also takes `templateId` (required) and the optional `integrationId`,
`language`, `notes`, `campaignTime` (Unix seconds) and `mappedTokens`. Note the
spelling: `rolesNames` on `allUsers`, but `roleNames` on `collection` — the
gateway names them differently.

```python
client.hub.notifications.create_push_campaign(
Expand All @@ -37,16 +42,21 @@ Send `source` as the name, not a number — the server reads it as a string.

`save_push_integration` works the same way, with `integration["provider"]`:

| provider | `provider` value |
|---|---|
| Fake (sandbox, never sends) | `Fake` |
| Android / Firebase | `AndroidFirebase` |
| Apple APNs | `AppleApns` |
| Chrome extension | `CodeMashChromePlugin` |
| Chrome web | `ChromeWeb` |
| Edge web | `EdgeWeb` |
| Firefox web | `FirefoxWeb` |
| Safari | `SafariPush` |
| provider | `provider` value | own fields |
|---|---|---|
| Fake (sandbox, never sends) | `Fake` | none |
| Android / Firebase | `AndroidFirebase` | `projectId`, `clientEmail`, `serviceAccountJson` |
| Apple APNs | `AppleApns` | `teamId`, `appBundleId`, `keyId`, `privateKey`, `isProduction` |
| Chrome extension | `ChromePush` | `extensionId` (a GUID), `vapidPublicKey`, `vapidPrivateKey`, optional `subject` |
| Chrome web | `ChromeWeb` | `vapidPublicKey`, `vapidPrivateKey`, optional `subject` |
| Edge web | `EdgeWeb` | `vapidPublicKey`, `vapidPrivateKey`, optional `subject` |
| Firefox web | `FirefoxWeb` | `vapidPublicKey`, `vapidPrivateKey`, optional `subject` |
| Safari | `SafariPush` | `websitePushId`, `certificateP12Base64`, `certificatePassword` |

Every provider also takes `integrationName` and `isEnabled`; send `integrationId`
to update an existing one. The Chrome extension value is `ChromePush`. The
generated types also list `CodeMashChromePlugin` and other `CodeMash*` values —
the server rejects those here with "Unsupported provider".

```python
client.hub.notifications.save_push_integration(
Expand All @@ -57,6 +67,41 @@ client.hub.notifications.save_push_integration(
Use `Fake` in tests and local development. It accepts a send and contacts no
push service, so nothing reaches a real device.

## Registering a device

A device is registered for one user. Send the device under `pushDeviceDto`
(`deviceOs` and `token` are required; `deviceId`, `brand`, `manufacturer`,
`modelName`, `deviceName`, `deviceType` are optional) plus `userId`:

```python
client.hub.notifications.register_device(
userId="user_123",
pushDeviceDto={"deviceOs": "iOS", "token": "<device token>"},
)
```

## Listing registered devices

`get_push_devices` returns the devices registered in the project, each with
the user it belongs to. Narrow it with `userId`, `deviceKey` (the provider
token) or `platform` (`ios`, `android`, `chrome`, `safari`, `expo`); a word
outside that list is refused rather than answered with an empty page.

```python
devices = client.hub.notifications.get_push_devices(platform="ios")
```

Devices are stored inside their user, so a page is a page of **users** and
carries every matching device those users hold. Follow `hasMore` rather than
stopping at the first short page.

`get_push_device` takes one device id and answers with the device and its
owner:

```python
device = client.hub.notifications.get_push_device("pnd_123")
```

## Known gaps

| what | why |
Expand Down
60 changes: 60 additions & 0 deletions src/norbix_python/hub/notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,36 @@ def register_device(self, *, timeout: float | None = None, bearer_token: str | N
bearer_token=bearer_token,
)

def get_push_devices(self, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any:
"""GET /{version}/notifications/push/devices

Narrow the list with userId, deviceKey (the provider token) or
platform (ios, android, chrome, safari, expo).
"""
return self._transport.send(
target="hub",
path="/{version}/notifications/push/devices",
method="GET",
path_params={},
request=request,
scope="project",
timeout=timeout,
bearer_token=bearer_token,
)

def get_push_device(self, id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any:
"""GET /{version}/notifications/push/devices/{id}"""
return self._transport.send(
target="hub",
path="/{version}/notifications/push/devices/{id}",
method="GET",
path_params={"id": id},
request=request,
scope="project",
timeout=timeout,
bearer_token=bearer_token,
)

def get_all_contacts(self, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any:
"""GET /{version}/notifications/contacts"""
return self._transport.send(
Expand Down Expand Up @@ -2627,6 +2657,36 @@ async def register_device(self, *, timeout: float | None = None, bearer_token: s
bearer_token=bearer_token,
)

async def get_push_devices(self, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any:
"""GET /{version}/notifications/push/devices

Narrow the list with userId, deviceKey (the provider token) or
platform (ios, android, chrome, safari, expo).
"""
return await self._transport.send(
target="hub",
path="/{version}/notifications/push/devices",
method="GET",
path_params={},
request=request,
scope="project",
timeout=timeout,
bearer_token=bearer_token,
)

async def get_push_device(self, id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any:
"""GET /{version}/notifications/push/devices/{id}"""
return await self._transport.send(
target="hub",
path="/{version}/notifications/push/devices/{id}",
method="GET",
path_params={"id": id},
request=request,
scope="project",
timeout=timeout,
bearer_token=bearer_token,
)

async def get_all_contacts(self, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any:
"""GET /{version}/notifications/contacts"""
return await self._transport.send(
Expand Down
Loading
Loading