From 62581689798572cac6e6f4c33203fcf5dd4b0f4d Mon Sep 17 00:00:00 2001 From: Robin Genz Date: Sat, 3 Oct 2026 08:08:51 +0200 Subject: [PATCH] feat(automations): support multiple channels --- src/capawesome_cloud/models.py | 1 + src/capawesome_cloud/resources/automations.py | 15 +++++++++--- tests/test_resources.py | 23 +++++++++++++++++++ 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/capawesome_cloud/models.py b/src/capawesome_cloud/models.py index 86e91fa..3aec817 100644 --- a/src/capawesome_cloud/models.py +++ b/src/capawesome_cloud/models.py @@ -195,6 +195,7 @@ class AppAutomation(CapawesomeModel): platform: Optional[str] = None trigger_type: Optional[str] = None enabled: Optional[bool] = None + app_channel_ids: Optional[list[str]] = None app_id: Optional[str] = None created_at: Optional[datetime] = None updated_at: Optional[datetime] = None diff --git a/src/capawesome_cloud/resources/automations.py b/src/capawesome_cloud/resources/automations.py index 5457f21..142b1d0 100644 --- a/src/capawesome_cloud/resources/automations.py +++ b/src/capawesome_cloud/resources/automations.py @@ -21,7 +21,9 @@ "app_certificate_id": "appCertificateId", "app_certificate_name": "appCertificateName", "app_channel_id": "appChannelId", + "app_channel_ids": "appChannelIds", "app_channel_name": "appChannelName", + "app_channel_names": "appChannelNames", "app_configuration_id": "appConfigurationId", "app_configuration_name": "appConfigurationName", "app_destination_id": "appDestinationId", @@ -42,8 +44,12 @@ def create( ``name`` and ``trigger_type`` (``branch`` or ``tag``) are required. Provide further fields by their snake_case names (e.g. ``platform``, - ``trigger_pattern``, ``app_channel_id``). Resources can also be referenced - by name (e.g. ``app_channel_name``); ids take precedence. + ``trigger_pattern``, ``app_channel_ids``). Resources can also be referenced + by name (e.g. ``app_channel_names``); ids take precedence. + + Web builds are deployed to the channels in ``app_channel_ids`` (or + ``app_channel_names``) after they succeed; ``app_channel_id`` and + ``app_channel_name`` are deprecated. """ body = _map_write_fields({"name": name, "trigger_type": trigger_type, **fields}) return self._request_model("POST", self._base(app_id), AppAutomation, json=body) @@ -98,7 +104,10 @@ def get( ) def update(self, app_id: str, automation_id: str, **fields: object) -> AppAutomation: - """Update an automation. Provide fields by their snake_case names.""" + """Update an automation. Provide fields by their snake_case names. + + Omitted fields stay unchanged; pass ``app_channel_ids=[]`` to remove all channels. + """ body = _map_write_fields(fields) return self._request_model( "PATCH", f"{self._base(app_id)}/{automation_id}", AppAutomation, json=body diff --git a/tests/test_resources.py b/tests/test_resources.py index b9cbb6d..73deff3 100644 --- a/tests/test_resources.py +++ b/tests/test_resources.py @@ -126,6 +126,29 @@ def test_build_sends_channel_ids_as_list(client: CapawesomeCloud) -> None: assert route.calls.last.request.read() == b'{"appChannelIds":["ch1","ch2"]}' +@respx.mock +def test_automation_create_sends_channel_ids_and_parses(client: CapawesomeCloud) -> None: + route = respx.post(f"{BASE_URL}/v1/apps/app1/automations").mock( + return_value=httpx.Response(201, json={"id": "a1", "appChannelIds": ["ch1", "ch2"]}) + ) + automation = client.apps.automations.create( + "app1", name="Web", trigger_type="branch", app_channel_ids=["ch1", "ch2"] + ) + assert route.calls.last.request.read() == ( + b'{"name":"Web","triggerType":"branch","appChannelIds":["ch1","ch2"]}' + ) + assert automation.app_channel_ids == ["ch1", "ch2"] + + +@respx.mock +def test_automation_update_clears_channels_with_empty_list(client: CapawesomeCloud) -> None: + route = respx.patch(f"{BASE_URL}/v1/apps/app1/automations/a1").mock( + return_value=httpx.Response(200, json={"id": "a1", "appChannelIds": []}) + ) + client.apps.automations.update("app1", "a1", app_channel_ids=[]) + assert route.calls.last.request.read() == b'{"appChannelIds":[]}' + + @respx.mock def test_destination_create_sends_type_and_new_fields(client: CapawesomeCloud) -> None: route = respx.post(f"{BASE_URL}/v1/apps/app1/destinations").mock(