Skip to content

Commit 4fd1b6c

Browse files
committed
item: devices-read-side — get_push_devices and get_push_device, sync and async, 37 -> 39 routes
1 parent 6bae0fd commit 4fd1b6c

3 files changed

Lines changed: 124 additions & 3 deletions

File tree

‎docs/hub/push.md‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,28 @@ client.hub.notifications.register_device(
8080
)
8181
```
8282

83+
## Listing registered devices
84+
85+
`get_push_devices` returns the devices registered in the project, each with
86+
the user it belongs to. Narrow it with `userId`, `deviceKey` (the provider
87+
token) or `platform` (`ios`, `android`, `chrome`, `safari`, `expo`); a word
88+
outside that list is refused rather than answered with an empty page.
89+
90+
```python
91+
devices = client.hub.notifications.get_push_devices(platform="ios")
92+
```
93+
94+
Devices are stored inside their user, so a page is a page of **users** and
95+
carries every matching device those users hold. Follow `hasMore` rather than
96+
stopping at the first short page.
97+
98+
`get_push_device` takes one device id and answers with the device and its
99+
owner:
100+
101+
```python
102+
device = client.hub.notifications.get_push_device("pnd_123")
103+
```
104+
83105
## Known gaps
84106

85107
| what | why |

‎src/norbix_python/hub/notifications.py‎

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -919,6 +919,36 @@ def register_device(self, *, timeout: float | None = None, bearer_token: str | N
919919
bearer_token=bearer_token,
920920
)
921921

922+
def get_push_devices(self, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any:
923+
"""GET /{version}/notifications/push/devices
924+
925+
Narrow the list with userId, deviceKey (the provider token) or
926+
platform (ios, android, chrome, safari, expo).
927+
"""
928+
return self._transport.send(
929+
target="hub",
930+
path="/{version}/notifications/push/devices",
931+
method="GET",
932+
path_params={},
933+
request=request,
934+
scope="project",
935+
timeout=timeout,
936+
bearer_token=bearer_token,
937+
)
938+
939+
def get_push_device(self, id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any:
940+
"""GET /{version}/notifications/push/devices/{id}"""
941+
return self._transport.send(
942+
target="hub",
943+
path="/{version}/notifications/push/devices/{id}",
944+
method="GET",
945+
path_params={"id": id},
946+
request=request,
947+
scope="project",
948+
timeout=timeout,
949+
bearer_token=bearer_token,
950+
)
951+
922952
def get_all_contacts(self, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any:
923953
"""GET /{version}/notifications/contacts"""
924954
return self._transport.send(
@@ -2627,6 +2657,36 @@ async def register_device(self, *, timeout: float | None = None, bearer_token: s
26272657
bearer_token=bearer_token,
26282658
)
26292659

2660+
async def get_push_devices(self, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any:
2661+
"""GET /{version}/notifications/push/devices
2662+
2663+
Narrow the list with userId, deviceKey (the provider token) or
2664+
platform (ios, android, chrome, safari, expo).
2665+
"""
2666+
return await self._transport.send(
2667+
target="hub",
2668+
path="/{version}/notifications/push/devices",
2669+
method="GET",
2670+
path_params={},
2671+
request=request,
2672+
scope="project",
2673+
timeout=timeout,
2674+
bearer_token=bearer_token,
2675+
)
2676+
2677+
async def get_push_device(self, id: str, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any:
2678+
"""GET /{version}/notifications/push/devices/{id}"""
2679+
return await self._transport.send(
2680+
target="hub",
2681+
path="/{version}/notifications/push/devices/{id}",
2682+
method="GET",
2683+
path_params={"id": id},
2684+
request=request,
2685+
scope="project",
2686+
timeout=timeout,
2687+
bearer_token=bearer_token,
2688+
)
2689+
26302690
async def get_all_contacts(self, *, timeout: float | None = None, bearer_token: str | None = None, **request: Any) -> Any:
26312691
"""GET /{version}/notifications/contacts"""
26322692
return await self._transport.send(

‎tests/hub/test_push.py‎

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import json
44
from collections.abc import Callable
55
from typing import Any
6+
from urllib.parse import parse_qs, urlparse
67

78
import pytest
89

@@ -12,7 +13,7 @@
1213
1314
The generated test file asserts each push method's verb and that the URL is
1415
https. This file adds the two things it cannot see: the fully-resolved path of
15-
every one of the 37 push routes, and the polymorphic bodies that the server
16+
every one of the 39 push routes, and the polymorphic bodies that the server
1617
routes on.
1718
1819
Only the Fake provider is used for a send path — it is the sandbox that accepts
@@ -25,6 +26,7 @@
2526
NOTIFICATION_ID = "notif_1"
2627
TEMPLATE_ID = "tpl_1"
2728
INTEGRATION_ID = "int_1"
29+
DEVICE_ID = "pnd_1"
2830

2931
BASE = "/v2/notifications/push"
3032

@@ -201,6 +203,13 @@
201203
),
202204
# devices
203205
("register_device", "POST", f"{BASE}/devices", lambda m: m.register_device()),
206+
("get_push_devices", "GET", f"{BASE}/devices", lambda m: m.get_push_devices()),
207+
(
208+
"get_push_device",
209+
"GET",
210+
f"{BASE}/devices/{DEVICE_ID}",
211+
lambda m: m.get_push_device(DEVICE_ID),
212+
),
204213
]
205214

206215

@@ -225,8 +234,8 @@ def test_push_endpoint_hits_the_expected_route(
225234

226235

227236
def test_push_surface_size() -> None:
228-
"""37 live push routes. A new one changes this count, so it cannot arrive untested."""
229-
assert len(PUSH_CASES) == 37
237+
"""39 live push routes. A new one changes this count, so it cannot arrive untested."""
238+
assert len(PUSH_CASES) == 39
230239

231240

232241
def test_push_call_sends_auth_and_project_headers() -> None:
@@ -382,6 +391,36 @@ def test_register_device_sends_the_device_shape() -> None:
382391
assert body["userId"] == "user_1"
383392

384393

394+
def test_get_push_devices_narrows_by_user_token_and_platform() -> None:
395+
"""Gateway Hub.Push/Devices/GetAll.cs: the three filters travel as query fields."""
396+
client, transport = make_client()
397+
client.hub.notifications.get_push_devices(
398+
userId="user_1", deviceKey="device_1", platform="ios"
399+
)
400+
401+
assert transport.last_request is not None
402+
assert transport.last_request["method"] == "GET"
403+
404+
# A GET carries its filters in the query string, not a body.
405+
url = urlparse(transport.last_request["url"])
406+
assert url.path == f"{BASE}/devices"
407+
assert parse_qs(url.query) == {
408+
"userId": ["user_1"],
409+
"deviceKey": ["device_1"],
410+
"platform": ["ios"],
411+
}
412+
413+
414+
def test_get_push_device_fills_the_route_token() -> None:
415+
"""The device id is a route token, not a body field."""
416+
client, transport = make_client()
417+
client.hub.notifications.get_push_device(DEVICE_ID)
418+
419+
assert transport.last_request is not None
420+
assert transport.last_request["method"] == "GET"
421+
assert urlparse(transport.last_request["url"]).path == f"{BASE}/devices/{DEVICE_ID}"
422+
423+
385424
def test_async_module_exposes_the_same_push_surface() -> None:
386425
"""Every sync push method must have an async twin."""
387426
from norbix_python.hub.notifications import AsyncNotificationsModule, NotificationsModule

0 commit comments

Comments
 (0)