diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 257e308..44959ac 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.34.0" + ".": "1.35.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index c2c8b51..97520a7 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 27 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/moderation-api/moderation-api-984af2e010ad1bf22f607ee96f27260ecf421c0ce5664839799b92eb349f9255.yml -openapi_spec_hash: bbd9dbdc1a4b7b0ad35aa08fba66e676 -config_hash: 9d144cc6c49d3fd53e5b4472c1e22165 +configured_endpoints: 34 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/moderation-api/moderation-api-407f949d64421d5454ec0f4b518a19c14daa41312b6efa370eac631e495ca44a.yml +openapi_spec_hash: 7bcba0df8e8cb1c5a2d02efc3d6c0fa5 +config_hash: 6e090a71f4d354167d93b4534a0e7f05 diff --git a/CHANGELOG.md b/CHANGELOG.md index b682512..8cd1a61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.35.0 (2026-08-20) + +Full Changelog: [v1.34.0...v1.35.0](https://github.com/moderation-api/sdk-python/compare/v1.34.0...v1.35.0) + +### Features + +* **api:** webhooks and stream ([4f4bf9f](https://github.com/moderation-api/sdk-python/commit/4f4bf9f2d1e2b6c5ddea270e16fd4d8f10600618)) + ## 1.34.0 (2026-08-14) Full Changelog: [v1.33.0...v1.34.0](https://github.com/moderation-api/sdk-python/compare/v1.33.0...v1.34.0) diff --git a/api.md b/api.md index 478b6df..bf7cc9f 100644 --- a/api.md +++ b/api.md @@ -92,6 +92,7 @@ from moderation_api.types import ContentSubmitResponse Methods: +- client.content.stream() -> None - client.content.submit(\*\*params) -> ContentSubmitResponse # Account @@ -151,3 +152,37 @@ Methods: - client.wordlist.words.add(id, \*\*params) -> WordAddResponse - client.wordlist.words.remove(id, \*\*params) -> WordRemoveResponse + +# Webhooks + +Types: + +```python +from moderation_api.types import ( + WebhookCreateResponse, + WebhookRetrieveResponse, + WebhookUpdateResponse, + WebhookListResponse, + WebhookDeleteResponse, +) +``` + +Methods: + +- client.webhooks.create(\*\*params) -> WebhookCreateResponse +- client.webhooks.retrieve(id) -> WebhookRetrieveResponse +- client.webhooks.update(id, \*\*params) -> WebhookUpdateResponse +- client.webhooks.list() -> WebhookListResponse +- client.webhooks.delete(id) -> WebhookDeleteResponse + +# WebhookSecret + +Types: + +```python +from moderation_api.types import WebhookSecretRetrieveResponse +``` + +Methods: + +- client.webhook_secret.retrieve() -> WebhookSecretRetrieveResponse diff --git a/pyproject.toml b/pyproject.toml index 6834ebd..4e756b9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "moderation_api" -version = "1.34.0" +version = "1.35.0" description = "The official Python library for the moderation-api API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/moderation_api/_client.py b/src/moderation_api/_client.py index fc2797e..6bdd9f5 100644 --- a/src/moderation_api/_client.py +++ b/src/moderation_api/_client.py @@ -35,12 +35,14 @@ ) if TYPE_CHECKING: - from .resources import auth, queue, account, actions, authors, content, wordlist + from .resources import auth, queue, account, actions, authors, content, webhooks, wordlist, webhook_secret from .resources.auth import AuthResource, AsyncAuthResource from .resources.account import AccountResource, AsyncAccountResource from .resources.authors import AuthorsResource, AsyncAuthorsResource from .resources.content import ContentResource, AsyncContentResource + from .resources.webhooks import WebhooksResource, AsyncWebhooksResource from .resources.queue.queue import QueueResource, AsyncQueueResource + from .resources.webhook_secret import WebhookSecretResource, AsyncWebhookSecretResource from .resources.actions.actions import ActionsResource, AsyncActionsResource from .resources.wordlist.wordlist import WordlistResource, AsyncWordlistResource @@ -97,6 +99,7 @@ def __init__( if base_url is None: base_url = os.environ.get("MODERATION_API_BASE_URL") + self._base_url_overridden = base_url is not None if base_url is None: base_url = f"https://api.moderationapi.com/v1" @@ -162,6 +165,18 @@ def wordlist(self) -> WordlistResource: return WordlistResource(self) + @cached_property + def webhooks(self) -> WebhooksResource: + from .resources.webhooks import WebhooksResource + + return WebhooksResource(self) + + @cached_property + def webhook_secret(self) -> WebhookSecretResource: + from .resources.webhook_secret import WebhookSecretResource + + return WebhookSecretResource(self) + @cached_property def with_raw_response(self) -> ModerationAPIWithRawResponse: return ModerationAPIWithRawResponse(self) @@ -226,7 +241,7 @@ def copy( params = set_default_query http_client = http_client or self._client - return self.__class__( + client = self.__class__( secret_key=secret_key or self.secret_key, base_url=base_url or self.base_url, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, @@ -236,6 +251,8 @@ def copy( default_query=params, **_extra_kwargs, ) + client._base_url_overridden = self._base_url_overridden or base_url is not None + return client # Alias for `copy` for nicer inline usage, e.g. # client.with_options(timeout=10).foo.create(...) @@ -316,6 +333,7 @@ def __init__( if base_url is None: base_url = os.environ.get("MODERATION_API_BASE_URL") + self._base_url_overridden = base_url is not None if base_url is None: base_url = f"https://api.moderationapi.com/v1" @@ -381,6 +399,18 @@ def wordlist(self) -> AsyncWordlistResource: return AsyncWordlistResource(self) + @cached_property + def webhooks(self) -> AsyncWebhooksResource: + from .resources.webhooks import AsyncWebhooksResource + + return AsyncWebhooksResource(self) + + @cached_property + def webhook_secret(self) -> AsyncWebhookSecretResource: + from .resources.webhook_secret import AsyncWebhookSecretResource + + return AsyncWebhookSecretResource(self) + @cached_property def with_raw_response(self) -> AsyncModerationAPIWithRawResponse: return AsyncModerationAPIWithRawResponse(self) @@ -445,7 +475,7 @@ def copy( params = set_default_query http_client = http_client or self._client - return self.__class__( + client = self.__class__( secret_key=secret_key or self.secret_key, base_url=base_url or self.base_url, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, @@ -455,6 +485,8 @@ def copy( default_query=params, **_extra_kwargs, ) + client._base_url_overridden = self._base_url_overridden or base_url is not None + return client # Alias for `copy` for nicer inline usage, e.g. # client.with_options(timeout=10).foo.create(...) @@ -542,6 +574,18 @@ def wordlist(self) -> wordlist.WordlistResourceWithRawResponse: return WordlistResourceWithRawResponse(self._client.wordlist) + @cached_property + def webhooks(self) -> webhooks.WebhooksResourceWithRawResponse: + from .resources.webhooks import WebhooksResourceWithRawResponse + + return WebhooksResourceWithRawResponse(self._client.webhooks) + + @cached_property + def webhook_secret(self) -> webhook_secret.WebhookSecretResourceWithRawResponse: + from .resources.webhook_secret import WebhookSecretResourceWithRawResponse + + return WebhookSecretResourceWithRawResponse(self._client.webhook_secret) + class AsyncModerationAPIWithRawResponse: _client: AsyncModerationAPI @@ -591,6 +635,18 @@ def wordlist(self) -> wordlist.AsyncWordlistResourceWithRawResponse: return AsyncWordlistResourceWithRawResponse(self._client.wordlist) + @cached_property + def webhooks(self) -> webhooks.AsyncWebhooksResourceWithRawResponse: + from .resources.webhooks import AsyncWebhooksResourceWithRawResponse + + return AsyncWebhooksResourceWithRawResponse(self._client.webhooks) + + @cached_property + def webhook_secret(self) -> webhook_secret.AsyncWebhookSecretResourceWithRawResponse: + from .resources.webhook_secret import AsyncWebhookSecretResourceWithRawResponse + + return AsyncWebhookSecretResourceWithRawResponse(self._client.webhook_secret) + class ModerationAPIWithStreamedResponse: _client: ModerationAPI @@ -640,6 +696,18 @@ def wordlist(self) -> wordlist.WordlistResourceWithStreamingResponse: return WordlistResourceWithStreamingResponse(self._client.wordlist) + @cached_property + def webhooks(self) -> webhooks.WebhooksResourceWithStreamingResponse: + from .resources.webhooks import WebhooksResourceWithStreamingResponse + + return WebhooksResourceWithStreamingResponse(self._client.webhooks) + + @cached_property + def webhook_secret(self) -> webhook_secret.WebhookSecretResourceWithStreamingResponse: + from .resources.webhook_secret import WebhookSecretResourceWithStreamingResponse + + return WebhookSecretResourceWithStreamingResponse(self._client.webhook_secret) + class AsyncModerationAPIWithStreamedResponse: _client: AsyncModerationAPI @@ -689,6 +757,18 @@ def wordlist(self) -> wordlist.AsyncWordlistResourceWithStreamingResponse: return AsyncWordlistResourceWithStreamingResponse(self._client.wordlist) + @cached_property + def webhooks(self) -> webhooks.AsyncWebhooksResourceWithStreamingResponse: + from .resources.webhooks import AsyncWebhooksResourceWithStreamingResponse + + return AsyncWebhooksResourceWithStreamingResponse(self._client.webhooks) + + @cached_property + def webhook_secret(self) -> webhook_secret.AsyncWebhookSecretResourceWithStreamingResponse: + from .resources.webhook_secret import AsyncWebhookSecretResourceWithStreamingResponse + + return AsyncWebhookSecretResourceWithStreamingResponse(self._client.webhook_secret) + Client = ModerationAPI diff --git a/src/moderation_api/_version.py b/src/moderation_api/_version.py index 7ed0731..ba231c7 100644 --- a/src/moderation_api/_version.py +++ b/src/moderation_api/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "moderation_api" -__version__ = "1.34.0" # x-release-please-version +__version__ = "1.35.0" # x-release-please-version diff --git a/src/moderation_api/resources/__init__.py b/src/moderation_api/resources/__init__.py index 5d14523..2a7ead1 100644 --- a/src/moderation_api/resources/__init__.py +++ b/src/moderation_api/resources/__init__.py @@ -48,6 +48,14 @@ ContentResourceWithStreamingResponse, AsyncContentResourceWithStreamingResponse, ) +from .webhooks import ( + WebhooksResource, + AsyncWebhooksResource, + WebhooksResourceWithRawResponse, + AsyncWebhooksResourceWithRawResponse, + WebhooksResourceWithStreamingResponse, + AsyncWebhooksResourceWithStreamingResponse, +) from .wordlist import ( WordlistResource, AsyncWordlistResource, @@ -56,6 +64,14 @@ WordlistResourceWithStreamingResponse, AsyncWordlistResourceWithStreamingResponse, ) +from .webhook_secret import ( + WebhookSecretResource, + AsyncWebhookSecretResource, + WebhookSecretResourceWithRawResponse, + AsyncWebhookSecretResourceWithRawResponse, + WebhookSecretResourceWithStreamingResponse, + AsyncWebhookSecretResourceWithStreamingResponse, +) __all__ = [ "AuthorsResource", @@ -100,4 +116,16 @@ "AsyncWordlistResourceWithRawResponse", "WordlistResourceWithStreamingResponse", "AsyncWordlistResourceWithStreamingResponse", + "WebhooksResource", + "AsyncWebhooksResource", + "WebhooksResourceWithRawResponse", + "AsyncWebhooksResourceWithRawResponse", + "WebhooksResourceWithStreamingResponse", + "AsyncWebhooksResourceWithStreamingResponse", + "WebhookSecretResource", + "AsyncWebhookSecretResource", + "WebhookSecretResourceWithRawResponse", + "AsyncWebhookSecretResourceWithRawResponse", + "WebhookSecretResourceWithStreamingResponse", + "AsyncWebhookSecretResourceWithStreamingResponse", ] diff --git a/src/moderation_api/resources/content.py b/src/moderation_api/resources/content.py index 7938459..9d68f93 100644 --- a/src/moderation_api/resources/content.py +++ b/src/moderation_api/resources/content.py @@ -8,7 +8,7 @@ import httpx from ..types import content_submit_params -from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource @@ -44,6 +44,59 @@ def with_streaming_response(self) -> ContentResourceWithStreamingResponse: """ return ContentResourceWithStreamingResponse(self) + def stream( + self, + *, + sec_web_socket_protocol: Literal["moderationapi.v1"], + # 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, + ) -> None: + """Open a WebSocket to moderate live voice/call audio in real time. + + Speech is + transcribed and each finalized utterance is moderated by your enabled text + policies; you receive a verdict per utterance as it's spoken. + + **This is a WebSocket upgrade, not a regular HTTP call.** The request body below + documents the frames you _send_ over the socket; the `101` response documents + the events you _receive_. + + - **Auth:** `Authorization: Bearer ` on the upgrade. A missing/invalid + key closes `4401`; voice not enabled on the plan/channel closes `4403`. + - **Subprotocol:** request `moderationapi.v1`. + - **Flow:** send one `start` frame, then `media` frames as audio arrives, then + `stop` (or disconnect). You receive `session.started`, `utterance.final` per + utterance, optional `utterance.partial`/`warning`, and `session.ended`. + - **Close codes:** `1000` normal · `1011` server error · `4400` bad request · + `4401` auth failed · `4403` voice not enabled · `4429` concurrency limit. + + See the + [Real-time voice guide](https://docs.moderationapi.com/content-moderation/real-time-voice) + for the full walkthrough and code examples. + + Args: + 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 + """ + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + extra_headers.update({"Sec-WebSocket-Protocol": str(sec_web_socket_protocol)}) + return self._get( + "/stream" if self._client._base_url_overridden else "wss://voice.moderationapi.com/v1/stream", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + def submit( self, *, @@ -147,6 +200,59 @@ def with_streaming_response(self) -> AsyncContentResourceWithStreamingResponse: """ return AsyncContentResourceWithStreamingResponse(self) + async def stream( + self, + *, + sec_web_socket_protocol: Literal["moderationapi.v1"], + # 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, + ) -> None: + """Open a WebSocket to moderate live voice/call audio in real time. + + Speech is + transcribed and each finalized utterance is moderated by your enabled text + policies; you receive a verdict per utterance as it's spoken. + + **This is a WebSocket upgrade, not a regular HTTP call.** The request body below + documents the frames you _send_ over the socket; the `101` response documents + the events you _receive_. + + - **Auth:** `Authorization: Bearer ` on the upgrade. A missing/invalid + key closes `4401`; voice not enabled on the plan/channel closes `4403`. + - **Subprotocol:** request `moderationapi.v1`. + - **Flow:** send one `start` frame, then `media` frames as audio arrives, then + `stop` (or disconnect). You receive `session.started`, `utterance.final` per + utterance, optional `utterance.partial`/`warning`, and `session.ended`. + - **Close codes:** `1000` normal · `1011` server error · `4400` bad request · + `4401` auth failed · `4403` voice not enabled · `4429` concurrency limit. + + See the + [Real-time voice guide](https://docs.moderationapi.com/content-moderation/real-time-voice) + for the full walkthrough and code examples. + + Args: + 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 + """ + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + extra_headers.update({"Sec-WebSocket-Protocol": str(sec_web_socket_protocol)}) + return await self._get( + "/stream" if self._client._base_url_overridden else "wss://voice.moderationapi.com/v1/stream", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + async def submit( self, *, @@ -234,6 +340,9 @@ class ContentResourceWithRawResponse: def __init__(self, content: ContentResource) -> None: self._content = content + self.stream = to_raw_response_wrapper( + content.stream, + ) self.submit = to_raw_response_wrapper( content.submit, ) @@ -243,6 +352,9 @@ class AsyncContentResourceWithRawResponse: def __init__(self, content: AsyncContentResource) -> None: self._content = content + self.stream = async_to_raw_response_wrapper( + content.stream, + ) self.submit = async_to_raw_response_wrapper( content.submit, ) @@ -252,6 +364,9 @@ class ContentResourceWithStreamingResponse: def __init__(self, content: ContentResource) -> None: self._content = content + self.stream = to_streamed_response_wrapper( + content.stream, + ) self.submit = to_streamed_response_wrapper( content.submit, ) @@ -261,6 +376,9 @@ class AsyncContentResourceWithStreamingResponse: def __init__(self, content: AsyncContentResource) -> None: self._content = content + self.stream = async_to_streamed_response_wrapper( + content.stream, + ) self.submit = async_to_streamed_response_wrapper( content.submit, ) diff --git a/src/moderation_api/resources/webhook_secret.py b/src/moderation_api/resources/webhook_secret.py new file mode 100644 index 0000000..0bbd008 --- /dev/null +++ b/src/moderation_api/resources/webhook_secret.py @@ -0,0 +1,143 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from .._types import Body, Query, Headers, NotGiven, not_given +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 .._base_client import make_request_options +from ..types.webhook_secret_retrieve_response import WebhookSecretRetrieveResponse + +__all__ = ["WebhookSecretResource", "AsyncWebhookSecretResource"] + + +class WebhookSecretResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> WebhookSecretResourceWithRawResponse: + """ + 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/moderation-api/sdk-python#accessing-raw-response-data-eg-headers + """ + return WebhookSecretResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> WebhookSecretResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/moderation-api/sdk-python#with_streaming_response + """ + return WebhookSecretResourceWithStreamingResponse(self) + + def retrieve( + self, + *, + # 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, + ) -> WebhookSecretRetrieveResponse: + """ + Get the signing secret used to sign webhook deliveries for this project, + creating one if none exists yet. Verify deliveries by comparing the + `modapi-signature` header to HMAC-SHA256(raw request body, secret) hex-encoded. + """ + return self._get( + "/webhook-secret", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WebhookSecretRetrieveResponse, + ) + + +class AsyncWebhookSecretResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncWebhookSecretResourceWithRawResponse: + """ + 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/moderation-api/sdk-python#accessing-raw-response-data-eg-headers + """ + return AsyncWebhookSecretResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncWebhookSecretResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/moderation-api/sdk-python#with_streaming_response + """ + return AsyncWebhookSecretResourceWithStreamingResponse(self) + + async def retrieve( + self, + *, + # 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, + ) -> WebhookSecretRetrieveResponse: + """ + Get the signing secret used to sign webhook deliveries for this project, + creating one if none exists yet. Verify deliveries by comparing the + `modapi-signature` header to HMAC-SHA256(raw request body, secret) hex-encoded. + """ + return await self._get( + "/webhook-secret", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WebhookSecretRetrieveResponse, + ) + + +class WebhookSecretResourceWithRawResponse: + def __init__(self, webhook_secret: WebhookSecretResource) -> None: + self._webhook_secret = webhook_secret + + self.retrieve = to_raw_response_wrapper( + webhook_secret.retrieve, + ) + + +class AsyncWebhookSecretResourceWithRawResponse: + def __init__(self, webhook_secret: AsyncWebhookSecretResource) -> None: + self._webhook_secret = webhook_secret + + self.retrieve = async_to_raw_response_wrapper( + webhook_secret.retrieve, + ) + + +class WebhookSecretResourceWithStreamingResponse: + def __init__(self, webhook_secret: WebhookSecretResource) -> None: + self._webhook_secret = webhook_secret + + self.retrieve = to_streamed_response_wrapper( + webhook_secret.retrieve, + ) + + +class AsyncWebhookSecretResourceWithStreamingResponse: + def __init__(self, webhook_secret: AsyncWebhookSecretResource) -> None: + self._webhook_secret = webhook_secret + + self.retrieve = async_to_streamed_response_wrapper( + webhook_secret.retrieve, + ) diff --git a/src/moderation_api/resources/webhooks.py b/src/moderation_api/resources/webhooks.py new file mode 100644 index 0000000..1366369 --- /dev/null +++ b/src/moderation_api/resources/webhooks.py @@ -0,0 +1,618 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional +from typing_extensions import Literal + +import httpx + +from ..types import webhook_create_params, webhook_update_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import path_template, maybe_transform, async_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 .._base_client import make_request_options +from ..types.webhook_list_response import WebhookListResponse +from ..types.webhook_create_response import WebhookCreateResponse +from ..types.webhook_delete_response import WebhookDeleteResponse +from ..types.webhook_update_response import WebhookUpdateResponse +from ..types.webhook_retrieve_response import WebhookRetrieveResponse + +__all__ = ["WebhooksResource", "AsyncWebhooksResource"] + + +class WebhooksResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> WebhooksResourceWithRawResponse: + """ + 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/moderation-api/sdk-python#accessing-raw-response-data-eg-headers + """ + return WebhooksResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> WebhooksResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/moderation-api/sdk-python#with_streaming_response + """ + return WebhooksResourceWithStreamingResponse(self) + + def create( + self, + *, + event_types: List[ + Literal[ + "QUEUE_ITEM_NEW", + "QUEUE_ITEM_COMPLETED", + "QUEUE_ITEM_ACTION", + "QUEUE_ITEM_REJECTED", + "QUEUE_ITEM_ALLOWED", + "AUTHOR_BLOCKED", + "AUTHOR_UNBLOCKED", + "AUTHOR_SUSPENDED", + "AUTHOR_UPDATED", + "AUTHOR_TRUST_LEVEL_CHANGED", + "AUTHOR_ACTION", + ] + ], + name: str, + url: str, + description: Optional[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, + ) -> WebhookCreateResponse: + """Create a webhook subscribed to one or more event types. + + Deliveries use the v2 + envelope and are signed with the project signing secret (see the signing secret + endpoint). + + Args: + event_types: Event types this webhook subscribes to. One webhook URL receives all events you + list here. + + name: The webhook's name, used to identify it in the dashboard + + url: The webhook's URL. We'll call this URL when an event occurs. + + description: The webhook's description + + 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._post( + "/webhooks", + body=maybe_transform( + { + "event_types": event_types, + "name": name, + "url": url, + "description": description, + }, + webhook_create_params.WebhookCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WebhookCreateResponse, + ) + + def retrieve( + self, + id: str, + *, + # 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, + ) -> WebhookRetrieveResponse: + """ + Get a webhook by ID. + + Args: + id: The ID of the webhook to get. + + 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 id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + path_template("/webhooks/{id}", id=id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WebhookRetrieveResponse, + ) + + def update( + self, + id: str, + *, + description: Optional[str] | Omit = omit, + event_types: List[ + Literal[ + "QUEUE_ITEM_NEW", + "QUEUE_ITEM_COMPLETED", + "QUEUE_ITEM_ACTION", + "QUEUE_ITEM_REJECTED", + "QUEUE_ITEM_ALLOWED", + "AUTHOR_BLOCKED", + "AUTHOR_UNBLOCKED", + "AUTHOR_SUSPENDED", + "AUTHOR_UPDATED", + "AUTHOR_TRUST_LEVEL_CHANGED", + "AUTHOR_ACTION", + ] + ] + | Omit = omit, + name: str | Omit = omit, + url: 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, + ) -> WebhookUpdateResponse: + """Update a webhook. + + Legacy v1 webhooks are read-only: delete them and create a new + webhook instead. + + Args: + id: The ID of the webhook to update. + + description: The webhook's description + + event_types: Event types this webhook subscribes to. One webhook URL receives all events you + list here. + + name: The webhook's name, used to identify it in the dashboard + + url: The webhook's URL. We'll call this URL when an event occurs. + + 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 id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._put( + path_template("/webhooks/{id}", id=id), + body=maybe_transform( + { + "description": description, + "event_types": event_types, + "name": name, + "url": url, + }, + webhook_update_params.WebhookUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WebhookUpdateResponse, + ) + + def list( + self, + *, + # 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, + ) -> WebhookListResponse: + """List all webhooks for the authenticated project.""" + return self._get( + "/webhooks", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WebhookListResponse, + ) + + def delete( + self, + id: str, + *, + # 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, + ) -> WebhookDeleteResponse: + """ + Delete a webhook. + + Args: + id: The ID of the webhook to delete. + + 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 id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._delete( + path_template("/webhooks/{id}", id=id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WebhookDeleteResponse, + ) + + +class AsyncWebhooksResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncWebhooksResourceWithRawResponse: + """ + 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/moderation-api/sdk-python#accessing-raw-response-data-eg-headers + """ + return AsyncWebhooksResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncWebhooksResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/moderation-api/sdk-python#with_streaming_response + """ + return AsyncWebhooksResourceWithStreamingResponse(self) + + async def create( + self, + *, + event_types: List[ + Literal[ + "QUEUE_ITEM_NEW", + "QUEUE_ITEM_COMPLETED", + "QUEUE_ITEM_ACTION", + "QUEUE_ITEM_REJECTED", + "QUEUE_ITEM_ALLOWED", + "AUTHOR_BLOCKED", + "AUTHOR_UNBLOCKED", + "AUTHOR_SUSPENDED", + "AUTHOR_UPDATED", + "AUTHOR_TRUST_LEVEL_CHANGED", + "AUTHOR_ACTION", + ] + ], + name: str, + url: str, + description: Optional[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, + ) -> WebhookCreateResponse: + """Create a webhook subscribed to one or more event types. + + Deliveries use the v2 + envelope and are signed with the project signing secret (see the signing secret + endpoint). + + Args: + event_types: Event types this webhook subscribes to. One webhook URL receives all events you + list here. + + name: The webhook's name, used to identify it in the dashboard + + url: The webhook's URL. We'll call this URL when an event occurs. + + description: The webhook's description + + 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._post( + "/webhooks", + body=await async_maybe_transform( + { + "event_types": event_types, + "name": name, + "url": url, + "description": description, + }, + webhook_create_params.WebhookCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WebhookCreateResponse, + ) + + async def retrieve( + self, + id: str, + *, + # 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, + ) -> WebhookRetrieveResponse: + """ + Get a webhook by ID. + + Args: + id: The ID of the webhook to get. + + 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 id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + path_template("/webhooks/{id}", id=id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WebhookRetrieveResponse, + ) + + async def update( + self, + id: str, + *, + description: Optional[str] | Omit = omit, + event_types: List[ + Literal[ + "QUEUE_ITEM_NEW", + "QUEUE_ITEM_COMPLETED", + "QUEUE_ITEM_ACTION", + "QUEUE_ITEM_REJECTED", + "QUEUE_ITEM_ALLOWED", + "AUTHOR_BLOCKED", + "AUTHOR_UNBLOCKED", + "AUTHOR_SUSPENDED", + "AUTHOR_UPDATED", + "AUTHOR_TRUST_LEVEL_CHANGED", + "AUTHOR_ACTION", + ] + ] + | Omit = omit, + name: str | Omit = omit, + url: 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, + ) -> WebhookUpdateResponse: + """Update a webhook. + + Legacy v1 webhooks are read-only: delete them and create a new + webhook instead. + + Args: + id: The ID of the webhook to update. + + description: The webhook's description + + event_types: Event types this webhook subscribes to. One webhook URL receives all events you + list here. + + name: The webhook's name, used to identify it in the dashboard + + url: The webhook's URL. We'll call this URL when an event occurs. + + 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 id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._put( + path_template("/webhooks/{id}", id=id), + body=await async_maybe_transform( + { + "description": description, + "event_types": event_types, + "name": name, + "url": url, + }, + webhook_update_params.WebhookUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WebhookUpdateResponse, + ) + + async def list( + self, + *, + # 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, + ) -> WebhookListResponse: + """List all webhooks for the authenticated project.""" + return await self._get( + "/webhooks", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WebhookListResponse, + ) + + async def delete( + self, + id: str, + *, + # 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, + ) -> WebhookDeleteResponse: + """ + Delete a webhook. + + Args: + id: The ID of the webhook to delete. + + 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 id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._delete( + path_template("/webhooks/{id}", id=id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WebhookDeleteResponse, + ) + + +class WebhooksResourceWithRawResponse: + def __init__(self, webhooks: WebhooksResource) -> None: + self._webhooks = webhooks + + self.create = to_raw_response_wrapper( + webhooks.create, + ) + self.retrieve = to_raw_response_wrapper( + webhooks.retrieve, + ) + self.update = to_raw_response_wrapper( + webhooks.update, + ) + self.list = to_raw_response_wrapper( + webhooks.list, + ) + self.delete = to_raw_response_wrapper( + webhooks.delete, + ) + + +class AsyncWebhooksResourceWithRawResponse: + def __init__(self, webhooks: AsyncWebhooksResource) -> None: + self._webhooks = webhooks + + self.create = async_to_raw_response_wrapper( + webhooks.create, + ) + self.retrieve = async_to_raw_response_wrapper( + webhooks.retrieve, + ) + self.update = async_to_raw_response_wrapper( + webhooks.update, + ) + self.list = async_to_raw_response_wrapper( + webhooks.list, + ) + self.delete = async_to_raw_response_wrapper( + webhooks.delete, + ) + + +class WebhooksResourceWithStreamingResponse: + def __init__(self, webhooks: WebhooksResource) -> None: + self._webhooks = webhooks + + self.create = to_streamed_response_wrapper( + webhooks.create, + ) + self.retrieve = to_streamed_response_wrapper( + webhooks.retrieve, + ) + self.update = to_streamed_response_wrapper( + webhooks.update, + ) + self.list = to_streamed_response_wrapper( + webhooks.list, + ) + self.delete = to_streamed_response_wrapper( + webhooks.delete, + ) + + +class AsyncWebhooksResourceWithStreamingResponse: + def __init__(self, webhooks: AsyncWebhooksResource) -> None: + self._webhooks = webhooks + + self.create = async_to_streamed_response_wrapper( + webhooks.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + webhooks.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + webhooks.update, + ) + self.list = async_to_streamed_response_wrapper( + webhooks.list, + ) + self.delete = async_to_streamed_response_wrapper( + webhooks.delete, + ) diff --git a/src/moderation_api/types/__init__.py b/src/moderation_api/types/__init__.py index 0db1c73..217c8c2 100644 --- a/src/moderation_api/types/__init__.py +++ b/src/moderation_api/types/__init__.py @@ -13,6 +13,9 @@ from .author_update_params import AuthorUpdateParams as AuthorUpdateParams from .account_list_response import AccountListResponse as AccountListResponse from .content_submit_params import ContentSubmitParams as ContentSubmitParams +from .webhook_create_params import WebhookCreateParams as WebhookCreateParams +from .webhook_list_response import WebhookListResponse as WebhookListResponse +from .webhook_update_params import WebhookUpdateParams as WebhookUpdateParams from .action_create_response import ActionCreateResponse as ActionCreateResponse from .action_delete_response import ActionDeleteResponse as ActionDeleteResponse from .action_update_response import ActionUpdateResponse as ActionUpdateResponse @@ -25,11 +28,16 @@ from .wordlist_update_params import WordlistUpdateParams as WordlistUpdateParams from .content_submit_response import ContentSubmitResponse as ContentSubmitResponse from .queue_retrieve_response import QueueRetrieveResponse as QueueRetrieveResponse +from .webhook_create_response import WebhookCreateResponse as WebhookCreateResponse +from .webhook_delete_response import WebhookDeleteResponse as WebhookDeleteResponse +from .webhook_update_response import WebhookUpdateResponse as WebhookUpdateResponse from .action_retrieve_response import ActionRetrieveResponse as ActionRetrieveResponse from .author_retrieve_response import AuthorRetrieveResponse as AuthorRetrieveResponse from .queue_get_stats_response import QueueGetStatsResponse as QueueGetStatsResponse from .wordlist_update_response import WordlistUpdateResponse as WordlistUpdateResponse +from .webhook_retrieve_response import WebhookRetrieveResponse as WebhookRetrieveResponse from .wordlist_retrieve_response import WordlistRetrieveResponse as WordlistRetrieveResponse +from .webhook_secret_retrieve_response import WebhookSecretRetrieveResponse as WebhookSecretRetrieveResponse from .wordlist_get_embedding_status_response import ( WordlistGetEmbeddingStatusResponse as WordlistGetEmbeddingStatusResponse, ) diff --git a/src/moderation_api/types/webhook_create_params.py b/src/moderation_api/types/webhook_create_params.py new file mode 100644 index 0000000..d1b1753 --- /dev/null +++ b/src/moderation_api/types/webhook_create_params.py @@ -0,0 +1,46 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional +from typing_extensions import Literal, Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["WebhookCreateParams"] + + +class WebhookCreateParams(TypedDict, total=False): + event_types: Required[ + Annotated[ + List[ + Literal[ + "QUEUE_ITEM_NEW", + "QUEUE_ITEM_COMPLETED", + "QUEUE_ITEM_ACTION", + "QUEUE_ITEM_REJECTED", + "QUEUE_ITEM_ALLOWED", + "AUTHOR_BLOCKED", + "AUTHOR_UNBLOCKED", + "AUTHOR_SUSPENDED", + "AUTHOR_UPDATED", + "AUTHOR_TRUST_LEVEL_CHANGED", + "AUTHOR_ACTION", + ] + ], + PropertyInfo(alias="eventTypes"), + ] + ] + """Event types this webhook subscribes to. + + One webhook URL receives all events you list here. + """ + + name: Required[str] + """The webhook's name, used to identify it in the dashboard""" + + url: Required[str] + """The webhook's URL. We'll call this URL when an event occurs.""" + + description: Optional[str] + """The webhook's description""" diff --git a/src/moderation_api/types/webhook_create_response.py b/src/moderation_api/types/webhook_create_response.py new file mode 100644 index 0000000..48c01b8 --- /dev/null +++ b/src/moderation_api/types/webhook_create_response.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["WebhookCreateResponse"] + + +class WebhookCreateResponse(BaseModel): + id: str + """The ID of the webhook.""" + + created_at: str = FieldInfo(alias="createdAt") + """The date the webhook was created.""" + + event_types: List[ + Literal[ + "QUEUE_ITEM_NEW", + "QUEUE_ITEM_COMPLETED", + "QUEUE_ITEM_ACTION", + "QUEUE_ITEM_REJECTED", + "QUEUE_ITEM_ALLOWED", + "AUTHOR_BLOCKED", + "AUTHOR_UNBLOCKED", + "AUTHOR_SUSPENDED", + "AUTHOR_UPDATED", + "AUTHOR_TRUST_LEVEL_CHANGED", + "AUTHOR_ACTION", + ] + ] = FieldInfo(alias="eventTypes") + """Event types this webhook subscribes to. + + Empty for legacy v1 webhooks, which subscribe via their single deprecated `type` + instead. + """ + + name: str + """The webhook's name.""" + + payload_version: Literal["V1", "V2"] = FieldInfo(alias="payloadVersion") + """Payload envelope version. + + V2 is the Stripe-style envelope; V1 is the legacy flat shape and is read-only + via this API. + """ + + url: str + """The URL we call when a subscribed event occurs.""" + + description: Optional[str] = None + """The webhook's description.""" diff --git a/src/moderation_api/types/webhook_delete_response.py b/src/moderation_api/types/webhook_delete_response.py new file mode 100644 index 0000000..b66c864 --- /dev/null +++ b/src/moderation_api/types/webhook_delete_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .._models import BaseModel + +__all__ = ["WebhookDeleteResponse"] + + +class WebhookDeleteResponse(BaseModel): + id: str + """The ID of the webhook.""" + + deleted: bool + """Whether the webhook was deleted.""" diff --git a/src/moderation_api/types/webhook_list_response.py b/src/moderation_api/types/webhook_list_response.py new file mode 100644 index 0000000..d35726f --- /dev/null +++ b/src/moderation_api/types/webhook_list_response.py @@ -0,0 +1,58 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal, TypeAlias + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["WebhookListResponse", "WebhookListResponseItem"] + + +class WebhookListResponseItem(BaseModel): + id: str + """The ID of the webhook.""" + + created_at: str = FieldInfo(alias="createdAt") + """The date the webhook was created.""" + + event_types: List[ + Literal[ + "QUEUE_ITEM_NEW", + "QUEUE_ITEM_COMPLETED", + "QUEUE_ITEM_ACTION", + "QUEUE_ITEM_REJECTED", + "QUEUE_ITEM_ALLOWED", + "AUTHOR_BLOCKED", + "AUTHOR_UNBLOCKED", + "AUTHOR_SUSPENDED", + "AUTHOR_UPDATED", + "AUTHOR_TRUST_LEVEL_CHANGED", + "AUTHOR_ACTION", + ] + ] = FieldInfo(alias="eventTypes") + """Event types this webhook subscribes to. + + Empty for legacy v1 webhooks, which subscribe via their single deprecated `type` + instead. + """ + + name: str + """The webhook's name.""" + + payload_version: Literal["V1", "V2"] = FieldInfo(alias="payloadVersion") + """Payload envelope version. + + V2 is the Stripe-style envelope; V1 is the legacy flat shape and is read-only + via this API. + """ + + url: str + """The URL we call when a subscribed event occurs.""" + + description: Optional[str] = None + """The webhook's description.""" + + +WebhookListResponse: TypeAlias = List[WebhookListResponseItem] diff --git a/src/moderation_api/types/webhook_retrieve_response.py b/src/moderation_api/types/webhook_retrieve_response.py new file mode 100644 index 0000000..efde9e3 --- /dev/null +++ b/src/moderation_api/types/webhook_retrieve_response.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["WebhookRetrieveResponse"] + + +class WebhookRetrieveResponse(BaseModel): + id: str + """The ID of the webhook.""" + + created_at: str = FieldInfo(alias="createdAt") + """The date the webhook was created.""" + + event_types: List[ + Literal[ + "QUEUE_ITEM_NEW", + "QUEUE_ITEM_COMPLETED", + "QUEUE_ITEM_ACTION", + "QUEUE_ITEM_REJECTED", + "QUEUE_ITEM_ALLOWED", + "AUTHOR_BLOCKED", + "AUTHOR_UNBLOCKED", + "AUTHOR_SUSPENDED", + "AUTHOR_UPDATED", + "AUTHOR_TRUST_LEVEL_CHANGED", + "AUTHOR_ACTION", + ] + ] = FieldInfo(alias="eventTypes") + """Event types this webhook subscribes to. + + Empty for legacy v1 webhooks, which subscribe via their single deprecated `type` + instead. + """ + + name: str + """The webhook's name.""" + + payload_version: Literal["V1", "V2"] = FieldInfo(alias="payloadVersion") + """Payload envelope version. + + V2 is the Stripe-style envelope; V1 is the legacy flat shape and is read-only + via this API. + """ + + url: str + """The URL we call when a subscribed event occurs.""" + + description: Optional[str] = None + """The webhook's description.""" diff --git a/src/moderation_api/types/webhook_secret_retrieve_response.py b/src/moderation_api/types/webhook_secret_retrieve_response.py new file mode 100644 index 0000000..1b247eb --- /dev/null +++ b/src/moderation_api/types/webhook_secret_retrieve_response.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .._models import BaseModel + +__all__ = ["WebhookSecretRetrieveResponse"] + + +class WebhookSecretRetrieveResponse(BaseModel): + secret: str + """The signing secret for this project. + + Every webhook delivery is signed with HMAC-SHA256 over the raw JSON body, + hex-encoded in the `modapi-signature` header. + """ diff --git a/src/moderation_api/types/webhook_update_params.py b/src/moderation_api/types/webhook_update_params.py new file mode 100644 index 0000000..ae25311 --- /dev/null +++ b/src/moderation_api/types/webhook_update_params.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Optional +from typing_extensions import Literal, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["WebhookUpdateParams"] + + +class WebhookUpdateParams(TypedDict, total=False): + description: Optional[str] + """The webhook's description""" + + event_types: Annotated[ + List[ + Literal[ + "QUEUE_ITEM_NEW", + "QUEUE_ITEM_COMPLETED", + "QUEUE_ITEM_ACTION", + "QUEUE_ITEM_REJECTED", + "QUEUE_ITEM_ALLOWED", + "AUTHOR_BLOCKED", + "AUTHOR_UNBLOCKED", + "AUTHOR_SUSPENDED", + "AUTHOR_UPDATED", + "AUTHOR_TRUST_LEVEL_CHANGED", + "AUTHOR_ACTION", + ] + ], + PropertyInfo(alias="eventTypes"), + ] + """Event types this webhook subscribes to. + + One webhook URL receives all events you list here. + """ + + name: str + """The webhook's name, used to identify it in the dashboard""" + + url: str + """The webhook's URL. We'll call this URL when an event occurs.""" diff --git a/src/moderation_api/types/webhook_update_response.py b/src/moderation_api/types/webhook_update_response.py new file mode 100644 index 0000000..34604df --- /dev/null +++ b/src/moderation_api/types/webhook_update_response.py @@ -0,0 +1,55 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["WebhookUpdateResponse"] + + +class WebhookUpdateResponse(BaseModel): + id: str + """The ID of the webhook.""" + + created_at: str = FieldInfo(alias="createdAt") + """The date the webhook was created.""" + + event_types: List[ + Literal[ + "QUEUE_ITEM_NEW", + "QUEUE_ITEM_COMPLETED", + "QUEUE_ITEM_ACTION", + "QUEUE_ITEM_REJECTED", + "QUEUE_ITEM_ALLOWED", + "AUTHOR_BLOCKED", + "AUTHOR_UNBLOCKED", + "AUTHOR_SUSPENDED", + "AUTHOR_UPDATED", + "AUTHOR_TRUST_LEVEL_CHANGED", + "AUTHOR_ACTION", + ] + ] = FieldInfo(alias="eventTypes") + """Event types this webhook subscribes to. + + Empty for legacy v1 webhooks, which subscribe via their single deprecated `type` + instead. + """ + + name: str + """The webhook's name.""" + + payload_version: Literal["V1", "V2"] = FieldInfo(alias="payloadVersion") + """Payload envelope version. + + V2 is the Stripe-style envelope; V1 is the legacy flat shape and is read-only + via this API. + """ + + url: str + """The URL we call when a subscribed event occurs.""" + + description: Optional[str] = None + """The webhook's description.""" diff --git a/tests/api_resources/test_content.py b/tests/api_resources/test_content.py index 9f5a864..3693af5 100644 --- a/tests/api_resources/test_content.py +++ b/tests/api_resources/test_content.py @@ -17,6 +17,40 @@ class TestContent: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_stream(self, client: ModerationAPI) -> None: + content = client.content.stream( + sec_web_socket_protocol="moderationapi.v1", + ) + assert content is None + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_stream(self, client: ModerationAPI) -> None: + response = client.content.with_raw_response.stream( + sec_web_socket_protocol="moderationapi.v1", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + content = response.parse() + assert content is None + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_stream(self, client: ModerationAPI) -> None: + with client.content.with_streaming_response.stream( + sec_web_socket_protocol="moderationapi.v1", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + content = response.parse() + assert content is None + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize def test_method_submit(self, client: ModerationAPI) -> None: @@ -98,6 +132,40 @@ class TestAsyncContent: "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_stream(self, async_client: AsyncModerationAPI) -> None: + content = await async_client.content.stream( + sec_web_socket_protocol="moderationapi.v1", + ) + assert content is None + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_stream(self, async_client: AsyncModerationAPI) -> None: + response = await async_client.content.with_raw_response.stream( + sec_web_socket_protocol="moderationapi.v1", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + content = await response.parse() + assert content is None + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_stream(self, async_client: AsyncModerationAPI) -> None: + async with async_client.content.with_streaming_response.stream( + sec_web_socket_protocol="moderationapi.v1", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + content = await response.parse() + assert content is None + + assert cast(Any, response.is_closed) is True + @pytest.mark.skip(reason="Mock server tests are disabled") @parametrize async def test_method_submit(self, async_client: AsyncModerationAPI) -> None: diff --git a/tests/api_resources/test_webhook_secret.py b/tests/api_resources/test_webhook_secret.py new file mode 100644 index 0000000..0d8f0fc --- /dev/null +++ b/tests/api_resources/test_webhook_secret.py @@ -0,0 +1,80 @@ +# 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 tests.utils import assert_matches_type +from moderation_api import ModerationAPI, AsyncModerationAPI +from moderation_api.types import WebhookSecretRetrieveResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestWebhookSecret: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_retrieve(self, client: ModerationAPI) -> None: + webhook_secret = client.webhook_secret.retrieve() + assert_matches_type(WebhookSecretRetrieveResponse, webhook_secret, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: ModerationAPI) -> None: + response = client.webhook_secret.with_raw_response.retrieve() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + webhook_secret = response.parse() + assert_matches_type(WebhookSecretRetrieveResponse, webhook_secret, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: ModerationAPI) -> None: + with client.webhook_secret.with_streaming_response.retrieve() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + webhook_secret = response.parse() + assert_matches_type(WebhookSecretRetrieveResponse, webhook_secret, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncWebhookSecret: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncModerationAPI) -> None: + webhook_secret = await async_client.webhook_secret.retrieve() + assert_matches_type(WebhookSecretRetrieveResponse, webhook_secret, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncModerationAPI) -> None: + response = await async_client.webhook_secret.with_raw_response.retrieve() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + webhook_secret = await response.parse() + assert_matches_type(WebhookSecretRetrieveResponse, webhook_secret, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncModerationAPI) -> None: + async with async_client.webhook_secret.with_streaming_response.retrieve() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + webhook_secret = await response.parse() + assert_matches_type(WebhookSecretRetrieveResponse, webhook_secret, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_webhooks.py b/tests/api_resources/test_webhooks.py new file mode 100644 index 0000000..33ea5bd --- /dev/null +++ b/tests/api_resources/test_webhooks.py @@ -0,0 +1,464 @@ +# 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 tests.utils import assert_matches_type +from moderation_api import ModerationAPI, AsyncModerationAPI +from moderation_api.types import ( + WebhookListResponse, + WebhookCreateResponse, + WebhookDeleteResponse, + WebhookUpdateResponse, + WebhookRetrieveResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestWebhooks: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_create(self, client: ModerationAPI) -> None: + webhook = client.webhooks.create( + event_types=["QUEUE_ITEM_NEW"], + name="x", + url="https://example.com", + ) + assert_matches_type(WebhookCreateResponse, webhook, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_create_with_all_params(self, client: ModerationAPI) -> None: + webhook = client.webhooks.create( + event_types=["QUEUE_ITEM_NEW"], + name="x", + url="https://example.com", + description="description", + ) + assert_matches_type(WebhookCreateResponse, webhook, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_create(self, client: ModerationAPI) -> None: + response = client.webhooks.with_raw_response.create( + event_types=["QUEUE_ITEM_NEW"], + name="x", + url="https://example.com", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + webhook = response.parse() + assert_matches_type(WebhookCreateResponse, webhook, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_create(self, client: ModerationAPI) -> None: + with client.webhooks.with_streaming_response.create( + event_types=["QUEUE_ITEM_NEW"], + name="x", + url="https://example.com", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + webhook = response.parse() + assert_matches_type(WebhookCreateResponse, webhook, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_retrieve(self, client: ModerationAPI) -> None: + webhook = client.webhooks.retrieve( + "id", + ) + assert_matches_type(WebhookRetrieveResponse, webhook, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_retrieve(self, client: ModerationAPI) -> None: + response = client.webhooks.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + webhook = response.parse() + assert_matches_type(WebhookRetrieveResponse, webhook, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_retrieve(self, client: ModerationAPI) -> None: + with client.webhooks.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + webhook = response.parse() + assert_matches_type(WebhookRetrieveResponse, webhook, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_path_params_retrieve(self, client: ModerationAPI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.webhooks.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_update(self, client: ModerationAPI) -> None: + webhook = client.webhooks.update( + id="id", + ) + assert_matches_type(WebhookUpdateResponse, webhook, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_update_with_all_params(self, client: ModerationAPI) -> None: + webhook = client.webhooks.update( + id="id", + description="description", + event_types=["QUEUE_ITEM_NEW"], + name="x", + url="https://example.com", + ) + assert_matches_type(WebhookUpdateResponse, webhook, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_update(self, client: ModerationAPI) -> None: + response = client.webhooks.with_raw_response.update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + webhook = response.parse() + assert_matches_type(WebhookUpdateResponse, webhook, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_update(self, client: ModerationAPI) -> None: + with client.webhooks.with_streaming_response.update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + webhook = response.parse() + assert_matches_type(WebhookUpdateResponse, webhook, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_path_params_update(self, client: ModerationAPI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.webhooks.with_raw_response.update( + id="", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_list(self, client: ModerationAPI) -> None: + webhook = client.webhooks.list() + assert_matches_type(WebhookListResponse, webhook, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_list(self, client: ModerationAPI) -> None: + response = client.webhooks.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + webhook = response.parse() + assert_matches_type(WebhookListResponse, webhook, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_list(self, client: ModerationAPI) -> None: + with client.webhooks.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + webhook = response.parse() + assert_matches_type(WebhookListResponse, webhook, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_delete(self, client: ModerationAPI) -> None: + webhook = client.webhooks.delete( + "id", + ) + assert_matches_type(WebhookDeleteResponse, webhook, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_delete(self, client: ModerationAPI) -> None: + response = client.webhooks.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + webhook = response.parse() + assert_matches_type(WebhookDeleteResponse, webhook, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: ModerationAPI) -> None: + with client.webhooks.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + webhook = response.parse() + assert_matches_type(WebhookDeleteResponse, webhook, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_path_params_delete(self, client: ModerationAPI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.webhooks.with_raw_response.delete( + "", + ) + + +class TestAsyncWebhooks: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_create(self, async_client: AsyncModerationAPI) -> None: + webhook = await async_client.webhooks.create( + event_types=["QUEUE_ITEM_NEW"], + name="x", + url="https://example.com", + ) + assert_matches_type(WebhookCreateResponse, webhook, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncModerationAPI) -> None: + webhook = await async_client.webhooks.create( + event_types=["QUEUE_ITEM_NEW"], + name="x", + url="https://example.com", + description="description", + ) + assert_matches_type(WebhookCreateResponse, webhook, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_create(self, async_client: AsyncModerationAPI) -> None: + response = await async_client.webhooks.with_raw_response.create( + event_types=["QUEUE_ITEM_NEW"], + name="x", + url="https://example.com", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + webhook = await response.parse() + assert_matches_type(WebhookCreateResponse, webhook, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_create(self, async_client: AsyncModerationAPI) -> None: + async with async_client.webhooks.with_streaming_response.create( + event_types=["QUEUE_ITEM_NEW"], + name="x", + url="https://example.com", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + webhook = await response.parse() + assert_matches_type(WebhookCreateResponse, webhook, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_retrieve(self, async_client: AsyncModerationAPI) -> None: + webhook = await async_client.webhooks.retrieve( + "id", + ) + assert_matches_type(WebhookRetrieveResponse, webhook, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncModerationAPI) -> None: + response = await async_client.webhooks.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + webhook = await response.parse() + assert_matches_type(WebhookRetrieveResponse, webhook, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncModerationAPI) -> None: + async with async_client.webhooks.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + webhook = await response.parse() + assert_matches_type(WebhookRetrieveResponse, webhook, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncModerationAPI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.webhooks.with_raw_response.retrieve( + "", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_update(self, async_client: AsyncModerationAPI) -> None: + webhook = await async_client.webhooks.update( + id="id", + ) + assert_matches_type(WebhookUpdateResponse, webhook, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncModerationAPI) -> None: + webhook = await async_client.webhooks.update( + id="id", + description="description", + event_types=["QUEUE_ITEM_NEW"], + name="x", + url="https://example.com", + ) + assert_matches_type(WebhookUpdateResponse, webhook, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_update(self, async_client: AsyncModerationAPI) -> None: + response = await async_client.webhooks.with_raw_response.update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + webhook = await response.parse() + assert_matches_type(WebhookUpdateResponse, webhook, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_update(self, async_client: AsyncModerationAPI) -> None: + async with async_client.webhooks.with_streaming_response.update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + webhook = await response.parse() + assert_matches_type(WebhookUpdateResponse, webhook, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_path_params_update(self, async_client: AsyncModerationAPI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.webhooks.with_raw_response.update( + id="", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncModerationAPI) -> None: + webhook = await async_client.webhooks.list() + assert_matches_type(WebhookListResponse, webhook, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncModerationAPI) -> None: + response = await async_client.webhooks.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + webhook = await response.parse() + assert_matches_type(WebhookListResponse, webhook, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncModerationAPI) -> None: + async with async_client.webhooks.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + webhook = await response.parse() + assert_matches_type(WebhookListResponse, webhook, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncModerationAPI) -> None: + webhook = await async_client.webhooks.delete( + "id", + ) + assert_matches_type(WebhookDeleteResponse, webhook, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncModerationAPI) -> None: + response = await async_client.webhooks.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + webhook = await response.parse() + assert_matches_type(WebhookDeleteResponse, webhook, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncModerationAPI) -> None: + async with async_client.webhooks.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + webhook = await response.parse() + assert_matches_type(WebhookDeleteResponse, webhook, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_path_params_delete(self, async_client: AsyncModerationAPI) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.webhooks.with_raw_response.delete( + "", + )