From 797719ac713c52ac1abae12df3b3667ee3d536aa Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 1 May 2025 16:23:30 +0000 Subject: [PATCH 1/9] chore(internal): version bump --- .release-please-manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 37fcefaa..fea34540 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { ".": "1.0.0" -} +} \ No newline at end of file From 07aa7ef945058ad65a8b7586c74dabf156c69cfb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 2 May 2025 20:23:39 +0000 Subject: [PATCH 2/9] feat(api): api update --- .stats.yml | 4 ++-- src/knockapi/resources/objects/objects.py | 16 +++++++-------- src/knockapi/resources/users/users.py | 20 +++++++++---------- .../types/audience_add_members_params.py | 1 + .../types/audience_remove_members_params.py | 1 + .../inline_identify_user_request_param.py | 15 ++++++++++++++ src/knockapi/types/object_set_params.py | 8 ++++---- src/knockapi/types/user.py | 10 +++++----- src/knockapi/types/user_update_params.py | 10 +++++----- 9 files changed, 51 insertions(+), 34 deletions(-) diff --git a/.stats.yml b/.stats.yml index 6a1766d5..46b7dfc9 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 89 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/knock%2Fknock-641970ffdc1043cad290c3384d3d9c55b50cb709016a7513c03b2b84cf24fb31.yml -openapi_spec_hash: e10e8093bf0dbde38523b98bec436123 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/knock%2Fknock-5176d1bb3a88b127808b197c9ae1cf366fd56599fd8c7b7241ac829e72d69a42.yml +openapi_spec_hash: 92953a04021af2d0132fd9eebeb844b9 config_hash: 7460c5bd6d1a7041faa274f677789407 diff --git a/src/knockapi/resources/objects/objects.py b/src/knockapi/resources/objects/objects.py index 8469041f..7cd99792 100644 --- a/src/knockapi/resources/objects/objects.py +++ b/src/knockapi/resources/objects/objects.py @@ -708,10 +708,10 @@ def set( preferences: Inline set preferences for a recipient, where the key is the preference set id. - timezone: The timezone of the object. Must be a valid - [tz database time zone string](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). - Used for - [recurring schedules](/concepts/schedules#scheduling-workflows-with-recurring-schedules-for-recipients). + timezone: The timezone of the object. Must be a + valid [tz database time zone string](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). + Used + for [recurring schedules](/concepts/schedules#scheduling-workflows-with-recurring-schedules-for-recipients). extra_headers: Send extra headers @@ -1544,10 +1544,10 @@ async def set( preferences: Inline set preferences for a recipient, where the key is the preference set id. - timezone: The timezone of the object. Must be a valid - [tz database time zone string](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). - Used for - [recurring schedules](/concepts/schedules#scheduling-workflows-with-recurring-schedules-for-recipients). + timezone: The timezone of the object. Must be a + valid [tz database time zone string](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). + Used + for [recurring schedules](/concepts/schedules#scheduling-workflows-with-recurring-schedules-for-recipients). extra_headers: Send extra headers diff --git a/src/knockapi/resources/users/users.py b/src/knockapi/resources/users/users.py index 22d2d2fa..8fee6e79 100644 --- a/src/knockapi/resources/users/users.py +++ b/src/knockapi/resources/users/users.py @@ -140,15 +140,15 @@ def update( name: Display name of the user. - phone_number: The [E.164](https://www.twilio.com/docs/glossary/what-e164) phone number of the + phone_number: The [E.164](https://www.twilio.com/docs/glossary/what-e164) phone number of the user (required for SMS channels). preferences: Inline set preferences for a recipient, where the key is the preference set id. - timezone: The timezone of the user. Must be a valid - [tz database time zone string](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). - Used for - [recurring schedules](/concepts/schedules#scheduling-workflows-with-recurring-schedules-for-recipients). + timezone: The timezone of the user. Must be a + valid [tz database time zone string](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). + Used + for [recurring schedules](/concepts/schedules#scheduling-workflows-with-recurring-schedules-for-recipients). extra_headers: Send extra headers @@ -891,15 +891,15 @@ async def update( name: Display name of the user. - phone_number: The [E.164](https://www.twilio.com/docs/glossary/what-e164) phone number of the + phone_number: The [E.164](https://www.twilio.com/docs/glossary/what-e164) phone number of the user (required for SMS channels). preferences: Inline set preferences for a recipient, where the key is the preference set id. - timezone: The timezone of the user. Must be a valid - [tz database time zone string](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). - Used for - [recurring schedules](/concepts/schedules#scheduling-workflows-with-recurring-schedules-for-recipients). + timezone: The timezone of the user. Must be a + valid [tz database time zone string](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). + Used + for [recurring schedules](/concepts/schedules#scheduling-workflows-with-recurring-schedules-for-recipients). extra_headers: Send extra headers diff --git a/src/knockapi/types/audience_add_members_params.py b/src/knockapi/types/audience_add_members_params.py index 4043ee6a..2baf6174 100644 --- a/src/knockapi/types/audience_add_members_params.py +++ b/src/knockapi/types/audience_add_members_params.py @@ -20,6 +20,7 @@ class MemberUser(TypedDict, total=False): class Member(TypedDict, total=False): user: Required[MemberUser] + """An object containing the user's ID.""" tenant: Optional[str] """The unique identifier for the tenant.""" diff --git a/src/knockapi/types/audience_remove_members_params.py b/src/knockapi/types/audience_remove_members_params.py index f4ef4c3b..4b449606 100644 --- a/src/knockapi/types/audience_remove_members_params.py +++ b/src/knockapi/types/audience_remove_members_params.py @@ -20,6 +20,7 @@ class MemberUser(TypedDict, total=False): class Member(TypedDict, total=False): user: Required[MemberUser] + """An object containing the user's ID.""" tenant: Optional[str] """The unique identifier for the tenant.""" diff --git a/src/knockapi/types/inline_identify_user_request_param.py b/src/knockapi/types/inline_identify_user_request_param.py index 9172195f..75ab532f 100644 --- a/src/knockapi/types/inline_identify_user_request_param.py +++ b/src/knockapi/types/inline_identify_user_request_param.py @@ -23,8 +23,23 @@ class InlineIdentifyUserRequestParamTyped(TypedDict, total=False): created_at: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] """The creation date of the user from your system.""" + email: Optional[str] + """The primary email address for the user.""" + + name: Optional[str] + """Display name of the user.""" + preferences: Optional[InlinePreferenceSetRequestParam] """Inline set preferences for a recipient, where the key is the preference set id.""" + timezone: Optional[str] + """The timezone of the user. + + Must be a + valid [tz database time zone string](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). + Used + for [recurring schedules](/concepts/schedules#scheduling-workflows-with-recurring-schedules-for-recipients). + """ + InlineIdentifyUserRequestParam: TypeAlias = Union[InlineIdentifyUserRequestParamTyped, Dict[str, object]] diff --git a/src/knockapi/types/object_set_params.py b/src/knockapi/types/object_set_params.py index 6cc33162..5d8b80cc 100644 --- a/src/knockapi/types/object_set_params.py +++ b/src/knockapi/types/object_set_params.py @@ -27,8 +27,8 @@ class ObjectSetParams(TypedDict, total=False): timezone: Optional[str] """The timezone of the object. - Must be a valid - [tz database time zone string](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). - Used for - [recurring schedules](/concepts/schedules#scheduling-workflows-with-recurring-schedules-for-recipients). + Must be a + valid [tz database time zone string](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). + Used + for [recurring schedules](/concepts/schedules#scheduling-workflows-with-recurring-schedules-for-recipients). """ diff --git a/src/knockapi/types/user.py b/src/knockapi/types/user.py index 704d057c..ee5b56b7 100644 --- a/src/knockapi/types/user.py +++ b/src/knockapi/types/user.py @@ -34,17 +34,17 @@ class User(BaseModel): phone_number: Optional[str] = None """ - The [E.164](https://www.twilio.com/docs/glossary/what-e164) phone number of the + The [E.164](https://www.twilio.com/docs/glossary/what-e164) phone number of the user (required for SMS channels). """ timezone: Optional[str] = None """The timezone of the user. - Must be a valid - [tz database time zone string](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). - Used for - [recurring schedules](/concepts/schedules#scheduling-workflows-with-recurring-schedules-for-recipients). + Must be a + valid [tz database time zone string](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). + Used + for [recurring schedules](/concepts/schedules#scheduling-workflows-with-recurring-schedules-for-recipients). """ if TYPE_CHECKING: diff --git a/src/knockapi/types/user_update_params.py b/src/knockapi/types/user_update_params.py index 96d6b2ed..3f4814c2 100644 --- a/src/knockapi/types/user_update_params.py +++ b/src/knockapi/types/user_update_params.py @@ -37,7 +37,7 @@ class UserUpdateParams(TypedDict, total=False): phone_number: Optional[str] """ - The [E.164](https://www.twilio.com/docs/glossary/what-e164) phone number of the + The [E.164](https://www.twilio.com/docs/glossary/what-e164) phone number of the user (required for SMS channels). """ @@ -47,8 +47,8 @@ class UserUpdateParams(TypedDict, total=False): timezone: Optional[str] """The timezone of the user. - Must be a valid - [tz database time zone string](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). - Used for - [recurring schedules](/concepts/schedules#scheduling-workflows-with-recurring-schedules-for-recipients). + Must be a + valid [tz database time zone string](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). + Used + for [recurring schedules](/concepts/schedules#scheduling-workflows-with-recurring-schedules-for-recipients). """ From 49ba46929f05e16a4d134ce201d7a10bbfd09b4b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 9 May 2025 04:07:09 +0000 Subject: [PATCH 3/9] chore(internal): avoid errors for isinstance checks on proxies --- src/knockapi/_utils/_proxy.py | 5 ++++- tests/test_utils/test_proxy.py | 11 +++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/knockapi/_utils/_proxy.py b/src/knockapi/_utils/_proxy.py index ffd883e9..0f239a33 100644 --- a/src/knockapi/_utils/_proxy.py +++ b/src/knockapi/_utils/_proxy.py @@ -46,7 +46,10 @@ def __dir__(self) -> Iterable[str]: @property # type: ignore @override def __class__(self) -> type: # pyright: ignore - proxied = self.__get_proxied__() + try: + proxied = self.__get_proxied__() + except Exception: + return type(self) if issubclass(type(proxied), LazyProxy): return type(proxied) return proxied.__class__ diff --git a/tests/test_utils/test_proxy.py b/tests/test_utils/test_proxy.py index e57c32c5..82da93a8 100644 --- a/tests/test_utils/test_proxy.py +++ b/tests/test_utils/test_proxy.py @@ -21,3 +21,14 @@ def test_recursive_proxy() -> None: assert dir(proxy) == [] assert type(proxy).__name__ == "RecursiveLazyProxy" assert type(operator.attrgetter("name.foo.bar.baz")(proxy)).__name__ == "RecursiveLazyProxy" + + +def test_isinstance_does_not_error() -> None: + class AlwaysErrorProxy(LazyProxy[Any]): + @override + def __load__(self) -> Any: + raise RuntimeError("Mocking missing dependency") + + proxy = AlwaysErrorProxy() + assert not isinstance(proxy, dict) + assert isinstance(proxy, LazyProxy) From 03d835a305fe7024814c863ed902481ec509b439 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 10 May 2025 03:31:18 +0000 Subject: [PATCH 4/9] fix(package): support direct resource imports --- src/knockapi/__init__.py | 5 +++++ src/knockapi/_utils/_resources_proxy.py | 24 ++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 src/knockapi/_utils/_resources_proxy.py diff --git a/src/knockapi/__init__.py b/src/knockapi/__init__.py index f6ac71d7..e72f3cb4 100644 --- a/src/knockapi/__init__.py +++ b/src/knockapi/__init__.py @@ -1,5 +1,7 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. +import typing as _t + from . import types from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes from ._utils import file_from_path @@ -68,6 +70,9 @@ "DefaultAsyncHttpxClient", ] +if not _t.TYPE_CHECKING: + from ._utils._resources_proxy import resources as resources + _setup_logging() # Update the __module__ attribute for exported symbols so that diff --git a/src/knockapi/_utils/_resources_proxy.py b/src/knockapi/_utils/_resources_proxy.py new file mode 100644 index 00000000..f85f01c8 --- /dev/null +++ b/src/knockapi/_utils/_resources_proxy.py @@ -0,0 +1,24 @@ +from __future__ import annotations + +from typing import Any +from typing_extensions import override + +from ._proxy import LazyProxy + + +class ResourcesProxy(LazyProxy[Any]): + """A proxy for the `knockapi.resources` module. + + This is used so that we can lazily import `knockapi.resources` only when + needed *and* so that users can just import `knockapi` and reference `knockapi.resources` + """ + + @override + def __load__(self) -> Any: + import importlib + + mod = importlib.import_module("knockapi.resources") + return mod + + +resources = ResourcesProxy().__as_proxied__() From 5e79f8b6dc84a299099052a204f50c8522671d51 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 14 May 2025 21:40:42 +0000 Subject: [PATCH 5/9] feat(api): removes duplicate activities section This section is extra since the endpoint is already included in the list of methods above --- .stats.yml | 2 +- api.md | 6 - src/knockapi/resources/messages/__init__.py | 14 -- src/knockapi/resources/messages/activities.py | 216 ------------------ src/knockapi/resources/messages/messages.py | 32 --- src/knockapi/types/messages/__init__.py | 1 - .../types/messages/activity_list_params.py | 21 -- .../api_resources/messages/test_activities.py | 151 ------------ 8 files changed, 1 insertion(+), 442 deletions(-) delete mode 100644 src/knockapi/resources/messages/activities.py delete mode 100644 src/knockapi/types/messages/activity_list_params.py delete mode 100644 tests/api_resources/messages/test_activities.py diff --git a/.stats.yml b/.stats.yml index 46b7dfc9..2c15eaf0 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 89 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/knock%2Fknock-5176d1bb3a88b127808b197c9ae1cf366fd56599fd8c7b7241ac829e72d69a42.yml openapi_spec_hash: 92953a04021af2d0132fd9eebeb844b9 -config_hash: 7460c5bd6d1a7041faa274f677789407 +config_hash: 2ae8965d371a03bd30c6a56819c04cf2 diff --git a/api.md b/api.md index f7a45fb2..2a815109 100644 --- a/api.md +++ b/api.md @@ -260,12 +260,6 @@ Methods: - client.messages.batch.mark_as_unseen(\*\*params) -> BatchMarkAsUnseenResponse - client.messages.batch.unarchive(\*\*params) -> BatchUnarchiveResponse -## Activities - -Methods: - -- client.messages.activities.list(message_id, \*\*params) -> SyncItemsCursor[Activity] - # Providers ## Slack diff --git a/src/knockapi/resources/messages/__init__.py b/src/knockapi/resources/messages/__init__.py index 74a67564..389b8cc1 100644 --- a/src/knockapi/resources/messages/__init__.py +++ b/src/knockapi/resources/messages/__init__.py @@ -16,14 +16,6 @@ MessagesResourceWithStreamingResponse, AsyncMessagesResourceWithStreamingResponse, ) -from .activities import ( - ActivitiesResource, - AsyncActivitiesResource, - ActivitiesResourceWithRawResponse, - AsyncActivitiesResourceWithRawResponse, - ActivitiesResourceWithStreamingResponse, - AsyncActivitiesResourceWithStreamingResponse, -) __all__ = [ "BatchResource", @@ -32,12 +24,6 @@ "AsyncBatchResourceWithRawResponse", "BatchResourceWithStreamingResponse", "AsyncBatchResourceWithStreamingResponse", - "ActivitiesResource", - "AsyncActivitiesResource", - "ActivitiesResourceWithRawResponse", - "AsyncActivitiesResourceWithRawResponse", - "ActivitiesResourceWithStreamingResponse", - "AsyncActivitiesResourceWithStreamingResponse", "MessagesResource", "AsyncMessagesResource", "MessagesResourceWithRawResponse", diff --git a/src/knockapi/resources/messages/activities.py b/src/knockapi/resources/messages/activities.py deleted file mode 100644 index 36b83205..00000000 --- a/src/knockapi/resources/messages/activities.py +++ /dev/null @@ -1,216 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import httpx - -from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven -from ..._utils import maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ...pagination import SyncItemsCursor, AsyncItemsCursor -from ..._base_client import AsyncPaginator, make_request_options -from ...types.activity import Activity -from ...types.messages import activity_list_params - -__all__ = ["ActivitiesResource", "AsyncActivitiesResource"] - - -class ActivitiesResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> ActivitiesResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/knocklabs/knock-python#accessing-raw-response-data-eg-headers - """ - return ActivitiesResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ActivitiesResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/knocklabs/knock-python#with_streaming_response - """ - return ActivitiesResourceWithStreamingResponse(self) - - def list( - self, - message_id: str, - *, - after: str | NotGiven = NOT_GIVEN, - before: str | NotGiven = NOT_GIVEN, - page_size: int | NotGiven = NOT_GIVEN, - trigger_data: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> SyncItemsCursor[Activity]: - """ - Returns a paginated list of activities for the specified message. - - Args: - after: The cursor to fetch entries after. - - before: The cursor to fetch entries before. - - page_size: The number of items per page. - - trigger_data: The trigger data to filter activities by. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not message_id: - raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") - return self._get_api_list( - f"/v1/messages/{message_id}/activities", - page=SyncItemsCursor[Activity], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "after": after, - "before": before, - "page_size": page_size, - "trigger_data": trigger_data, - }, - activity_list_params.ActivityListParams, - ), - ), - model=Activity, - ) - - -class AsyncActivitiesResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncActivitiesResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/knocklabs/knock-python#accessing-raw-response-data-eg-headers - """ - return AsyncActivitiesResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncActivitiesResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/knocklabs/knock-python#with_streaming_response - """ - return AsyncActivitiesResourceWithStreamingResponse(self) - - def list( - self, - message_id: str, - *, - after: str | NotGiven = NOT_GIVEN, - before: str | NotGiven = NOT_GIVEN, - page_size: int | NotGiven = NOT_GIVEN, - trigger_data: str | NotGiven = NOT_GIVEN, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, - ) -> AsyncPaginator[Activity, AsyncItemsCursor[Activity]]: - """ - Returns a paginated list of activities for the specified message. - - Args: - after: The cursor to fetch entries after. - - before: The cursor to fetch entries before. - - page_size: The number of items per page. - - trigger_data: The trigger data to filter activities by. - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not message_id: - raise ValueError(f"Expected a non-empty value for `message_id` but received {message_id!r}") - return self._get_api_list( - f"/v1/messages/{message_id}/activities", - page=AsyncItemsCursor[Activity], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "after": after, - "before": before, - "page_size": page_size, - "trigger_data": trigger_data, - }, - activity_list_params.ActivityListParams, - ), - ), - model=Activity, - ) - - -class ActivitiesResourceWithRawResponse: - def __init__(self, activities: ActivitiesResource) -> None: - self._activities = activities - - self.list = to_raw_response_wrapper( - activities.list, - ) - - -class AsyncActivitiesResourceWithRawResponse: - def __init__(self, activities: AsyncActivitiesResource) -> None: - self._activities = activities - - self.list = async_to_raw_response_wrapper( - activities.list, - ) - - -class ActivitiesResourceWithStreamingResponse: - def __init__(self, activities: ActivitiesResource) -> None: - self._activities = activities - - self.list = to_streamed_response_wrapper( - activities.list, - ) - - -class AsyncActivitiesResourceWithStreamingResponse: - def __init__(self, activities: AsyncActivitiesResource) -> None: - self._activities = activities - - self.list = async_to_streamed_response_wrapper( - activities.list, - ) diff --git a/src/knockapi/resources/messages/messages.py b/src/knockapi/resources/messages/messages.py index 99f44e79..e3ec1e50 100644 --- a/src/knockapi/resources/messages/messages.py +++ b/src/knockapi/resources/messages/messages.py @@ -25,14 +25,6 @@ from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property -from .activities import ( - ActivitiesResource, - AsyncActivitiesResource, - ActivitiesResourceWithRawResponse, - AsyncActivitiesResourceWithRawResponse, - ActivitiesResourceWithStreamingResponse, - AsyncActivitiesResourceWithStreamingResponse, -) from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( to_raw_response_wrapper, @@ -56,10 +48,6 @@ class MessagesResource(SyncAPIResource): def batch(self) -> BatchResource: return BatchResource(self._client) - @cached_property - def activities(self) -> ActivitiesResource: - return ActivitiesResource(self._client) - @cached_property def with_raw_response(self) -> MessagesResourceWithRawResponse: """ @@ -674,10 +662,6 @@ class AsyncMessagesResource(AsyncAPIResource): def batch(self) -> AsyncBatchResource: return AsyncBatchResource(self._client) - @cached_property - def activities(self) -> AsyncActivitiesResource: - return AsyncActivitiesResource(self._client) - @cached_property def with_raw_response(self) -> AsyncMessagesResourceWithRawResponse: """ @@ -1335,10 +1319,6 @@ def __init__(self, messages: MessagesResource) -> None: def batch(self) -> BatchResourceWithRawResponse: return BatchResourceWithRawResponse(self._messages.batch) - @cached_property - def activities(self) -> ActivitiesResourceWithRawResponse: - return ActivitiesResourceWithRawResponse(self._messages.activities) - class AsyncMessagesResourceWithRawResponse: def __init__(self, messages: AsyncMessagesResource) -> None: @@ -1388,10 +1368,6 @@ def __init__(self, messages: AsyncMessagesResource) -> None: def batch(self) -> AsyncBatchResourceWithRawResponse: return AsyncBatchResourceWithRawResponse(self._messages.batch) - @cached_property - def activities(self) -> AsyncActivitiesResourceWithRawResponse: - return AsyncActivitiesResourceWithRawResponse(self._messages.activities) - class MessagesResourceWithStreamingResponse: def __init__(self, messages: MessagesResource) -> None: @@ -1441,10 +1417,6 @@ def __init__(self, messages: MessagesResource) -> None: def batch(self) -> BatchResourceWithStreamingResponse: return BatchResourceWithStreamingResponse(self._messages.batch) - @cached_property - def activities(self) -> ActivitiesResourceWithStreamingResponse: - return ActivitiesResourceWithStreamingResponse(self._messages.activities) - class AsyncMessagesResourceWithStreamingResponse: def __init__(self, messages: AsyncMessagesResource) -> None: @@ -1493,7 +1465,3 @@ def __init__(self, messages: AsyncMessagesResource) -> None: @cached_property def batch(self) -> AsyncBatchResourceWithStreamingResponse: return AsyncBatchResourceWithStreamingResponse(self._messages.batch) - - @cached_property - def activities(self) -> AsyncActivitiesResourceWithStreamingResponse: - return AsyncActivitiesResourceWithStreamingResponse(self._messages.activities) diff --git a/src/knockapi/types/messages/__init__.py b/src/knockapi/types/messages/__init__.py index 77bdb73c..8e38f600 100644 --- a/src/knockapi/types/messages/__init__.py +++ b/src/knockapi/types/messages/__init__.py @@ -2,7 +2,6 @@ from __future__ import annotations -from .activity_list_params import ActivityListParams as ActivityListParams from .batch_archive_params import BatchArchiveParams as BatchArchiveParams from .batch_archive_response import BatchArchiveResponse as BatchArchiveResponse from .batch_unarchive_params import BatchUnarchiveParams as BatchUnarchiveParams diff --git a/src/knockapi/types/messages/activity_list_params.py b/src/knockapi/types/messages/activity_list_params.py deleted file mode 100644 index c5cc589a..00000000 --- a/src/knockapi/types/messages/activity_list_params.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import TypedDict - -__all__ = ["ActivityListParams"] - - -class ActivityListParams(TypedDict, total=False): - after: str - """The cursor to fetch entries after.""" - - before: str - """The cursor to fetch entries before.""" - - page_size: int - """The number of items per page.""" - - trigger_data: str - """The trigger data to filter activities by.""" diff --git a/tests/api_resources/messages/test_activities.py b/tests/api_resources/messages/test_activities.py deleted file mode 100644 index 48f22907..00000000 --- a/tests/api_resources/messages/test_activities.py +++ /dev/null @@ -1,151 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from knockapi import Knock, AsyncKnock -from tests.utils import assert_matches_type -from knockapi.types import Activity -from knockapi.pagination import SyncItemsCursor, AsyncItemsCursor - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestActivities: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @pytest.mark.skip( - reason="currently no good way to test endpoints defining callbacks, Prism mock server will fail trying to reach the provided callback url" - ) - @parametrize - def test_method_list(self, client: Knock) -> None: - activity = client.messages.activities.list( - message_id="message_id", - ) - assert_matches_type(SyncItemsCursor[Activity], activity, path=["response"]) - - @pytest.mark.skip( - reason="currently no good way to test endpoints defining callbacks, Prism mock server will fail trying to reach the provided callback url" - ) - @parametrize - def test_method_list_with_all_params(self, client: Knock) -> None: - activity = client.messages.activities.list( - message_id="message_id", - after="after", - before="before", - page_size=0, - trigger_data="trigger_data", - ) - assert_matches_type(SyncItemsCursor[Activity], activity, path=["response"]) - - @pytest.mark.skip( - reason="currently no good way to test endpoints defining callbacks, Prism mock server will fail trying to reach the provided callback url" - ) - @parametrize - def test_raw_response_list(self, client: Knock) -> None: - response = client.messages.activities.with_raw_response.list( - message_id="message_id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - activity = response.parse() - assert_matches_type(SyncItemsCursor[Activity], activity, path=["response"]) - - @pytest.mark.skip( - reason="currently no good way to test endpoints defining callbacks, Prism mock server will fail trying to reach the provided callback url" - ) - @parametrize - def test_streaming_response_list(self, client: Knock) -> None: - with client.messages.activities.with_streaming_response.list( - message_id="message_id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - activity = response.parse() - assert_matches_type(SyncItemsCursor[Activity], activity, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip( - reason="currently no good way to test endpoints defining callbacks, Prism mock server will fail trying to reach the provided callback url" - ) - @parametrize - def test_path_params_list(self, client: Knock) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_id` but received ''"): - client.messages.activities.with_raw_response.list( - message_id="", - ) - - -class TestAsyncActivities: - parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) - - @pytest.mark.skip( - reason="currently no good way to test endpoints defining callbacks, Prism mock server will fail trying to reach the provided callback url" - ) - @parametrize - async def test_method_list(self, async_client: AsyncKnock) -> None: - activity = await async_client.messages.activities.list( - message_id="message_id", - ) - assert_matches_type(AsyncItemsCursor[Activity], activity, path=["response"]) - - @pytest.mark.skip( - reason="currently no good way to test endpoints defining callbacks, Prism mock server will fail trying to reach the provided callback url" - ) - @parametrize - async def test_method_list_with_all_params(self, async_client: AsyncKnock) -> None: - activity = await async_client.messages.activities.list( - message_id="message_id", - after="after", - before="before", - page_size=0, - trigger_data="trigger_data", - ) - assert_matches_type(AsyncItemsCursor[Activity], activity, path=["response"]) - - @pytest.mark.skip( - reason="currently no good way to test endpoints defining callbacks, Prism mock server will fail trying to reach the provided callback url" - ) - @parametrize - async def test_raw_response_list(self, async_client: AsyncKnock) -> None: - response = await async_client.messages.activities.with_raw_response.list( - message_id="message_id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - activity = await response.parse() - assert_matches_type(AsyncItemsCursor[Activity], activity, path=["response"]) - - @pytest.mark.skip( - reason="currently no good way to test endpoints defining callbacks, Prism mock server will fail trying to reach the provided callback url" - ) - @parametrize - async def test_streaming_response_list(self, async_client: AsyncKnock) -> None: - async with async_client.messages.activities.with_streaming_response.list( - message_id="message_id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - activity = await response.parse() - assert_matches_type(AsyncItemsCursor[Activity], activity, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @pytest.mark.skip( - reason="currently no good way to test endpoints defining callbacks, Prism mock server will fail trying to reach the provided callback url" - ) - @parametrize - async def test_path_params_list(self, async_client: AsyncKnock) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_id` but received ''"): - await async_client.messages.activities.with_raw_response.list( - message_id="", - ) From 6793aeb32db67c2f41dc3c721c64b25471031c13 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 15 May 2025 04:43:26 +0000 Subject: [PATCH 6/9] chore(ci): upload sdks to package manager --- .github/workflows/ci.yml | 24 ++++++++++++++++++++++++ scripts/utils/upload-artifact.sh | 25 +++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100755 scripts/utils/upload-artifact.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 83d1a081..6e9bff9b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,6 +30,30 @@ jobs: - name: Run lints run: ./scripts/lint + upload: + if: github.repository == 'stainless-sdks/knock-python' + timeout-minutes: 10 + name: upload + permissions: + contents: read + id-token: write + runs-on: depot-ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + + - name: Get GitHub OIDC Token + id: github-oidc + uses: actions/github-script@v6 + with: + script: core.setOutput('github_token', await core.getIDToken()); + + - name: Upload tarball + env: + URL: https://pkg.stainless.com/s + AUTH: ${{ steps.github-oidc.outputs.github_token }} + SHA: ${{ github.sha }} + run: ./scripts/utils/upload-artifact.sh + test: timeout-minutes: 10 name: test diff --git a/scripts/utils/upload-artifact.sh b/scripts/utils/upload-artifact.sh new file mode 100755 index 00000000..c2229e23 --- /dev/null +++ b/scripts/utils/upload-artifact.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -exuo pipefail + +RESPONSE=$(curl -X POST "$URL" \ + -H "Authorization: Bearer $AUTH" \ + -H "Content-Type: application/json") + +SIGNED_URL=$(echo "$RESPONSE" | jq -r '.url') + +if [[ "$SIGNED_URL" == "null" ]]; then + echo -e "\033[31mFailed to get signed URL.\033[0m" + exit 1 +fi + +UPLOAD_RESPONSE=$(tar -cz . | curl -v -X PUT \ + -H "Content-Type: application/gzip" \ + --data-binary @- "$SIGNED_URL" 2>&1) + +if echo "$UPLOAD_RESPONSE" | grep -q "HTTP/[0-9.]* 200"; then + echo -e "\033[32mUploaded build to Stainless storage.\033[0m" + echo -e "\033[32mInstallation: npm install 'https://pkg.stainless.com/s/knock-python/$SHA'\033[0m" +else + echo -e "\033[31mFailed to upload artifact.\033[0m" + exit 1 +fi From e88471a8b9c90f68d9ff6438298754f8b63148cc Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 15 May 2025 22:18:26 +0000 Subject: [PATCH 7/9] feat(api): api update --- .stats.yml | 4 +-- src/knockapi/resources/schedules/schedules.py | 6 ++--- .../inline_identify_user_request_param.py | 15 +++++++++++ src/knockapi/types/object.py | 9 +++---- src/knockapi/types/recipients/__init__.py | 1 - .../recipients/channel_data_request_param.py | 27 ------------------- .../inline_channel_data_request_param.py | 20 +++++++++++--- src/knockapi/types/schedule_create_params.py | 6 +---- tests/api_resources/test_objects.py | 4 +-- tests/api_resources/test_tenants.py | 4 +-- tests/api_resources/test_users.py | 4 +-- 11 files changed, 45 insertions(+), 55 deletions(-) delete mode 100644 src/knockapi/types/recipients/channel_data_request_param.py diff --git a/.stats.yml b/.stats.yml index 2c15eaf0..9b155a84 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 89 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/knock%2Fknock-5176d1bb3a88b127808b197c9ae1cf366fd56599fd8c7b7241ac829e72d69a42.yml -openapi_spec_hash: 92953a04021af2d0132fd9eebeb844b9 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/knock%2Fknock-923618cb1489556307ce0be07853170d15516da8fe450578fed856e560ec361c.yml +openapi_spec_hash: 1406cf9ab287412197a1907f5d66d541 config_hash: 2ae8965d371a03bd30c6a56819c04cf2 diff --git a/src/knockapi/resources/schedules/schedules.py b/src/knockapi/resources/schedules/schedules.py index 816d1f1d..8e58b442 100644 --- a/src/knockapi/resources/schedules/schedules.py +++ b/src/knockapi/resources/schedules/schedules.py @@ -94,8 +94,7 @@ def create( for the `actor`, `recipient`, and `tenant` fields. Args: - recipients: The recipients to trigger the workflow for. Can inline identify users, objects, - or use a list of user IDs. Limited to 1,000 recipients. + recipients: The recipients to set the schedule for. Limited to 100 recipients per request. repeats: The repeat rule for the schedule. @@ -354,8 +353,7 @@ async def create( for the `actor`, `recipient`, and `tenant` fields. Args: - recipients: The recipients to trigger the workflow for. Can inline identify users, objects, - or use a list of user IDs. Limited to 1,000 recipients. + recipients: The recipients to set the schedule for. Limited to 100 recipients per request. repeats: The repeat rule for the schedule. diff --git a/src/knockapi/types/inline_identify_user_request_param.py b/src/knockapi/types/inline_identify_user_request_param.py index 75ab532f..c47254f2 100644 --- a/src/knockapi/types/inline_identify_user_request_param.py +++ b/src/knockapi/types/inline_identify_user_request_param.py @@ -17,6 +17,9 @@ class InlineIdentifyUserRequestParamTyped(TypedDict, total=False): id: Required[str] """The ID for the user that you set when identifying them in Knock.""" + avatar: Optional[str] + """URL to the user's avatar image.""" + channel_data: Optional[InlineChannelDataRequestParam] """A request to set channel data for a type of channel inline.""" @@ -26,9 +29,21 @@ class InlineIdentifyUserRequestParamTyped(TypedDict, total=False): email: Optional[str] """The primary email address for the user.""" + locale: Optional[str] + """The locale of the user. + + Used for [message localization](/concepts/translations). + """ + name: Optional[str] """Display name of the user.""" + phone_number: Optional[str] + """ + The [E.164](https://www.twilio.com/docs/glossary/what-e164) phone number of the + user (required for SMS channels). + """ + preferences: Optional[InlinePreferenceSetRequestParam] """Inline set preferences for a recipient, where the key is the preference set id.""" diff --git a/src/knockapi/types/object.py b/src/knockapi/types/object.py index bcb94643..3d2ed638 100644 --- a/src/knockapi/types/object.py +++ b/src/knockapi/types/object.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import TYPE_CHECKING, Optional +from typing import Dict, Optional from datetime import datetime from pydantic import Field as FieldInfo @@ -26,8 +26,5 @@ class Object(BaseModel): created_at: Optional[datetime] = None """Timestamp when the resource was created.""" - if TYPE_CHECKING: - # Stub to indicate that arbitrary properties are accepted. - # To access properties that are not valid identifiers you can use `getattr`, e.g. - # `getattr(obj, '$type')` - def __getattr__(self, attr: str) -> object: ... + properties: Optional[Dict[str, object]] = None + """The custom properties associated with the object.""" diff --git a/src/knockapi/types/recipients/__init__.py b/src/knockapi/types/recipients/__init__.py index e1c0177c..49d62a96 100644 --- a/src/knockapi/types/recipients/__init__.py +++ b/src/knockapi/types/recipients/__init__.py @@ -12,7 +12,6 @@ from .one_signal_channel_data import OneSignalChannelData as OneSignalChannelData from .push_channel_data_param import PushChannelDataParam as PushChannelDataParam from .slack_channel_data_param import SlackChannelDataParam as SlackChannelDataParam -from .channel_data_request_param import ChannelDataRequestParam as ChannelDataRequestParam from .discord_channel_data_param import DiscordChannelDataParam as DiscordChannelDataParam from .ms_teams_channel_data_param import MsTeamsChannelDataParam as MsTeamsChannelDataParam from .preference_set_channel_types import PreferenceSetChannelTypes as PreferenceSetChannelTypes diff --git a/src/knockapi/types/recipients/channel_data_request_param.py b/src/knockapi/types/recipients/channel_data_request_param.py deleted file mode 100644 index e9af5159..00000000 --- a/src/knockapi/types/recipients/channel_data_request_param.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Union -from typing_extensions import Required, TypeAlias, TypedDict - -from .push_channel_data_param import PushChannelDataParam -from .slack_channel_data_param import SlackChannelDataParam -from .discord_channel_data_param import DiscordChannelDataParam -from .ms_teams_channel_data_param import MsTeamsChannelDataParam -from .one_signal_channel_data_param import OneSignalChannelDataParam - -__all__ = ["ChannelDataRequestParam", "Data"] - -Data: TypeAlias = Union[ - PushChannelDataParam, - OneSignalChannelDataParam, - SlackChannelDataParam, - MsTeamsChannelDataParam, - DiscordChannelDataParam, -] - - -class ChannelDataRequestParam(TypedDict, total=False): - data: Required[Data] - """Channel data for a given channel type.""" diff --git a/src/knockapi/types/recipients/inline_channel_data_request_param.py b/src/knockapi/types/recipients/inline_channel_data_request_param.py index 33fcc462..df544367 100644 --- a/src/knockapi/types/recipients/inline_channel_data_request_param.py +++ b/src/knockapi/types/recipients/inline_channel_data_request_param.py @@ -2,11 +2,23 @@ from __future__ import annotations -from typing import Dict +from typing import Dict, Union from typing_extensions import TypeAlias -from .channel_data_request_param import ChannelDataRequestParam +from .push_channel_data_param import PushChannelDataParam +from .slack_channel_data_param import SlackChannelDataParam +from .discord_channel_data_param import DiscordChannelDataParam +from .ms_teams_channel_data_param import MsTeamsChannelDataParam +from .one_signal_channel_data_param import OneSignalChannelDataParam -__all__ = ["InlineChannelDataRequestParam"] +__all__ = ["InlineChannelDataRequestParam", "InlineChannelDataRequestParamItem"] -InlineChannelDataRequestParam: TypeAlias = Dict[str, ChannelDataRequestParam] +InlineChannelDataRequestParamItem: TypeAlias = Union[ + PushChannelDataParam, + OneSignalChannelDataParam, + SlackChannelDataParam, + MsTeamsChannelDataParam, + DiscordChannelDataParam, +] + +InlineChannelDataRequestParam: TypeAlias = Dict[str, InlineChannelDataRequestParamItem] diff --git a/src/knockapi/types/schedule_create_params.py b/src/knockapi/types/schedule_create_params.py index cd9c9f43..491ee949 100644 --- a/src/knockapi/types/schedule_create_params.py +++ b/src/knockapi/types/schedule_create_params.py @@ -16,11 +16,7 @@ class ScheduleCreateParams(TypedDict, total=False): recipients: Required[List[RecipientRequestParam]] - """The recipients to trigger the workflow for. - - Can inline identify users, objects, or use a list of user IDs. Limited to 1,000 - recipients. - """ + """The recipients to set the schedule for. Limited to 100 recipients per request.""" repeats: Required[Iterable[ScheduleRepeatRuleParam]] """The repeat rule for the schedule.""" diff --git a/tests/api_resources/test_objects.py b/tests/api_resources/test_objects.py index e868b4af..b8dde450 100644 --- a/tests/api_resources/test_objects.py +++ b/tests/api_resources/test_objects.py @@ -829,7 +829,7 @@ def test_method_set_with_all_params(self, client: Knock) -> None: object_ = client.objects.set( collection="collection", id="id", - channel_data={"97c5837d-c65c-4d54-aa39-080eeb81c69d": {"data": {"tokens": ["push_token_123"]}}}, + channel_data={"97c5837d-c65c-4d54-aa39-080eeb81c69d": {"tokens": ["push_token_123"]}}, locale="en-US", preferences={ "default": { @@ -2032,7 +2032,7 @@ async def test_method_set_with_all_params(self, async_client: AsyncKnock) -> Non object_ = await async_client.objects.set( collection="collection", id="id", - channel_data={"97c5837d-c65c-4d54-aa39-080eeb81c69d": {"data": {"tokens": ["push_token_123"]}}}, + channel_data={"97c5837d-c65c-4d54-aa39-080eeb81c69d": {"tokens": ["push_token_123"]}}, locale="en-US", preferences={ "default": { diff --git a/tests/api_resources/test_tenants.py b/tests/api_resources/test_tenants.py index 3bc1993a..9dee76b2 100644 --- a/tests/api_resources/test_tenants.py +++ b/tests/api_resources/test_tenants.py @@ -183,7 +183,7 @@ def test_method_set(self, client: Knock) -> None: def test_method_set_with_all_params(self, client: Knock) -> None: tenant = client.tenants.set( id="id", - channel_data={"97c5837d-c65c-4d54-aa39-080eeb81c69d": {"data": {"tokens": ["push_token_xxx"]}}}, + channel_data={"97c5837d-c65c-4d54-aa39-080eeb81c69d": {"tokens": ["push_token_xxx"]}}, preferences={ "default": { "categories": { @@ -504,7 +504,7 @@ async def test_method_set(self, async_client: AsyncKnock) -> None: async def test_method_set_with_all_params(self, async_client: AsyncKnock) -> None: tenant = await async_client.tenants.set( id="id", - channel_data={"97c5837d-c65c-4d54-aa39-080eeb81c69d": {"data": {"tokens": ["push_token_xxx"]}}}, + channel_data={"97c5837d-c65c-4d54-aa39-080eeb81c69d": {"tokens": ["push_token_xxx"]}}, preferences={ "default": { "categories": { diff --git a/tests/api_resources/test_users.py b/tests/api_resources/test_users.py index 18718405..76315573 100644 --- a/tests/api_resources/test_users.py +++ b/tests/api_resources/test_users.py @@ -47,7 +47,7 @@ def test_method_update_with_all_params(self, client: Knock) -> None: user = client.users.update( user_id="user_id", avatar="avatar", - channel_data={"97c5837d-c65c-4d54-aa39-080eeb81c69d": {"data": {"tokens": ["push_token_123"]}}}, + channel_data={"97c5837d-c65c-4d54-aa39-080eeb81c69d": {"tokens": ["push_token_123"]}}, created_at=parse_datetime("2019-12-27T18:11:19.117Z"), email="ian.malcolm@chaos.theory", locale="locale", @@ -1017,7 +1017,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncKnock) -> user = await async_client.users.update( user_id="user_id", avatar="avatar", - channel_data={"97c5837d-c65c-4d54-aa39-080eeb81c69d": {"data": {"tokens": ["push_token_123"]}}}, + channel_data={"97c5837d-c65c-4d54-aa39-080eeb81c69d": {"tokens": ["push_token_123"]}}, created_at=parse_datetime("2019-12-27T18:11:19.117Z"), email="ian.malcolm@chaos.theory", locale="locale", From aad60e07fd672462e4689125a20f4f62a99d18de Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 16 May 2025 03:42:19 +0000 Subject: [PATCH 8/9] chore(ci): fix installation instructions --- scripts/utils/upload-artifact.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/utils/upload-artifact.sh b/scripts/utils/upload-artifact.sh index c2229e23..597153be 100755 --- a/scripts/utils/upload-artifact.sh +++ b/scripts/utils/upload-artifact.sh @@ -18,7 +18,7 @@ UPLOAD_RESPONSE=$(tar -cz . | curl -v -X PUT \ if echo "$UPLOAD_RESPONSE" | grep -q "HTTP/[0-9.]* 200"; then echo -e "\033[32mUploaded build to Stainless storage.\033[0m" - echo -e "\033[32mInstallation: npm install 'https://pkg.stainless.com/s/knock-python/$SHA'\033[0m" + echo -e "\033[32mInstallation: pip install 'https://pkg.stainless.com/s/knock-python/$SHA'\033[0m" else echo -e "\033[31mFailed to upload artifact.\033[0m" exit 1 From 961acf51bcbb8b4ca80e48331c1de33376d40fc1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 16 May 2025 03:42:36 +0000 Subject: [PATCH 9/9] release: 1.1.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 23 +++++++++++++++++++++++ pyproject.toml | 2 +- src/knockapi/_version.py | 2 +- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index fea34540..2601677b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.0.0" + ".": "1.1.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 22d5a70b..8f3ccea2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,28 @@ # Changelog +## 1.1.0 (2025-05-16) + +Full Changelog: [v1.0.0...v1.1.0](https://github.com/knocklabs/knock-python/compare/v1.0.0...v1.1.0) + +### Features + +* **api:** api update ([e88471a](https://github.com/knocklabs/knock-python/commit/e88471a8b9c90f68d9ff6438298754f8b63148cc)) +* **api:** api update ([07aa7ef](https://github.com/knocklabs/knock-python/commit/07aa7ef945058ad65a8b7586c74dabf156c69cfb)) +* **api:** removes duplicate activities section ([5e79f8b](https://github.com/knocklabs/knock-python/commit/5e79f8b6dc84a299099052a204f50c8522671d51)) + + +### Bug Fixes + +* **package:** support direct resource imports ([03d835a](https://github.com/knocklabs/knock-python/commit/03d835a305fe7024814c863ed902481ec509b439)) + + +### Chores + +* **ci:** fix installation instructions ([aad60e0](https://github.com/knocklabs/knock-python/commit/aad60e07fd672462e4689125a20f4f62a99d18de)) +* **ci:** upload sdks to package manager ([6793aeb](https://github.com/knocklabs/knock-python/commit/6793aeb32db67c2f41dc3c721c64b25471031c13)) +* **internal:** avoid errors for isinstance checks on proxies ([49ba469](https://github.com/knocklabs/knock-python/commit/49ba46929f05e16a4d134ce201d7a10bbfd09b4b)) +* **internal:** version bump ([797719a](https://github.com/knocklabs/knock-python/commit/797719ac713c52ac1abae12df3b3667ee3d536aa)) + ## 1.0.0 (2025-05-01) Full Changelog: [v0.5.14...v1.0.0](https://github.com/knocklabs/knock-python/compare/v0.5.14...v1.0.0) diff --git a/pyproject.toml b/pyproject.toml index fbe1edfd..8124a3ae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "knockapi" -version = "1.0.0" +version = "1.1.0" description = "The official Python library for the knock API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/knockapi/_version.py b/src/knockapi/_version.py index b18ad4b7..4cea0bdf 100644 --- a/src/knockapi/_version.py +++ b/src/knockapi/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "knockapi" -__version__ = "1.0.0" # x-release-please-version +__version__ = "1.1.0" # x-release-please-version