From 3f0c7cb7fad212e0edbfd63b999693153f84fcd5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 9 Apr 2026 16:16:00 +0000 Subject: [PATCH 1/5] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 035a6ed..57882fd 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 103 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier%2Fcourier-d891c800ffe6b7eddce179c225367cedcb49dab758ebc03e352d945240918d96.yml openapi_spec_hash: 852d55192b8d7ad96c1f85cd9070ef64 -config_hash: a730d0e598dc108e89c016802008c9b3 +config_hash: ef56639a76a9e93201330e60dff4dbdc From ca4b529bf902fa70de9776bd66154d9bb5783b2b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 11 Apr 2026 02:52:07 +0000 Subject: [PATCH 2/5] fix: ensure file data are only sent as 1 parameter --- src/courier/_utils/_utils.py | 5 +++-- tests/test_extract_files.py | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/courier/_utils/_utils.py b/src/courier/_utils/_utils.py index eec7f4a..63b8cd6 100644 --- a/src/courier/_utils/_utils.py +++ b/src/courier/_utils/_utils.py @@ -86,8 +86,9 @@ def _extract_items( index += 1 if is_dict(obj): try: - # We are at the last entry in the path so we must remove the field - if (len(path)) == index: + # Remove the field if there are no more dict keys in the path, + # only "" traversal markers or end. + if all(p == "" for p in path[index:]): item = obj.pop(key) else: item = obj[key] diff --git a/tests/test_extract_files.py b/tests/test_extract_files.py index 02fd18b..b1010d7 100644 --- a/tests/test_extract_files.py +++ b/tests/test_extract_files.py @@ -35,6 +35,15 @@ def test_multiple_files() -> None: assert query == {"documents": [{}, {}]} +def test_top_level_file_array() -> None: + query = {"files": [b"file one", b"file two"], "title": "hello"} + assert extract_files(query, paths=[["files", ""]]) == [ + ("files[]", b"file one"), + ("files[]", b"file two"), + ] + assert query == {"title": "hello"} + + @pytest.mark.parametrize( "query,paths,expected", [ From 069bf70791042c6ac0233022a8066cf1724a173c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 16:37:41 +0000 Subject: [PATCH 3/5] fix(api): correct providers update method, notifications/routing_strategies return types --- .stats.yml | 6 ++-- api.md | 12 +++---- .../resources/notifications/notifications.py | 17 +++++----- src/courier/resources/providers/providers.py | 30 ++++++++++------- src/courier/resources/routing_strategies.py | 17 +++++----- src/courier/types/__init__.py | 4 --- ...notification_template_mutation_response.py | 21 ------------ src/courier/types/provider_update_params.py | 6 +++- .../routing_strategy_mutation_response.py | 12 ------- tests/api_resources/test_notifications.py | 33 +++++++++---------- .../api_resources/test_routing_strategies.py | 33 +++++++++---------- 11 files changed, 79 insertions(+), 112 deletions(-) delete mode 100644 src/courier/types/notification_template_mutation_response.py delete mode 100644 src/courier/types/routing_strategy_mutation_response.py diff --git a/.stats.yml b/.stats.yml index 57882fd..5fb91ca 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 103 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier%2Fcourier-d891c800ffe6b7eddce179c225367cedcb49dab758ebc03e352d945240918d96.yml -openapi_spec_hash: 852d55192b8d7ad96c1f85cd9070ef64 -config_hash: ef56639a76a9e93201330e60dff4dbdc +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier%2Fcourier-34190bf43e37ce0b5859f875b37d6742fc0cfc653f144d7bd88c31fba558fa07.yml +openapi_spec_hash: 90c7ba2d9c97e16546a4133fecb64847 +config_hash: 6037e4a9909efb8b2edac2dcc907b84f diff --git a/api.md b/api.md index c1d4cde..80d82a8 100644 --- a/api.md +++ b/api.md @@ -127,7 +127,7 @@ Methods: - client.providers.create(\*\*params) -> Provider - client.providers.retrieve(id) -> Provider -- client.providers.update(id, \*\*params) -> Provider +- client.providers.update(id, \*\*params) -> Provider - client.providers.list(\*\*params) -> ProviderListResponse - client.providers.delete(id) -> None @@ -352,7 +352,6 @@ from courier.types import ( NotificationLocalePutRequest, NotificationTemplateCreateRequest, NotificationTemplateGetResponse, - NotificationTemplateMutationResponse, NotificationTemplatePayload, NotificationTemplatePublishRequest, NotificationTemplateState, @@ -367,7 +366,7 @@ from courier.types import ( Methods: -- client.notifications.create(\*\*params) -> NotificationTemplateMutationResponse +- client.notifications.create(\*\*params) -> NotificationTemplateGetResponse - client.notifications.retrieve(id, \*\*params) -> NotificationTemplateGetResponse - client.notifications.list(\*\*params) -> NotificationListResponse - client.notifications.archive(id) -> None @@ -376,7 +375,7 @@ Methods: - client.notifications.put_content(id, \*\*params) -> NotificationContentMutationResponse - client.notifications.put_element(element_id, \*, id, \*\*params) -> NotificationContentMutationResponse - client.notifications.put_locale(locale_id, \*, id, \*\*params) -> NotificationContentMutationResponse -- client.notifications.replace(id, \*\*params) -> NotificationTemplateMutationResponse +- client.notifications.replace(id, \*\*params) -> NotificationTemplateGetResponse - client.notifications.retrieve_content(id, \*\*params) -> NotificationRetrieveContentResponse ## Checks @@ -403,7 +402,6 @@ from courier.types import ( RoutingStrategyCreateRequest, RoutingStrategyGetResponse, RoutingStrategyListResponse, - RoutingStrategyMutationResponse, RoutingStrategyReplaceRequest, RoutingStrategySummary, ) @@ -411,12 +409,12 @@ from courier.types import ( Methods: -- client.routing_strategies.create(\*\*params) -> RoutingStrategyMutationResponse +- client.routing_strategies.create(\*\*params) -> RoutingStrategyGetResponse - client.routing_strategies.retrieve(id) -> RoutingStrategyGetResponse - client.routing_strategies.list(\*\*params) -> RoutingStrategyListResponse - client.routing_strategies.archive(id) -> None - client.routing_strategies.list_notifications(id, \*\*params) -> AssociatedNotificationListResponse -- client.routing_strategies.replace(id, \*\*params) -> RoutingStrategyMutationResponse +- client.routing_strategies.replace(id, \*\*params) -> RoutingStrategyGetResponse # Profiles diff --git a/src/courier/resources/notifications/notifications.py b/src/courier/resources/notifications/notifications.py index 37a1f1b..93ac470 100644 --- a/src/courier/resources/notifications/notifications.py +++ b/src/courier/resources/notifications/notifications.py @@ -45,7 +45,6 @@ from ...types.notification_template_payload_param import NotificationTemplatePayloadParam from ...types.notification_content_mutation_response import NotificationContentMutationResponse from ...types.notification_retrieve_content_response import NotificationRetrieveContentResponse -from ...types.notification_template_mutation_response import NotificationTemplateMutationResponse from ...types.notification_template_version_list_response import NotificationTemplateVersionListResponse __all__ = ["NotificationsResource", "AsyncNotificationsResource"] @@ -86,7 +85,7 @@ def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> NotificationTemplateMutationResponse: + ) -> NotificationTemplateGetResponse: """Create a notification template. Requires all fields in the notification object. @@ -119,7 +118,7 @@ def create( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=NotificationTemplateMutationResponse, + cast_to=NotificationTemplateGetResponse, ) def retrieve( @@ -512,7 +511,7 @@ def replace( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> NotificationTemplateMutationResponse: + ) -> NotificationTemplateGetResponse: """Replace a notification template. All fields are required. @@ -546,7 +545,7 @@ def replace( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=NotificationTemplateMutationResponse, + cast_to=NotificationTemplateGetResponse, ) def retrieve_content( @@ -637,7 +636,7 @@ async def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> NotificationTemplateMutationResponse: + ) -> NotificationTemplateGetResponse: """Create a notification template. Requires all fields in the notification object. @@ -670,7 +669,7 @@ async def create( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=NotificationTemplateMutationResponse, + cast_to=NotificationTemplateGetResponse, ) async def retrieve( @@ -1067,7 +1066,7 @@ async def replace( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> NotificationTemplateMutationResponse: + ) -> NotificationTemplateGetResponse: """Replace a notification template. All fields are required. @@ -1101,7 +1100,7 @@ async def replace( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=NotificationTemplateMutationResponse, + cast_to=NotificationTemplateGetResponse, ) async def retrieve_content( diff --git a/src/courier/resources/providers/providers.py b/src/courier/resources/providers/providers.py index bdd369d..57bc9da 100644 --- a/src/courier/resources/providers/providers.py +++ b/src/courier/resources/providers/providers.py @@ -160,14 +160,17 @@ def update( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Provider: - """Update an existing provider configuration. + """Replace an existing provider configuration. - The `provider` key is required. All - other fields are optional — omitted fields are cleared from the stored - configuration (this is a full replacement, not a partial merge). + The `provider` key is required and + determines which provider-specific settings schema is applied. All other fields + are optional — omitted fields are cleared from the stored configuration (this is + a full replacement, not a partial merge). Changing the provider type for an + existing configuration is not supported. Args: - provider: The provider key identifying the type. + provider: The provider key identifying the type. Required on every request because it + selects the provider-specific settings schema for validation. alias: Updated alias. Omit to clear. @@ -187,7 +190,7 @@ def update( """ if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._post( + return self._put( path_template("/providers/{id}", id=id), body=maybe_transform( { @@ -408,14 +411,17 @@ async def update( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Provider: - """Update an existing provider configuration. + """Replace an existing provider configuration. - The `provider` key is required. All - other fields are optional — omitted fields are cleared from the stored - configuration (this is a full replacement, not a partial merge). + The `provider` key is required and + determines which provider-specific settings schema is applied. All other fields + are optional — omitted fields are cleared from the stored configuration (this is + a full replacement, not a partial merge). Changing the provider type for an + existing configuration is not supported. Args: - provider: The provider key identifying the type. + provider: The provider key identifying the type. Required on every request because it + selects the provider-specific settings schema for validation. alias: Updated alias. Omit to clear. @@ -435,7 +441,7 @@ async def update( """ if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._post( + return await self._put( path_template("/providers/{id}", id=id), body=await async_maybe_transform( { diff --git a/src/courier/resources/routing_strategies.py b/src/courier/resources/routing_strategies.py index fbbbd1c..9991097 100644 --- a/src/courier/resources/routing_strategies.py +++ b/src/courier/resources/routing_strategies.py @@ -28,7 +28,6 @@ from ..types.routing_strategy_list_response import RoutingStrategyListResponse from ..types.shared_params.message_channels import MessageChannels from ..types.shared_params.message_providers import MessageProviders -from ..types.routing_strategy_mutation_response import RoutingStrategyMutationResponse from ..types.associated_notification_list_response import AssociatedNotificationListResponse __all__ = ["RoutingStrategiesResource", "AsyncRoutingStrategiesResource"] @@ -69,7 +68,7 @@ def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> RoutingStrategyMutationResponse: + ) -> RoutingStrategyGetResponse: """Create a routing strategy. Requires a name and routing configuration at minimum. @@ -112,7 +111,7 @@ def create( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=RoutingStrategyMutationResponse, + cast_to=RoutingStrategyGetResponse, ) def retrieve( @@ -302,7 +301,7 @@ def replace( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> RoutingStrategyMutationResponse: + ) -> RoutingStrategyGetResponse: """Replace a routing strategy. Full document replacement; the caller must send the @@ -347,7 +346,7 @@ def replace( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=RoutingStrategyMutationResponse, + cast_to=RoutingStrategyGetResponse, ) @@ -386,7 +385,7 @@ async def create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> RoutingStrategyMutationResponse: + ) -> RoutingStrategyGetResponse: """Create a routing strategy. Requires a name and routing configuration at minimum. @@ -429,7 +428,7 @@ async def create( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=RoutingStrategyMutationResponse, + cast_to=RoutingStrategyGetResponse, ) async def retrieve( @@ -619,7 +618,7 @@ async def replace( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> RoutingStrategyMutationResponse: + ) -> RoutingStrategyGetResponse: """Replace a routing strategy. Full document replacement; the caller must send the @@ -664,7 +663,7 @@ async def replace( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=RoutingStrategyMutationResponse, + cast_to=RoutingStrategyGetResponse, ) diff --git a/src/courier/types/__init__.py b/src/courier/types/__init__.py index 67a0911..92eaece 100644 --- a/src/courier/types/__init__.py +++ b/src/courier/types/__init__.py @@ -227,7 +227,6 @@ from .notification_list_versions_params import NotificationListVersionsParams as NotificationListVersionsParams from .put_subscriptions_recipient_param import PutSubscriptionsRecipientParam as PutSubscriptionsRecipientParam from .notification_template_get_response import NotificationTemplateGetResponse as NotificationTemplateGetResponse -from .routing_strategy_mutation_response import RoutingStrategyMutationResponse as RoutingStrategyMutationResponse from .notification_template_payload_param import NotificationTemplatePayloadParam as NotificationTemplatePayloadParam from .notification_retrieve_content_params import NotificationRetrieveContentParams as NotificationRetrieveContentParams from .associated_notification_list_response import ( @@ -243,9 +242,6 @@ from .notification_retrieve_content_response import ( NotificationRetrieveContentResponse as NotificationRetrieveContentResponse, ) -from .notification_template_mutation_response import ( - NotificationTemplateMutationResponse as NotificationTemplateMutationResponse, -) from .routing_strategy_list_notifications_params import ( RoutingStrategyListNotificationsParams as RoutingStrategyListNotificationsParams, ) diff --git a/src/courier/types/notification_template_mutation_response.py b/src/courier/types/notification_template_mutation_response.py deleted file mode 100644 index d67e495..0000000 --- a/src/courier/types/notification_template_mutation_response.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing_extensions import Literal - -from .._models import BaseModel - -__all__ = ["NotificationTemplateMutationResponse", "Notification"] - - -class Notification(BaseModel): - id: str - """The ID of the created or updated template.""" - - -class NotificationTemplateMutationResponse(BaseModel): - """Response returned by POST and PUT operations.""" - - notification: Notification - - state: Literal["DRAFT", "PUBLISHED"] - """The template state after the operation. Always uppercase.""" diff --git a/src/courier/types/provider_update_params.py b/src/courier/types/provider_update_params.py index 4516ef1..20699f9 100644 --- a/src/courier/types/provider_update_params.py +++ b/src/courier/types/provider_update_params.py @@ -10,7 +10,11 @@ class ProviderUpdateParams(TypedDict, total=False): provider: Required[str] - """The provider key identifying the type.""" + """The provider key identifying the type. + + Required on every request because it selects the provider-specific settings + schema for validation. + """ alias: str """Updated alias. Omit to clear.""" diff --git a/src/courier/types/routing_strategy_mutation_response.py b/src/courier/types/routing_strategy_mutation_response.py deleted file mode 100644 index 849d7e3..0000000 --- a/src/courier/types/routing_strategy_mutation_response.py +++ /dev/null @@ -1,12 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .._models import BaseModel - -__all__ = ["RoutingStrategyMutationResponse"] - - -class RoutingStrategyMutationResponse(BaseModel): - """Response returned by create and replace operations.""" - - id: str - """The routing strategy ID (rs\\__ prefix).""" diff --git a/tests/api_resources/test_notifications.py b/tests/api_resources/test_notifications.py index 68c98ac..145a34b 100644 --- a/tests/api_resources/test_notifications.py +++ b/tests/api_resources/test_notifications.py @@ -14,7 +14,6 @@ NotificationTemplateGetResponse, NotificationContentMutationResponse, NotificationRetrieveContentResponse, - NotificationTemplateMutationResponse, NotificationTemplateVersionListResponse, ) @@ -40,7 +39,7 @@ def test_method_create(self, client: Courier) -> None: "tags": ["onboarding", "welcome"], }, ) - assert_matches_type(NotificationTemplateMutationResponse, notification, path=["response"]) + assert_matches_type(NotificationTemplateGetResponse, notification, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -59,7 +58,7 @@ def test_method_create_with_all_params(self, client: Courier) -> None: }, state="DRAFT", ) - assert_matches_type(NotificationTemplateMutationResponse, notification, path=["response"]) + assert_matches_type(NotificationTemplateGetResponse, notification, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -81,7 +80,7 @@ def test_raw_response_create(self, client: Courier) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" notification = response.parse() - assert_matches_type(NotificationTemplateMutationResponse, notification, path=["response"]) + assert_matches_type(NotificationTemplateGetResponse, notification, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -103,7 +102,7 @@ def test_streaming_response_create(self, client: Courier) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" notification = response.parse() - assert_matches_type(NotificationTemplateMutationResponse, notification, path=["response"]) + assert_matches_type(NotificationTemplateGetResponse, notification, path=["response"]) assert cast(Any, response.is_closed) is True @@ -558,7 +557,7 @@ def test_method_replace(self, client: Courier) -> None: "tags": ["updated"], }, ) - assert_matches_type(NotificationTemplateMutationResponse, notification, path=["response"]) + assert_matches_type(NotificationTemplateGetResponse, notification, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -578,7 +577,7 @@ def test_method_replace_with_all_params(self, client: Courier) -> None: }, state="PUBLISHED", ) - assert_matches_type(NotificationTemplateMutationResponse, notification, path=["response"]) + assert_matches_type(NotificationTemplateGetResponse, notification, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -601,7 +600,7 @@ def test_raw_response_replace(self, client: Courier) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" notification = response.parse() - assert_matches_type(NotificationTemplateMutationResponse, notification, path=["response"]) + assert_matches_type(NotificationTemplateGetResponse, notification, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -624,7 +623,7 @@ def test_streaming_response_replace(self, client: Courier) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" notification = response.parse() - assert_matches_type(NotificationTemplateMutationResponse, notification, path=["response"]) + assert_matches_type(NotificationTemplateGetResponse, notification, path=["response"]) assert cast(Any, response.is_closed) is True @@ -720,7 +719,7 @@ async def test_method_create(self, async_client: AsyncCourier) -> None: "tags": ["onboarding", "welcome"], }, ) - assert_matches_type(NotificationTemplateMutationResponse, notification, path=["response"]) + assert_matches_type(NotificationTemplateGetResponse, notification, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -739,7 +738,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncCourier) - }, state="DRAFT", ) - assert_matches_type(NotificationTemplateMutationResponse, notification, path=["response"]) + assert_matches_type(NotificationTemplateGetResponse, notification, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -761,7 +760,7 @@ async def test_raw_response_create(self, async_client: AsyncCourier) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" notification = await response.parse() - assert_matches_type(NotificationTemplateMutationResponse, notification, path=["response"]) + assert_matches_type(NotificationTemplateGetResponse, notification, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -783,7 +782,7 @@ async def test_streaming_response_create(self, async_client: AsyncCourier) -> No assert response.http_request.headers.get("X-Stainless-Lang") == "python" notification = await response.parse() - assert_matches_type(NotificationTemplateMutationResponse, notification, path=["response"]) + assert_matches_type(NotificationTemplateGetResponse, notification, path=["response"]) assert cast(Any, response.is_closed) is True @@ -1238,7 +1237,7 @@ async def test_method_replace(self, async_client: AsyncCourier) -> None: "tags": ["updated"], }, ) - assert_matches_type(NotificationTemplateMutationResponse, notification, path=["response"]) + assert_matches_type(NotificationTemplateGetResponse, notification, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -1258,7 +1257,7 @@ async def test_method_replace_with_all_params(self, async_client: AsyncCourier) }, state="PUBLISHED", ) - assert_matches_type(NotificationTemplateMutationResponse, notification, path=["response"]) + assert_matches_type(NotificationTemplateGetResponse, notification, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -1281,7 +1280,7 @@ async def test_raw_response_replace(self, async_client: AsyncCourier) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" notification = await response.parse() - assert_matches_type(NotificationTemplateMutationResponse, notification, path=["response"]) + assert_matches_type(NotificationTemplateGetResponse, notification, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -1304,7 +1303,7 @@ async def test_streaming_response_replace(self, async_client: AsyncCourier) -> N assert response.http_request.headers.get("X-Stainless-Lang") == "python" notification = await response.parse() - assert_matches_type(NotificationTemplateMutationResponse, notification, path=["response"]) + assert_matches_type(NotificationTemplateGetResponse, notification, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_routing_strategies.py b/tests/api_resources/test_routing_strategies.py index 7f334a5..8d314b6 100644 --- a/tests/api_resources/test_routing_strategies.py +++ b/tests/api_resources/test_routing_strategies.py @@ -12,7 +12,6 @@ from courier.types import ( RoutingStrategyGetResponse, RoutingStrategyListResponse, - RoutingStrategyMutationResponse, AssociatedNotificationListResponse, ) @@ -32,7 +31,7 @@ def test_method_create(self, client: Courier) -> None: "method": "single", }, ) - assert_matches_type(RoutingStrategyMutationResponse, routing_strategy, path=["response"]) + assert_matches_type(RoutingStrategyGetResponse, routing_strategy, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -84,7 +83,7 @@ def test_method_create_with_all_params(self, client: Courier) -> None: }, tags=["production", "email"], ) - assert_matches_type(RoutingStrategyMutationResponse, routing_strategy, path=["response"]) + assert_matches_type(RoutingStrategyGetResponse, routing_strategy, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -100,7 +99,7 @@ def test_raw_response_create(self, client: Courier) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" routing_strategy = response.parse() - assert_matches_type(RoutingStrategyMutationResponse, routing_strategy, path=["response"]) + assert_matches_type(RoutingStrategyGetResponse, routing_strategy, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -116,7 +115,7 @@ def test_streaming_response_create(self, client: Courier) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" routing_strategy = response.parse() - assert_matches_type(RoutingStrategyMutationResponse, routing_strategy, path=["response"]) + assert_matches_type(RoutingStrategyGetResponse, routing_strategy, path=["response"]) assert cast(Any, response.is_closed) is True @@ -304,7 +303,7 @@ def test_method_replace(self, client: Courier) -> None: "method": "single", }, ) - assert_matches_type(RoutingStrategyMutationResponse, routing_strategy, path=["response"]) + assert_matches_type(RoutingStrategyGetResponse, routing_strategy, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -357,7 +356,7 @@ def test_method_replace_with_all_params(self, client: Courier) -> None: }, tags=["production", "email", "v2"], ) - assert_matches_type(RoutingStrategyMutationResponse, routing_strategy, path=["response"]) + assert_matches_type(RoutingStrategyGetResponse, routing_strategy, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -374,7 +373,7 @@ def test_raw_response_replace(self, client: Courier) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" routing_strategy = response.parse() - assert_matches_type(RoutingStrategyMutationResponse, routing_strategy, path=["response"]) + assert_matches_type(RoutingStrategyGetResponse, routing_strategy, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -391,7 +390,7 @@ def test_streaming_response_replace(self, client: Courier) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" routing_strategy = response.parse() - assert_matches_type(RoutingStrategyMutationResponse, routing_strategy, path=["response"]) + assert_matches_type(RoutingStrategyGetResponse, routing_strategy, path=["response"]) assert cast(Any, response.is_closed) is True @@ -424,7 +423,7 @@ async def test_method_create(self, async_client: AsyncCourier) -> None: "method": "single", }, ) - assert_matches_type(RoutingStrategyMutationResponse, routing_strategy, path=["response"]) + assert_matches_type(RoutingStrategyGetResponse, routing_strategy, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -476,7 +475,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncCourier) - }, tags=["production", "email"], ) - assert_matches_type(RoutingStrategyMutationResponse, routing_strategy, path=["response"]) + assert_matches_type(RoutingStrategyGetResponse, routing_strategy, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -492,7 +491,7 @@ async def test_raw_response_create(self, async_client: AsyncCourier) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" routing_strategy = await response.parse() - assert_matches_type(RoutingStrategyMutationResponse, routing_strategy, path=["response"]) + assert_matches_type(RoutingStrategyGetResponse, routing_strategy, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -508,7 +507,7 @@ async def test_streaming_response_create(self, async_client: AsyncCourier) -> No assert response.http_request.headers.get("X-Stainless-Lang") == "python" routing_strategy = await response.parse() - assert_matches_type(RoutingStrategyMutationResponse, routing_strategy, path=["response"]) + assert_matches_type(RoutingStrategyGetResponse, routing_strategy, path=["response"]) assert cast(Any, response.is_closed) is True @@ -696,7 +695,7 @@ async def test_method_replace(self, async_client: AsyncCourier) -> None: "method": "single", }, ) - assert_matches_type(RoutingStrategyMutationResponse, routing_strategy, path=["response"]) + assert_matches_type(RoutingStrategyGetResponse, routing_strategy, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -749,7 +748,7 @@ async def test_method_replace_with_all_params(self, async_client: AsyncCourier) }, tags=["production", "email", "v2"], ) - assert_matches_type(RoutingStrategyMutationResponse, routing_strategy, path=["response"]) + assert_matches_type(RoutingStrategyGetResponse, routing_strategy, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -766,7 +765,7 @@ async def test_raw_response_replace(self, async_client: AsyncCourier) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" routing_strategy = await response.parse() - assert_matches_type(RoutingStrategyMutationResponse, routing_strategy, path=["response"]) + assert_matches_type(RoutingStrategyGetResponse, routing_strategy, path=["response"]) @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize @@ -783,7 +782,7 @@ async def test_streaming_response_replace(self, async_client: AsyncCourier) -> N assert response.http_request.headers.get("X-Stainless-Lang") == "python" routing_strategy = await response.parse() - assert_matches_type(RoutingStrategyMutationResponse, routing_strategy, path=["response"]) + assert_matches_type(RoutingStrategyGetResponse, routing_strategy, path=["response"]) assert cast(Any, response.is_closed) is True From e480a11271d1e4cee76c3050b611477a43a592e2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 17:02:32 +0000 Subject: [PATCH 4/5] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 5fb91ca..37cb440 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 103 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier%2Fcourier-34190bf43e37ce0b5859f875b37d6742fc0cfc653f144d7bd88c31fba558fa07.yml -openapi_spec_hash: 90c7ba2d9c97e16546a4133fecb64847 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier%2Fcourier-f0ac5c2fa86eabda773c549f5809d3e2d109e3e1d5a07f6fe1ace18355e4680a.yml +openapi_spec_hash: b25dc0b42dd9386ab4a025bb2143bde6 config_hash: 6037e4a9909efb8b2edac2dcc907b84f From 8720aff16dab04981623cdcbda177b3ea116fdd0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 17:02:50 +0000 Subject: [PATCH 5/5] release: 7.11.1 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ pyproject.toml | 2 +- src/courier/_version.py | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index e4cc178..e8175f5 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "7.11.0" + ".": "7.11.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e306dc..12e7bb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 7.11.1 (2026-04-13) + +Full Changelog: [v7.11.0...v7.11.1](https://github.com/trycourier/courier-python/compare/v7.11.0...v7.11.1) + +### Bug Fixes + +* **api:** correct providers update method, notifications/routing_strategies return types ([069bf70](https://github.com/trycourier/courier-python/commit/069bf70791042c6ac0233022a8066cf1724a173c)) +* ensure file data are only sent as 1 parameter ([ca4b529](https://github.com/trycourier/courier-python/commit/ca4b529bf902fa70de9776bd66154d9bb5783b2b)) + ## 7.11.0 (2026-04-08) Full Changelog: [v7.10.0...v7.11.0](https://github.com/trycourier/courier-python/compare/v7.10.0...v7.11.0) diff --git a/pyproject.toml b/pyproject.toml index e5428f9..f23035e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "trycourier" -version = "7.11.0" +version = "7.11.1" description = "The official Python library for the Courier API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/courier/_version.py b/src/courier/_version.py index 7f12b90..a93f7c7 100644 --- a/src/courier/_version.py +++ b/src/courier/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "courier" -__version__ = "7.11.0" # x-release-please-version +__version__ = "7.11.1" # x-release-please-version