diff --git a/.stats.yml b/.stats.yml index 6407960..5fe5772 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1 +1 @@ -configured_endpoints: 157 +configured_endpoints: 158 diff --git a/api.md b/api.md index e663e78..3d159d2 100644 --- a/api.md +++ b/api.md @@ -578,6 +578,9 @@ Types: ```python from courier.types import ( + PreferenceChangeLogEntry, + PreferenceChangeLogValue, + PreferenceLogsListResponse, PublishPreferencesRequest, PublishPreferencesResponse, TopicDigestCategory, @@ -602,6 +605,7 @@ Methods: - client.workspace_preferences.retrieve(section_id) -> WorkspacePreferenceGetResponse - client.workspace_preferences.list() -> WorkspacePreferenceListResponse - client.workspace_preferences.archive(section_id) -> None +- client.workspace_preferences.list_logs(\*\*params) -> PreferenceLogsListResponse - client.workspace_preferences.publish(\*\*params) -> PublishPreferencesResponse - client.workspace_preferences.replace(section_id, \*\*params) -> WorkspacePreferenceGetResponse diff --git a/src/courier/resources/workspace_preferences/workspace_preferences.py b/src/courier/resources/workspace_preferences/workspace_preferences.py index 6239fd2..cdace88 100644 --- a/src/courier/resources/workspace_preferences/workspace_preferences.py +++ b/src/courier/resources/workspace_preferences/workspace_preferences.py @@ -18,6 +18,7 @@ workspace_preference_create_params, workspace_preference_publish_params, workspace_preference_replace_params, + workspace_preference_list_logs_params, ) from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given from ..._utils import path_template, maybe_transform, strip_not_given, async_maybe_transform @@ -31,6 +32,7 @@ ) from ..._base_client import make_request_options from ...types.publish_preferences_response import PublishPreferencesResponse +from ...types.preference_logs_list_response import PreferenceLogsListResponse from ...types.shared.channel_classification import ChannelClassification from ...types.workspace_preference_get_response import WorkspacePreferenceGetResponse from ...types.workspace_preference_list_response import WorkspacePreferenceListResponse @@ -222,6 +224,71 @@ def archive( cast_to=NoneType, ) + def list_logs( + self, + *, + cursor: str | Omit = omit, + limit: int | Omit = omit, + since: str | Omit = omit, + tenant_id: str | Omit = omit, + user_id: str | Omit = omit, + # 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, + ) -> PreferenceLogsListResponse: + """ + Returns the history of preference changes in this environment, newest first. + Each entry records one change a user made to one subscription topic, and carries + the value before it where there was one. Supply user_id to read a single user's + history instead of the whole environment. + + Args: + cursor: A cursor from a previous response's paging.cursor. Continue only while + paging.more is true; the cursor is omitted on the last page. + + limit: How many entries to return. Defaults to 25. + + since: Return only changes at or after this time, as an ISO-8601 date or date-time. A + date alone is read as the start of that day in UTC. + + tenant_id: Narrow to the changes this user made in one tenant context. Only valid together + with user_id. + + user_id: Return only this user's changes. Omit it to read every change in the + environment. + + 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 + """ + return self._get( + "/preferences/logs", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "cursor": cursor, + "limit": limit, + "since": since, + "tenant_id": tenant_id, + "user_id": user_id, + }, + workspace_preference_list_logs_params.WorkspacePreferenceListLogsParams, + ), + ), + cast_to=PreferenceLogsListResponse, + ) + def publish( self, *, @@ -524,6 +591,71 @@ async def archive( cast_to=NoneType, ) + async def list_logs( + self, + *, + cursor: str | Omit = omit, + limit: int | Omit = omit, + since: str | Omit = omit, + tenant_id: str | Omit = omit, + user_id: str | Omit = omit, + # 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, + ) -> PreferenceLogsListResponse: + """ + Returns the history of preference changes in this environment, newest first. + Each entry records one change a user made to one subscription topic, and carries + the value before it where there was one. Supply user_id to read a single user's + history instead of the whole environment. + + Args: + cursor: A cursor from a previous response's paging.cursor. Continue only while + paging.more is true; the cursor is omitted on the last page. + + limit: How many entries to return. Defaults to 25. + + since: Return only changes at or after this time, as an ISO-8601 date or date-time. A + date alone is read as the start of that day in UTC. + + tenant_id: Narrow to the changes this user made in one tenant context. Only valid together + with user_id. + + user_id: Return only this user's changes. Omit it to read every change in the + environment. + + 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 + """ + return await self._get( + "/preferences/logs", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "cursor": cursor, + "limit": limit, + "since": since, + "tenant_id": tenant_id, + "user_id": user_id, + }, + workspace_preference_list_logs_params.WorkspacePreferenceListLogsParams, + ), + ), + cast_to=PreferenceLogsListResponse, + ) + async def publish( self, *, @@ -658,6 +790,9 @@ def __init__(self, workspace_preferences: WorkspacePreferencesResource) -> None: self.archive = to_raw_response_wrapper( workspace_preferences.archive, ) + self.list_logs = to_raw_response_wrapper( + workspace_preferences.list_logs, + ) self.publish = to_raw_response_wrapper( workspace_preferences.publish, ) @@ -686,6 +821,9 @@ def __init__(self, workspace_preferences: AsyncWorkspacePreferencesResource) -> self.archive = async_to_raw_response_wrapper( workspace_preferences.archive, ) + self.list_logs = async_to_raw_response_wrapper( + workspace_preferences.list_logs, + ) self.publish = async_to_raw_response_wrapper( workspace_preferences.publish, ) @@ -714,6 +852,9 @@ def __init__(self, workspace_preferences: WorkspacePreferencesResource) -> None: self.archive = to_streamed_response_wrapper( workspace_preferences.archive, ) + self.list_logs = to_streamed_response_wrapper( + workspace_preferences.list_logs, + ) self.publish = to_streamed_response_wrapper( workspace_preferences.publish, ) @@ -742,6 +883,9 @@ def __init__(self, workspace_preferences: AsyncWorkspacePreferencesResource) -> self.archive = async_to_streamed_response_wrapper( workspace_preferences.archive, ) + self.list_logs = async_to_streamed_response_wrapper( + workspace_preferences.list_logs, + ) self.publish = async_to_streamed_response_wrapper( workspace_preferences.publish, ) diff --git a/src/courier/types/__init__.py b/src/courier/types/__init__.py index 6fedc45..eeb635d 100644 --- a/src/courier/types/__init__.py +++ b/src/courier/types/__init__.py @@ -268,6 +268,8 @@ from .notification_replace_params import NotificationReplaceParams as NotificationReplaceParams from .notification_template_alias import NotificationTemplateAlias as NotificationTemplateAlias from .notification_template_state import NotificationTemplateState as NotificationTemplateState +from .preference_change_log_entry import PreferenceChangeLogEntry as PreferenceChangeLogEntry +from .preference_change_log_value import PreferenceChangeLogValue as PreferenceChangeLogValue from .tenant_template_input_param import TenantTemplateInputParam as TenantTemplateInputParam from .topic_digest_category_param import TopicDigestCategoryParam as TopicDigestCategoryParam from .audience_list_members_params import AudienceListMembersParams as AudienceListMembersParams @@ -295,6 +297,7 @@ from .notification_metrics_response import NotificationMetricsResponse as NotificationMetricsResponse from .notification_template_payload import NotificationTemplatePayload as NotificationTemplatePayload from .notification_template_summary import NotificationTemplateSummary as NotificationTemplateSummary +from .preference_logs_list_response import PreferenceLogsListResponse as PreferenceLogsListResponse from .routing_strategy_get_response import RoutingStrategyGetResponse as RoutingStrategyGetResponse from .translation_retrieve_response import TranslationRetrieveResponse as TranslationRetrieveResponse from .audience_list_members_response import AudienceListMembersResponse as AudienceListMembersResponse @@ -351,6 +354,9 @@ PostTenantTemplatePublishResponse as PostTenantTemplatePublishResponse, ) from .subscribe_to_lists_request_item_param import SubscribeToListsRequestItemParam as SubscribeToListsRequestItemParam +from .workspace_preference_list_logs_params import ( + WorkspacePreferenceListLogsParams as WorkspacePreferenceListLogsParams, +) from .journey_send_node_to_slack_email_param import JourneySendNodeToSlackEmailParam as JourneySendNodeToSlackEmailParam from .notification_content_mutation_response import ( NotificationContentMutationResponse as NotificationContentMutationResponse, diff --git a/src/courier/types/preference_change_log_entry.py b/src/courier/types/preference_change_log_entry.py new file mode 100644 index 0000000..4fa28ca --- /dev/null +++ b/src/courier/types/preference_change_log_entry.py @@ -0,0 +1,52 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from .._models import BaseModel +from .shared.preference_status import PreferenceStatus +from .preference_change_log_value import PreferenceChangeLogValue +from .shared.channel_classification import ChannelClassification + +__all__ = ["PreferenceChangeLogEntry"] + + +class PreferenceChangeLogEntry(BaseModel): + id: str + """Unique identifier for this change.""" + + custom_routing: List[ChannelClassification] + """The channels chosen for this topic, present only when has_custom_routing is + true. + + Empty otherwise. + """ + + has_custom_routing: bool + """ + Whether specific delivery channels were chosen for this topic rather than the + topic's default routing. + """ + + status: PreferenceStatus + """The subscription status the change set.""" + + timestamp: str + """When the change was made, as an ISO-8601 date-time in UTC.""" + + topic_id: str + """The subscription topic the change applies to.""" + + topic_name: str + """The display name of that topic when the change was made.""" + + user_id: str + """The user whose preference changed.""" + + previous: Optional[PreferenceChangeLogValue] = None + """The value before this change, where it was recorded.""" + + tenant_id: Optional[str] = None + """The tenant context the change was made in. + + Absent when the user set the preference outside any tenant. + """ diff --git a/src/courier/types/preference_change_log_value.py b/src/courier/types/preference_change_log_value.py new file mode 100644 index 0000000..c90e88d --- /dev/null +++ b/src/courier/types/preference_change_log_value.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from .._models import BaseModel +from .shared.preference_status import PreferenceStatus +from .shared.channel_classification import ChannelClassification + +__all__ = ["PreferenceChangeLogValue"] + + +class PreferenceChangeLogValue(BaseModel): + custom_routing: List[ChannelClassification] + """The channels chosen before the change.""" + + has_custom_routing: bool + """Whether custom routing was in effect before the change.""" + + status: PreferenceStatus + """The subscription status before the change.""" diff --git a/src/courier/types/preference_logs_list_response.py b/src/courier/types/preference_logs_list_response.py new file mode 100644 index 0000000..56587b0 --- /dev/null +++ b/src/courier/types/preference_logs_list_response.py @@ -0,0 +1,16 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List + +from .._models import BaseModel +from .shared.paging import Paging +from .preference_change_log_entry import PreferenceChangeLogEntry + +__all__ = ["PreferenceLogsListResponse"] + + +class PreferenceLogsListResponse(BaseModel): + items: List[PreferenceChangeLogEntry] + """One entry per preference change, newest first.""" + + paging: Paging diff --git a/src/courier/types/workspace_preference_list_logs_params.py b/src/courier/types/workspace_preference_list_logs_params.py new file mode 100644 index 0000000..6524c21 --- /dev/null +++ b/src/courier/types/workspace_preference_list_logs_params.py @@ -0,0 +1,36 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["WorkspacePreferenceListLogsParams"] + + +class WorkspacePreferenceListLogsParams(TypedDict, total=False): + cursor: str + """A cursor from a previous response's paging.cursor. + + Continue only while paging.more is true; the cursor is omitted on the last page. + """ + + limit: int + """How many entries to return. Defaults to 25.""" + + since: str + """Return only changes at or after this time, as an ISO-8601 date or date-time. + + A date alone is read as the start of that day in UTC. + """ + + tenant_id: str + """Narrow to the changes this user made in one tenant context. + + Only valid together with user_id. + """ + + user_id: str + """Return only this user's changes. + + Omit it to read every change in the environment. + """ diff --git a/tests/api_resources/test_workspace_preferences.py b/tests/api_resources/test_workspace_preferences.py index 3257332..ee89394 100644 --- a/tests/api_resources/test_workspace_preferences.py +++ b/tests/api_resources/test_workspace_preferences.py @@ -10,6 +10,7 @@ from courier import Courier, AsyncCourier from tests.utils import assert_matches_type from courier.types import ( + PreferenceLogsListResponse, PublishPreferencesResponse, WorkspacePreferenceGetResponse, WorkspacePreferenceListResponse, @@ -180,6 +181,46 @@ def test_path_params_archive(self, client: Courier) -> None: "", ) + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_list_logs(self, client: Courier) -> None: + workspace_preference = client.workspace_preferences.list_logs() + assert_matches_type(PreferenceLogsListResponse, workspace_preference, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_list_logs_with_all_params(self, client: Courier) -> None: + workspace_preference = client.workspace_preferences.list_logs( + cursor="cursor", + limit=1, + since="since", + tenant_id="tenant_id", + user_id="user_id", + ) + assert_matches_type(PreferenceLogsListResponse, workspace_preference, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_list_logs(self, client: Courier) -> None: + response = client.workspace_preferences.with_raw_response.list_logs() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + workspace_preference = response.parse() + assert_matches_type(PreferenceLogsListResponse, workspace_preference, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_list_logs(self, client: Courier) -> None: + with client.workspace_preferences.with_streaming_response.list_logs() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + workspace_preference = response.parse() + assert_matches_type(PreferenceLogsListResponse, workspace_preference, path=["response"]) + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize def test_method_publish(self, client: Courier) -> None: @@ -443,6 +484,46 @@ async def test_path_params_archive(self, async_client: AsyncCourier) -> None: "", ) + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_list_logs(self, async_client: AsyncCourier) -> None: + workspace_preference = await async_client.workspace_preferences.list_logs() + assert_matches_type(PreferenceLogsListResponse, workspace_preference, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_list_logs_with_all_params(self, async_client: AsyncCourier) -> None: + workspace_preference = await async_client.workspace_preferences.list_logs( + cursor="cursor", + limit=1, + since="since", + tenant_id="tenant_id", + user_id="user_id", + ) + assert_matches_type(PreferenceLogsListResponse, workspace_preference, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_list_logs(self, async_client: AsyncCourier) -> None: + response = await async_client.workspace_preferences.with_raw_response.list_logs() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + workspace_preference = await response.parse() + assert_matches_type(PreferenceLogsListResponse, workspace_preference, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_list_logs(self, async_client: AsyncCourier) -> None: + async with async_client.workspace_preferences.with_streaming_response.list_logs() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + workspace_preference = await response.parse() + assert_matches_type(PreferenceLogsListResponse, workspace_preference, path=["response"]) + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize async def test_method_publish(self, async_client: AsyncCourier) -> None: