Skip to content

Commit 25b1d84

Browse files
release: 7.8.0 (#124)
* feat(client): add custom JSON encoder for extended type support * feat(api): support list of recipients in send message * feat(api): add publish/replace methods, versions resource to tenants.templates * release: 7.8.0 --------- Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com>
1 parent fad3b92 commit 25b1d84

27 files changed

Lines changed: 1765 additions & 309 deletions

.release-please-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "7.7.1"
2+
".": "7.8.0"
33
}

.stats.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
configured_endpoints: 78
2-
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier%2Fcourier-4469c7d243ac17a71d48187ede11d7f6fd178d1006f2542c973259c5c37007fb.yml
3-
openapi_spec_hash: 2036a46b6fa7ac8eae981583bd452458
4-
config_hash: 93eb861d9572cea4d66edeab309e08c6
1+
configured_endpoints: 81
2+
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier%2Fcourier-3fc1c86b4a83a16393aaf17d1fb3ac6098d30dd057ba872973b57285a7a3f0d0.yml
3+
openapi_spec_hash: 02a545d217b13399f311e99561f9de1d
4+
config_hash: 0789c3cddc625bb9712b3bded274ab6c

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# Changelog
22

3+
## 7.8.0 (2026-02-06)
4+
5+
Full Changelog: [v7.7.1...v7.8.0](https://github.com/trycourier/courier-python/compare/v7.7.1...v7.8.0)
6+
7+
### Features
8+
9+
* **api:** add publish/replace methods, versions resource to tenants.templates ([2b39781](https://github.com/trycourier/courier-python/commit/2b397816d696929cf5ad38b137b3c624f897ee26))
10+
* **api:** support list of recipients in send message ([76ccdfd](https://github.com/trycourier/courier-python/commit/76ccdfd9aee812c97cbad0d10b13b5746db4b24e))
11+
* **client:** add custom JSON encoder for extended type support ([b641cb6](https://github.com/trycourier/courier-python/commit/b641cb6819d5727a7ec7f1c3cf240a91a28ffd42))
12+
313
## 7.7.1 (2026-01-27)
414

515
Full Changelog: [v7.7.0...v7.7.1](https://github.com/trycourier/courier-python/compare/v7.7.0...v7.7.1)

api.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -357,9 +357,14 @@ Types:
357357
from courier.types import (
358358
BaseTemplateTenantAssociation,
359359
DefaultPreferences,
360+
PostTenantTemplatePublishRequest,
361+
PostTenantTemplatePublishResponse,
362+
PutTenantTemplateRequest,
363+
PutTenantTemplateResponse,
360364
SubscriptionTopicNew,
361365
Tenant,
362366
TenantAssociation,
367+
TenantTemplateInput,
363368
TenantListResponse,
364369
TenantListUsersResponse,
365370
)
@@ -392,8 +397,16 @@ from courier.types.tenants import TemplateListResponse
392397

393398
Methods:
394399

395-
- <code title="get /tenants/{tenant_id}/templates/{template_id}">client.tenants.templates.<a href="./src/courier/resources/tenants/templates.py">retrieve</a>(template_id, \*, tenant_id) -> <a href="./src/courier/types/base_template_tenant_association.py">BaseTemplateTenantAssociation</a></code>
396-
- <code title="get /tenants/{tenant_id}/templates">client.tenants.templates.<a href="./src/courier/resources/tenants/templates.py">list</a>(tenant_id, \*\*<a href="src/courier/types/tenants/template_list_params.py">params</a>) -> <a href="./src/courier/types/tenants/template_list_response.py">TemplateListResponse</a></code>
400+
- <code title="get /tenants/{tenant_id}/templates/{template_id}">client.tenants.templates.<a href="./src/courier/resources/tenants/templates/templates.py">retrieve</a>(template_id, \*, tenant_id) -> <a href="./src/courier/types/base_template_tenant_association.py">BaseTemplateTenantAssociation</a></code>
401+
- <code title="get /tenants/{tenant_id}/templates">client.tenants.templates.<a href="./src/courier/resources/tenants/templates/templates.py">list</a>(tenant_id, \*\*<a href="src/courier/types/tenants/template_list_params.py">params</a>) -> <a href="./src/courier/types/tenants/template_list_response.py">TemplateListResponse</a></code>
402+
- <code title="post /tenants/{tenant_id}/templates/{template_id}/publish">client.tenants.templates.<a href="./src/courier/resources/tenants/templates/templates.py">publish</a>(template_id, \*, tenant_id, \*\*<a href="src/courier/types/tenants/template_publish_params.py">params</a>) -> <a href="./src/courier/types/post_tenant_template_publish_response.py">PostTenantTemplatePublishResponse</a></code>
403+
- <code title="put /tenants/{tenant_id}/templates/{template_id}">client.tenants.templates.<a href="./src/courier/resources/tenants/templates/templates.py">replace</a>(template_id, \*, tenant_id, \*\*<a href="src/courier/types/tenants/template_replace_params.py">params</a>) -> <a href="./src/courier/types/put_tenant_template_response.py">PutTenantTemplateResponse</a></code>
404+
405+
### Versions
406+
407+
Methods:
408+
409+
- <code title="get /tenants/{tenant_id}/templates/{template_id}/versions/{version}">client.tenants.templates.versions.<a href="./src/courier/resources/tenants/templates/versions.py">retrieve</a>(version, \*, tenant_id, template_id) -> <a href="./src/courier/types/base_template_tenant_association.py">BaseTemplateTenantAssociation</a></code>
397410

398411
# Translations
399412

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "trycourier"
3-
version = "7.7.1"
3+
version = "7.8.0"
44
description = "The official Python library for the Courier API"
55
dynamic = ["readme"]
66
license = "Apache-2.0"

src/courier/_base_client.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
APIConnectionError,
8787
APIResponseValidationError,
8888
)
89+
from ._utils._json import openapi_dumps
8990

9091
log: logging.Logger = logging.getLogger(__name__)
9192

@@ -554,8 +555,10 @@ def _build_request(
554555
kwargs["content"] = options.content
555556
elif isinstance(json_data, bytes):
556557
kwargs["content"] = json_data
557-
else:
558-
kwargs["json"] = json_data if is_given(json_data) else None
558+
elif not files:
559+
# Don't set content when JSON is sent as multipart/form-data,
560+
# since httpx's content param overrides other body arguments
561+
kwargs["content"] = openapi_dumps(json_data) if is_given(json_data) and json_data is not None else None
559562
kwargs["files"] = files
560563
else:
561564
headers.pop("Content-Type", None)

src/courier/_compat.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ def model_dump(
139139
exclude_defaults: bool = False,
140140
warnings: bool = True,
141141
mode: Literal["json", "python"] = "python",
142+
by_alias: bool | None = None,
142143
) -> dict[str, Any]:
143144
if (not PYDANTIC_V1) or hasattr(model, "model_dump"):
144145
return model.model_dump(
@@ -148,13 +149,12 @@ def model_dump(
148149
exclude_defaults=exclude_defaults,
149150
# warnings are not supported in Pydantic v1
150151
warnings=True if PYDANTIC_V1 else warnings,
152+
by_alias=by_alias,
151153
)
152154
return cast(
153155
"dict[str, Any]",
154156
model.dict( # pyright: ignore[reportDeprecated, reportUnnecessaryCast]
155-
exclude=exclude,
156-
exclude_unset=exclude_unset,
157-
exclude_defaults=exclude_defaults,
157+
exclude=exclude, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, by_alias=bool(by_alias)
158158
),
159159
)
160160

src/courier/_utils/_json.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import json
2+
from typing import Any
3+
from datetime import datetime
4+
from typing_extensions import override
5+
6+
import pydantic
7+
8+
from .._compat import model_dump
9+
10+
11+
def openapi_dumps(obj: Any) -> bytes:
12+
"""
13+
Serialize an object to UTF-8 encoded JSON bytes.
14+
15+
Extends the standard json.dumps with support for additional types
16+
commonly used in the SDK, such as `datetime`, `pydantic.BaseModel`, etc.
17+
"""
18+
return json.dumps(
19+
obj,
20+
cls=_CustomEncoder,
21+
# Uses the same defaults as httpx's JSON serialization
22+
ensure_ascii=False,
23+
separators=(",", ":"),
24+
allow_nan=False,
25+
).encode()
26+
27+
28+
class _CustomEncoder(json.JSONEncoder):
29+
@override
30+
def default(self, o: Any) -> Any:
31+
if isinstance(o, datetime):
32+
return o.isoformat()
33+
if isinstance(o, pydantic.BaseModel):
34+
return model_dump(o, exclude_unset=True, mode="json", by_alias=True)
35+
return super().default(o)

src/courier/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
22

33
__title__ = "courier"
4-
__version__ = "7.7.1" # x-release-please-version
4+
__version__ = "7.8.0" # x-release-please-version

0 commit comments

Comments
 (0)