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
2 changes: 1 addition & 1 deletion examples/local_service_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def upsert_sandbox_listener(
updated = client.update_listener(
current.id,
webhook_url=public_webhook_url,
email=current.email,
emails=current.emails,
filters=current.filters,
name=current.name or preferred_listener_name,
active=True,
Expand Down
12 changes: 6 additions & 6 deletions notamify_sdk/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ def list_listeners(self) -> list[Listener]:
def create_listener(
self,
webhook_url: str | None = None,
email: str = "",
emails: list[str] | None = None,
filters: ListenerFilters | Mapping[str, Any] | None = None,
name: str = "",
active: bool | None = None,
Expand All @@ -175,7 +175,7 @@ def create_listener(
body = self._prepare_body(
self._build_listener_request_body(
webhook_url=webhook_url,
email=email,
emails=emails,
filters=filters,
name=name,
active=active,
Expand All @@ -192,7 +192,7 @@ def update_listener(
self,
listener_id: str,
webhook_url: str | None = None,
email: str = "",
emails: list[str] | None = None,
filters: ListenerFilters | Mapping[str, Any] | None = None,
name: str = "",
active: bool | None = None,
Expand All @@ -203,7 +203,7 @@ def update_listener(
body = self._prepare_body(
self._build_listener_request_body(
webhook_url=webhook_url,
email=email,
emails=emails,
filters=filters,
name=name,
active=active,
Expand Down Expand Up @@ -428,7 +428,7 @@ def _iterate_notam_pages(
def _build_listener_request_body(
self,
webhook_url: str | None,
email: str,
emails: list[str] | None,
filters: ListenerFilters | Mapping[str, Any] | None,
name: str,
active: bool | None,
Expand All @@ -438,7 +438,7 @@ def _build_listener_request_body(
) -> dict[str, Any]:
return {
"webhook_url": self._normalize_listener_text(webhook_url),
"email": self._normalize_listener_text(email),
"emails": emails,
"filters": filters or {},
"name": self._normalize_listener_text(name),
"active": active,
Expand Down
4 changes: 2 additions & 2 deletions notamify_sdk/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ def normalize_types(cls, value: Any) -> Any:
class ListenerRequest(NotamifyModel):
name: str | None = None
webhook_url: str | None = None
email: str | None = None
emails: list[str] | None = None
filters: ListenerFilters = Field(default_factory=ListenerFilters)
lifecycle: ListenerLifecycleRequest | None = None
active: bool | None = None
Expand All @@ -155,7 +155,7 @@ class Listener(NotamifyModel):
id: str = ""
name: str = ""
webhook_url: str = ""
email: str = ""
emails: list[str] = Field(default_factory=list)
filters: ListenerFilters = Field(default_factory=ListenerFilters)
lifecycle: ListenerLifecycle = Field(default_factory=ListenerLifecycle)
metadata: ListenerMetadata = Field(default_factory=ListenerMetadata)
Expand Down
8 changes: 4 additions & 4 deletions notamify_watcher_sdk/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def base_url(self, value: str) -> None:
def create_listener(
self,
webhook_url: str,
email: str = "",
emails: list[str] | None = None,
filters: Mapping[str, Any] | None = None,
name: str = "",
active: bool | None = None,
Expand All @@ -44,7 +44,7 @@ def create_listener(
) -> Listener:
return super().create_listener(
webhook_url=webhook_url,
email=email,
emails=emails,
filters=filters,
name=name,
active=active,
Expand All @@ -57,7 +57,7 @@ def update_listener(
self,
listener_id: str,
webhook_url: str,
email: str = "",
emails: list[str] | None = None,
filters: Mapping[str, Any] | None = None,
name: str = "",
active: bool | None = None,
Expand All @@ -68,7 +68,7 @@ def update_listener(
return super().update_listener(
listener_id=listener_id,
webhook_url=webhook_url,
email=email,
emails=emails,
filters=filters,
name=name,
active=active,
Expand Down
14 changes: 11 additions & 3 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ def do_GET(self):
"id": "l1",
"name": "listener-1",
"webhook_url": "https://x",
"emails": ["ops@example.com"],
"filters": {"notam_icao": ["KJFK"]},
"lifecycle": {"enabled": False, "types": []},
"metadata": {"notams_shipped": 7},
Expand Down Expand Up @@ -189,6 +190,7 @@ def do_POST(self):
"id": "new",
"name": body.get("name", ""),
"webhook_url": "https://x",
"emails": body.get("emails", []),
"filters": body.get("filters", {}),
"lifecycle": body.get("lifecycle", {"enabled": False, "types": []}),
"metadata": {"notams_shipped": 0},
Expand Down Expand Up @@ -268,6 +270,7 @@ def do_PUT(self):
"id": "l1",
"name": body.get("name", ""),
"webhook_url": "https://x2",
"emails": body.get("emails", []),
"filters": body.get("filters", {}),
"lifecycle": body.get("lifecycle", {"enabled": False, "types": []}),
"metadata": {"notams_shipped": 10},
Expand Down Expand Up @@ -335,9 +338,11 @@ def test_watcher_methods(self):
self.assertFalse(listeners[0].lifecycle.enabled)
self.assertFalse(listeners[0].lifecycle_enabled)
self.assertEqual(listeners[0].team.owner, "teammate-1")
self.assertEqual(listeners[0].emails, ["ops@example.com"])

created = client.create_listener(
"https://x",
emails=["created@example.com"],
mode="sandbox",
lifecycle={"enabled": True, "types": ["cancelled"]},
)
Expand All @@ -347,6 +352,9 @@ def test_watcher_methods(self):
self.assertEqual(created.lifecycle.types[0].value, "CANCELLED")
self.assertTrue(created.lifecycle_enabled)
self.assertEqual(created.webhook_secret, "nmf_wh_new_listener")
self.assertEqual(created.emails, ["created@example.com"])
self.assertEqual(_Handler.last_create_body.get("emails"), ["created@example.com"])
self.assertNotIn("email", _Handler.last_create_body)
self.assertEqual(_Handler.last_create_body.get("mode"), "sandbox")
self.assertEqual(
_Handler.last_create_body.get("lifecycle"),
Expand Down Expand Up @@ -375,10 +383,10 @@ def test_watcher_methods(self):

def test_update_listener_preserves_explicit_empty_fields(self):
client = NotamifyClient(token="t", watcher_base_url=self.base_url, api_base_url=self.base_url)
client.update_listener("l1", "https://x2", email="", name="")
self.assertIn("email", _Handler.last_update_body)
client.update_listener("l1", "https://x2", emails=[], name="")
self.assertIn("emails", _Handler.last_update_body)
self.assertIn("name", _Handler.last_update_body)
self.assertEqual(_Handler.last_update_body["email"], "")
self.assertEqual(_Handler.last_update_body["emails"], [])
self.assertEqual(_Handler.last_update_body["name"], "")

def test_default_user_agent_tracks_sdk_version(self):
Expand Down
8 changes: 8 additions & 0 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,20 +210,27 @@ def test_listener_request_accepts_nested_lifecycle_shape(self):
model = ListenerRequest.model_validate(
{
"webhook_url": "https://example.com/hook",
"emails": ["ops@example.com"],
"filters": {},
"lifecycle": {
"enabled": True,
"types": ["cancelled", "REPLACED"],
},
}
)
self.assertEqual(model.emails, ["ops@example.com"])
self.assertTrue(model.lifecycle.enabled)
self.assertEqual([item.value for item in model.lifecycle.types], ["CANCELLED", "REPLACED"])

def test_listener_request_rejects_scalar_emails(self):
with self.assertRaises(ValidationError):
ListenerRequest.model_validate({"webhook_url": "https://example.com/hook", "emails": "ops@example.com"})

def test_listener_model_maps_legacy_lifecycle_enabled_to_nested_shape(self):
model = Listener.model_validate(
{
"id": "l1",
"emails": ["ops@example.com"],
"filters": {},
"metadata": {"notams_shipped": 0},
"active": True,
Expand All @@ -233,6 +240,7 @@ def test_listener_model_maps_legacy_lifecycle_enabled_to_nested_shape(self):
"updated_at": "2026-03-01T10:01:00Z",
}
)
self.assertEqual(model.emails, ["ops@example.com"])
self.assertTrue(model.lifecycle.enabled)
self.assertTrue(model.lifecycle_enabled)

Expand Down
Loading