diff --git a/api.md b/api.md index 8956ead..68de1f6 100644 --- a/api.md +++ b/api.md @@ -10,7 +10,7 @@ A list method returns its first page, such as a `AddOnsListPage`: a subclass of model (`AddOnListResponse`) with the paging members `items`, `has_next_page()`, `get_next_page()`, `iter_pages()` and `body`, which iterates every item. The page classes are in `meteroid.api`. -[Add ons](#add-ons) · [Add ons entitlements](#add-ons-entitlements) · [Batch jobs](#batch-jobs) · [Checkout sessions](#checkout-sessions) · [Connect](#connect) · [Coupons](#coupons) · [Credit notes](#credit-notes) · [Custom properties](#custom-properties) · [Customers](#customers) · [Entitlements](#entitlements) · [Events](#events) · [Features](#features) · [Invoices](#invoices) · [Metrics](#metrics) · [Oauth](#oauth) · [Oauth apps](#oauth-apps) · [Plans](#plans) · [Plans versions](#plans-versions) · [Product families](#product-families) · [Products](#products) · [Products entitlements](#products-entitlements) · [Subscriptions](#subscriptions) · [Usage](#usage) +[Add ons](#add-ons) · [Add ons entitlements](#add-ons-entitlements) · [Batch jobs](#batch-jobs) · [Checkout sessions](#checkout-sessions) · [Connect](#connect) · [Coupons](#coupons) · [Credit notes](#credit-notes) · [Custom properties](#custom-properties) · [Customers](#customers) · [Entitlements](#entitlements) · [Events](#events) · [Features](#features) · [Invoices](#invoices) · [Metrics](#metrics) · [Oauth](#oauth) · [Oauth apps](#oauth-apps) · [Plans](#plans) · [Plans versions](#plans-versions) · [Product families](#product-families) · [Products](#products) · [Products entitlements](#products-entitlements) · [Subscriptions](#subscriptions) · [Usage](#usage) · [Webhook endpoints](#webhook-endpoints) · [Webhook endpoints endpoints](#webhook-endpoints-endpoints) ## Add ons @@ -113,7 +113,7 @@ model (`AddOnListResponse`) with the paging members `items`, `has_next_page()`, | Method | Request | Returns | | --- | --- | --- | | `client.customers.list(*, order_by=..., page=..., per_page=..., search=..., archived=...) -> CustomersListPage` | `GET /api/v1/customers` | [`CustomerListResponse`](meteroid/models/customer_list_response.py) pages of [`Customer`](meteroid/models/customer.py) | -| `client.customers.create(*, currency: Currency, custom_taxes: list[CustomTaxRate], invoicing_emails: list[str], alias=..., billing_address=..., billing_email=..., buyer_reference=..., connected_account_id=..., custom_properties=..., customer_type=..., exemption_reason=..., first_name=..., invoicing_entity_id=..., invoicing_language=..., is_tax_exempt=..., last_name=..., legal_number=..., name=..., phone=..., preferred_locales=..., shipping_address=..., vat_number=...) -> Customer` | `POST /api/v1/customers` | [`Customer`](meteroid/models/customer.py) | +| `client.customers.create(*, currency: Currency, alias=..., billing_address=..., billing_email=..., buyer_reference=..., connected_account_id=..., custom_properties=..., custom_taxes=..., customer_type=..., exemption_reason=..., first_name=..., invoicing_emails=..., invoicing_entity_id=..., invoicing_language=..., is_tax_exempt=..., last_name=..., legal_number=..., name=..., phone=..., preferred_locales=..., shipping_address=..., vat_number=...) -> Customer` | `POST /api/v1/customers` | [`Customer`](meteroid/models/customer.py) | | `client.customers.retrieve(id_or_alias: str) -> Customer` | `GET /api/v1/customers/{id_or_alias}` | [`Customer`](meteroid/models/customer.py) | | `client.customers.replace(id_or_alias: str, *, currency: Currency, custom_taxes: list[CustomTaxRate], invoicing_emails: list[str], invoicing_entity_id: InvoicingEntityId, alias=..., billing_address=..., billing_email=..., buyer_reference=..., custom_properties=..., customer_type=..., exemption_reason=..., first_name=..., invoicing_language=..., is_tax_exempt=..., last_name=..., legal_number=..., name=..., phone=..., preferred_locales=..., shipping_address=..., vat_number=...) -> Customer` | `PUT /api/v1/customers/{id_or_alias}` | [`Customer`](meteroid/models/customer.py) | | `client.customers.archive(id_or_alias: str) -> None` | `DELETE /api/v1/customers/{id_or_alias}` | nothing | @@ -283,3 +283,26 @@ model (`AddOnListResponse`) with the paging members `items`, `has_next_page()`, | `client.usage.retrieve_customer(customer_id: str, *, start_date: date, end_date: date, metric_id=...) -> UsageResponse` | `GET /api/v1/usage/customer/{customer_id}` | [`UsageResponse`](meteroid/models/usage_response.py) | | `client.usage.retrieve_subscription(subscription_id: str, *, start_date=..., end_date=..., metric_id=...) -> UsageResponse` | `GET /api/v1/usage/subscription/{subscription_id}` | [`UsageResponse`](meteroid/models/usage_response.py) | | `client.usage.retrieve_summary(*, start_date: date, end_date: date, metric_id=...) -> UsageResponse` | `GET /api/v1/usage/summary` | [`UsageResponse`](meteroid/models/usage_response.py) | + +## Webhook endpoints + +[`client.webhook_endpoints`](meteroid/api/webhook_endpoints.py) + +| Method | Request | Returns | +| --- | --- | --- | +| `client.webhook_endpoints.resend_webhook_delivery(delivery_id: str) -> WebhookDelivery` | `POST /api/v1/webhooks/deliveries/{delivery_id}/resend` | [`WebhookDelivery`](meteroid/models/webhook_delivery.py) | + +### Webhook endpoints endpoints + +[`client.webhook_endpoints.endpoints`](meteroid/api/webhook_endpoints_endpoints.py) + +| Method | Request | Returns | +| --- | --- | --- | +| `client.webhook_endpoints.endpoints.list() -> WebhookEndpointListResponse` | `GET /api/v1/webhooks/endpoints` | [`WebhookEndpointListResponse`](meteroid/models/webhook_endpoint_list_response.py) | +| `client.webhook_endpoints.endpoints.create(*, url: str, description=..., event_types=..., headers=..., rate_limit_per_sec=...) -> CreatedWebhookEndpoint` | `POST /api/v1/webhooks/endpoints` | [`CreatedWebhookEndpoint`](meteroid/models/created_webhook_endpoint.py) | +| `client.webhook_endpoints.endpoints.retrieve(endpoint_id: str) -> WebhookEndpoint` | `GET /api/v1/webhooks/endpoints/{endpoint_id}` | [`WebhookEndpoint`](meteroid/models/webhook_endpoint.py) | +| `client.webhook_endpoints.endpoints.delete(endpoint_id: str) -> None` | `DELETE /api/v1/webhooks/endpoints/{endpoint_id}` | nothing | +| `client.webhook_endpoints.endpoints.update(endpoint_id: str, *, description=..., disabled=..., event_types=..., headers=..., rate_limit_per_sec=..., url=...) -> WebhookEndpoint` | `PATCH /api/v1/webhooks/endpoints/{endpoint_id}` | [`WebhookEndpoint`](meteroid/models/webhook_endpoint.py) | +| `client.webhook_endpoints.endpoints.list_deliveries(endpoint_id: str, *, status=..., page=..., per_page=...) -> WebhookEndpointsEndpointsListDeliveriesPage` | `GET /api/v1/webhooks/endpoints/{endpoint_id}/deliveries` | [`WebhookDeliveryListResponse`](meteroid/models/webhook_delivery_list_response.py) pages of [`WebhookDelivery`](meteroid/models/webhook_delivery.py) | +| `client.webhook_endpoints.endpoints.rotate_secret(endpoint_id: str) -> WebhookEndpointSecret` | `POST /api/v1/webhooks/endpoints/{endpoint_id}/rotate-secret` | [`WebhookEndpointSecret`](meteroid/models/webhook_endpoint_secret.py) | +| `client.webhook_endpoints.endpoints.retrieve_secret(endpoint_id: str) -> WebhookEndpointSecret` | `GET /api/v1/webhooks/endpoints/{endpoint_id}/secret` | [`WebhookEndpointSecret`](meteroid/models/webhook_endpoint_secret.py) | diff --git a/meteroid/api/__init__.py b/meteroid/api/__init__.py index 780ab2c..93a4158 100644 --- a/meteroid/api/__init__.py +++ b/meteroid/api/__init__.py @@ -55,6 +55,7 @@ AsyncProductFamiliesListPage, AsyncProductsListPage, AsyncSubscriptionsListPage, + AsyncWebhookEndpointsEndpointsListDeliveriesPage, BatchJobsListFailuresPage, BatchJobsListPage, CouponsListPage, @@ -69,6 +70,7 @@ ProductFamiliesListPage, ProductsListPage, SubscriptionsListPage, + WebhookEndpointsEndpointsListDeliveriesPage, ) from .add_ons import AddOns, AsyncAddOns from .add_ons_entitlements import AddOnsEntitlements, AsyncAddOnsEntitlements @@ -93,6 +95,11 @@ from .products_entitlements import AsyncProductsEntitlements, ProductsEntitlements from .subscriptions import AsyncSubscriptions, Subscriptions from .usage import AsyncUsage, Usage + from .webhook_endpoints import AsyncWebhookEndpoints, WebhookEndpoints + from .webhook_endpoints_endpoints import ( + AsyncWebhookEndpointsEndpoints, + WebhookEndpointsEndpoints, + ) # Each resource's module is imported the first time it is used. _MODULES: dict[str, str] = { @@ -142,6 +149,10 @@ "Subscriptions": "subscriptions", "AsyncUsage": "usage", "Usage": "usage", + "AsyncWebhookEndpoints": "webhook_endpoints", + "WebhookEndpoints": "webhook_endpoints", + "AsyncWebhookEndpointsEndpoints": "webhook_endpoints_endpoints", + "WebhookEndpointsEndpoints": "webhook_endpoints_endpoints", "AsyncAddOnsListPage": "_pages", "AddOnsListPage": "_pages", "AsyncBatchJobsListPage": "_pages", @@ -172,6 +183,8 @@ "ProductsListPage": "_pages", "AsyncSubscriptionsListPage": "_pages", "SubscriptionsListPage": "_pages", + "AsyncWebhookEndpointsEndpointsListDeliveriesPage": "_pages", + "WebhookEndpointsEndpointsListDeliveriesPage": "_pages", } @@ -262,6 +275,10 @@ def __dir__() -> list[str]: "Subscriptions", "AsyncUsage", "Usage", + "AsyncWebhookEndpoints", + "WebhookEndpoints", + "AsyncWebhookEndpointsEndpoints", + "WebhookEndpointsEndpoints", "AsyncAddOnsListPage", "AddOnsListPage", "AsyncBatchJobsListPage", @@ -292,4 +309,6 @@ def __dir__() -> list[str]: "ProductsListPage", "AsyncSubscriptionsListPage", "SubscriptionsListPage", + "AsyncWebhookEndpointsEndpointsListDeliveriesPage", + "WebhookEndpointsEndpointsListDeliveriesPage", ] diff --git a/meteroid/api/_pages.py b/meteroid/api/_pages.py index d1026d2..c9dfc69 100644 --- a/meteroid/api/_pages.py +++ b/meteroid/api/_pages.py @@ -34,6 +34,8 @@ ProductListResponse, Subscription, SubscriptionListResponse, + WebhookDelivery, + WebhookDeliveryListResponse, ) from ._pagination import AsyncPage, SyncPage @@ -68,6 +70,8 @@ "ProductsListPage", "AsyncSubscriptionsListPage", "SubscriptionsListPage", + "AsyncWebhookEndpointsEndpointsListDeliveriesPage", + "WebhookEndpointsEndpointsListDeliveriesPage", ] @@ -231,3 +235,15 @@ class AsyncSubscriptionsListPage( AsyncPage[Subscription, SubscriptionListResponse], SubscriptionListResponse ): """A page of :class:`Subscription`, and the :class:`SubscriptionListResponse` response of its request.""" + + +class WebhookEndpointsEndpointsListDeliveriesPage( + SyncPage[WebhookDelivery, WebhookDeliveryListResponse], WebhookDeliveryListResponse +): + """A page of :class:`WebhookDelivery`, and the :class:`WebhookDeliveryListResponse` response of its request.""" + + +class AsyncWebhookEndpointsEndpointsListDeliveriesPage( + AsyncPage[WebhookDelivery, WebhookDeliveryListResponse], WebhookDeliveryListResponse +): + """A page of :class:`WebhookDelivery`, and the :class:`WebhookDeliveryListResponse` response of its request.""" diff --git a/meteroid/api/client.py b/meteroid/api/client.py index e4a8b7f..8ebe456 100644 --- a/meteroid/api/client.py +++ b/meteroid/api/client.py @@ -137,6 +137,12 @@ Usage, UsageWithRawResponse, ) + from .webhook_endpoints import ( + AsyncWebhookEndpoints, + AsyncWebhookEndpointsWithRawResponse, + WebhookEndpoints, + WebhookEndpointsWithRawResponse, + ) __all__ = [ "AsyncMeteroid", @@ -424,6 +430,13 @@ def usage(self) -> Usage: return Usage(self._cfg, self._httpx_client) + @functools.cached_property + def webhook_endpoints(self) -> WebhookEndpoints: + """The webhook endpoints API.""" + from .webhook_endpoints import WebhookEndpoints + + return WebhookEndpoints(self._cfg, self._httpx_client) + class MeteroidWithRawResponse: """The resources of :class:`Meteroid`, whose methods return an :class:`APIResponse`.""" @@ -571,6 +584,13 @@ def usage(self) -> UsageWithRawResponse: return UsageWithRawResponse(self._client.usage) + @property + def webhook_endpoints(self) -> WebhookEndpointsWithRawResponse: + """The webhook endpoints API.""" + from .webhook_endpoints import WebhookEndpointsWithRawResponse + + return WebhookEndpointsWithRawResponse(self._client.webhook_endpoints) + class AsyncMeteroid: """Asyncio Meteroid API client. @@ -818,6 +838,13 @@ def usage(self) -> AsyncUsage: return AsyncUsage(self._cfg, self._httpx_client) + @functools.cached_property + def webhook_endpoints(self) -> AsyncWebhookEndpoints: + """The webhook endpoints API.""" + from .webhook_endpoints import AsyncWebhookEndpoints + + return AsyncWebhookEndpoints(self._cfg, self._httpx_client) + class AsyncMeteroidWithRawResponse: """The resources of :class:`AsyncMeteroid`, whose methods return an :class:`APIResponse`.""" @@ -964,3 +991,10 @@ def usage(self) -> AsyncUsageWithRawResponse: from .usage import AsyncUsageWithRawResponse return AsyncUsageWithRawResponse(self._client.usage) + + @property + def webhook_endpoints(self) -> AsyncWebhookEndpointsWithRawResponse: + """The webhook endpoints API.""" + from .webhook_endpoints import AsyncWebhookEndpointsWithRawResponse + + return AsyncWebhookEndpointsWithRawResponse(self._client.webhook_endpoints) diff --git a/meteroid/api/customers.py b/meteroid/api/customers.py index 32efa80..734218e 100644 --- a/meteroid/api/customers.py +++ b/meteroid/api/customers.py @@ -116,17 +116,17 @@ async def create( self, *, currency: Currency | CurrencyLiteral, - custom_taxes: builtins.list[CustomTaxRate], - invoicing_emails: builtins.list[str], alias: str | None | Unset = UNSET, billing_address: Address | None | Unset = UNSET, billing_email: str | None | Unset = UNSET, buyer_reference: str | None | Unset = UNSET, connected_account_id: str | None | Unset = UNSET, custom_properties: t.Any = None, + custom_taxes: builtins.list[CustomTaxRate] | None = None, customer_type: CustomerType | CustomerTypeLiteral | None = None, exemption_reason: str | None | Unset = UNSET, first_name: str | None | Unset = UNSET, + invoicing_emails: builtins.list[str] | None = None, invoicing_entity_id: InvoicingEntityId | None | Unset = UNSET, invoicing_language: str | None | Unset = UNSET, is_tax_exempt: bool | None | Unset = UNSET, @@ -626,17 +626,17 @@ def create( self, *, currency: Currency | CurrencyLiteral, - custom_taxes: builtins.list[CustomTaxRate], - invoicing_emails: builtins.list[str], alias: str | None | Unset = UNSET, billing_address: Address | None | Unset = UNSET, billing_email: str | None | Unset = UNSET, buyer_reference: str | None | Unset = UNSET, connected_account_id: str | None | Unset = UNSET, custom_properties: t.Any = None, + custom_taxes: builtins.list[CustomTaxRate] | None = None, customer_type: CustomerType | CustomerTypeLiteral | None = None, exemption_reason: str | None | Unset = UNSET, first_name: str | None | Unset = UNSET, + invoicing_emails: builtins.list[str] | None = None, invoicing_entity_id: InvoicingEntityId | None | Unset = UNSET, invoicing_language: str | None | Unset = UNSET, is_tax_exempt: bool | None | Unset = UNSET, diff --git a/meteroid/api/webhook_endpoints.py b/meteroid/api/webhook_endpoints.py new file mode 100644 index 0000000..86cb160 --- /dev/null +++ b/meteroid/api/webhook_endpoints.py @@ -0,0 +1,149 @@ +# this file is @generated +"""Webhook endpoints API.""" + +from __future__ import annotations + +import functools +import typing as t + +from .. import models as _models +from ..models import ( + WebhookDelivery, +) +from ..serialization import UNSET, Unset +from ._response import async_to_raw_response_wrapper, to_raw_response_wrapper +from .common import ( + ApiBaseAsync, + ApiBaseSync, + ApiRequest, + Timeout, + decode_response, +) +from .webhook_endpoints_endpoints import ( + AsyncWebhookEndpointsEndpoints, + AsyncWebhookEndpointsEndpointsWithRawResponse, + WebhookEndpointsEndpoints, + WebhookEndpointsEndpointsWithRawResponse, +) + + +class AsyncWebhookEndpoints(ApiBaseAsync): + """Webhook endpoints API, for asyncio.""" + + @property + def with_raw_response(self) -> AsyncWebhookEndpointsWithRawResponse: + """These methods, returning an :class:`APIResponse` with the status and headers.""" + return AsyncWebhookEndpointsWithRawResponse(self) + + @functools.cached_property + def endpoints(self) -> AsyncWebhookEndpointsEndpoints: + """The endpoints API.""" + return AsyncWebhookEndpointsEndpoints(self._cfg, self._httpx_client) + + async def resend_webhook_delivery( + self, + delivery_id: str, + *, + extra_headers: t.Mapping[str, str] | None = None, + extra_query: t.Mapping[str, object] | None = None, + extra_body: t.Mapping[str, object] | None = None, + timeout: Timeout | Unset = UNSET, + max_retries: int | None = None, + ) -> WebhookDelivery: + """Resend a webhook delivery + + Re-queues the same event for the same endpoint. Fails if the endpoint is disabled.""" + response = await self._request( + ApiRequest( + method="post", + path="/api/v1/webhooks/deliveries/{delivery_id}/resend", + path_params={ + "delivery_id": delivery_id, + }, + error_types={ + "default": _models.RestErrorResponse, + }, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + max_retries=max_retries, + ) + ) + return decode_response(response, WebhookDelivery) + + +class AsyncWebhookEndpointsWithRawResponse: + """The methods of :class:`AsyncWebhookEndpoints`, returning an :class:`APIResponse`.""" + + def __init__(self, resource: AsyncWebhookEndpoints) -> None: + self._resource = resource + self.resend_webhook_delivery = async_to_raw_response_wrapper( + resource.resend_webhook_delivery + ) + + @property + def endpoints(self) -> AsyncWebhookEndpointsEndpointsWithRawResponse: + """The endpoints API.""" + return AsyncWebhookEndpointsEndpointsWithRawResponse(self._resource.endpoints) + + +class WebhookEndpoints(ApiBaseSync): + """Webhook endpoints API.""" + + @property + def with_raw_response(self) -> WebhookEndpointsWithRawResponse: + """These methods, returning an :class:`APIResponse` with the status and headers.""" + return WebhookEndpointsWithRawResponse(self) + + @functools.cached_property + def endpoints(self) -> WebhookEndpointsEndpoints: + """The endpoints API.""" + return WebhookEndpointsEndpoints(self._cfg, self._httpx_client) + + def resend_webhook_delivery( + self, + delivery_id: str, + *, + extra_headers: t.Mapping[str, str] | None = None, + extra_query: t.Mapping[str, object] | None = None, + extra_body: t.Mapping[str, object] | None = None, + timeout: Timeout | Unset = UNSET, + max_retries: int | None = None, + ) -> WebhookDelivery: + """Resend a webhook delivery + + Re-queues the same event for the same endpoint. Fails if the endpoint is disabled.""" + response = self._request( + ApiRequest( + method="post", + path="/api/v1/webhooks/deliveries/{delivery_id}/resend", + path_params={ + "delivery_id": delivery_id, + }, + error_types={ + "default": _models.RestErrorResponse, + }, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + max_retries=max_retries, + ) + ) + return decode_response(response, WebhookDelivery) + + +class WebhookEndpointsWithRawResponse: + """The methods of :class:`WebhookEndpoints`, returning an :class:`APIResponse`.""" + + def __init__(self, resource: WebhookEndpoints) -> None: + self._resource = resource + self.resend_webhook_delivery = to_raw_response_wrapper( + resource.resend_webhook_delivery + ) + + @property + def endpoints(self) -> WebhookEndpointsEndpointsWithRawResponse: + """The endpoints API.""" + return WebhookEndpointsEndpointsWithRawResponse(self._resource.endpoints) diff --git a/meteroid/api/webhook_endpoints_endpoints.py b/meteroid/api/webhook_endpoints_endpoints.py new file mode 100644 index 0000000..1a27016 --- /dev/null +++ b/meteroid/api/webhook_endpoints_endpoints.py @@ -0,0 +1,710 @@ +# this file is @generated +"""Webhook endpoints endpoints API.""" + +from __future__ import annotations + +import builtins +import typing as t + +from .. import models as _models +from ..models import ( + CreatedWebhookEndpoint, + CreateWebhookEndpointRequest, + UpdateWebhookEndpointRequest, + WebhookDelivery, + WebhookDeliveryListResponse, + WebhookDeliveryStatus, + WebhookEndpoint, + WebhookEndpointListResponse, + WebhookEndpointSecret, + WebhookHeaderInput, +) +from ..serialization import UNSET, Unset, to_json_value +from ._pages import ( + AsyncWebhookEndpointsEndpointsListDeliveriesPage, + WebhookEndpointsEndpointsListDeliveriesPage, +) +from ._pagination import ( + AsyncPaginator, + PagePaging, + step, +) +from ._response import async_to_raw_response_wrapper, to_raw_response_wrapper +from .common import ( + ApiBaseAsync, + ApiBaseSync, + ApiRequest, + Timeout, + decode_response, + serialize_query_params, +) + + +class AsyncWebhookEndpointsEndpoints(ApiBaseAsync): + """Webhook endpoints endpoints API, for asyncio.""" + + @property + def with_raw_response(self) -> AsyncWebhookEndpointsEndpointsWithRawResponse: + """These methods, returning an :class:`APIResponse` with the status and headers.""" + return AsyncWebhookEndpointsEndpointsWithRawResponse(self) + + async def list( + self, + *, + extra_headers: t.Mapping[str, str] | None = None, + extra_query: t.Mapping[str, object] | None = None, + extra_body: t.Mapping[str, object] | None = None, + timeout: Timeout | Unset = UNSET, + max_retries: int | None = None, + ) -> WebhookEndpointListResponse: + """List webhook endpoints""" + response = await self._request( + ApiRequest( + method="get", + path="/api/v1/webhooks/endpoints", + error_types={ + "default": _models.RestErrorResponse, + }, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + max_retries=max_retries, + ) + ) + return decode_response(response, WebhookEndpointListResponse) + + async def create( + self, + *, + url: str, + description: str | None | Unset = UNSET, + event_types: builtins.list[str] | None = None, + headers: builtins.list[WebhookHeaderInput] | None = None, + rate_limit_per_sec: int | None | Unset = UNSET, + extra_headers: t.Mapping[str, str] | None = None, + extra_query: t.Mapping[str, object] | None = None, + extra_body: t.Mapping[str, object] | None = None, + timeout: Timeout | Unset = UNSET, + max_retries: int | None = None, + ) -> CreatedWebhookEndpoint: + """Create a webhook endpoint + + The signing secret is returned once, in this response only. + + :param event_types: Event types to subscribe to. Omit or leave empty to receive every event type. + :param headers: Custom headers sent with every delivery. + :param rate_limit_per_sec: Deliveries started per second, at most (1 to 1000). + :param url: HTTPS destination. Private and loopback addresses are rejected unless the instance is configured to allow them.""" + response = await self._request( + ApiRequest( + method="post", + path="/api/v1/webhooks/endpoints", + json_body=to_json_value( + CreateWebhookEndpointRequest( + description=description, + event_types=event_types, + headers=headers, + rate_limit_per_sec=rate_limit_per_sec, + url=url, + ), + CreateWebhookEndpointRequest, + ), + error_types={ + "default": _models.RestErrorResponse, + }, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + max_retries=max_retries, + ) + ) + return decode_response(response, CreatedWebhookEndpoint) + + async def retrieve( + self, + endpoint_id: str, + *, + extra_headers: t.Mapping[str, str] | None = None, + extra_query: t.Mapping[str, object] | None = None, + extra_body: t.Mapping[str, object] | None = None, + timeout: Timeout | Unset = UNSET, + max_retries: int | None = None, + ) -> WebhookEndpoint: + """Get a webhook endpoint""" + response = await self._request( + ApiRequest( + method="get", + path="/api/v1/webhooks/endpoints/{endpoint_id}", + path_params={ + "endpoint_id": endpoint_id, + }, + error_types={ + "default": _models.RestErrorResponse, + }, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + max_retries=max_retries, + ) + ) + return decode_response(response, WebhookEndpoint) + + async def delete( + self, + endpoint_id: str, + *, + extra_headers: t.Mapping[str, str] | None = None, + extra_query: t.Mapping[str, object] | None = None, + extra_body: t.Mapping[str, object] | None = None, + timeout: Timeout | Unset = UNSET, + max_retries: int | None = None, + ) -> None: + """Delete a webhook endpoint + + The endpoint is archived and its pending deliveries are cancelled.""" + await self._request( + ApiRequest( + method="delete", + path="/api/v1/webhooks/endpoints/{endpoint_id}", + path_params={ + "endpoint_id": endpoint_id, + }, + error_types={ + "default": _models.RestErrorResponse, + }, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + max_retries=max_retries, + ) + ) + + async def update( + self, + endpoint_id: str, + *, + description: str | None | Unset = UNSET, + disabled: bool | None | Unset = UNSET, + event_types: builtins.list[str] | None | Unset = UNSET, + headers: builtins.list[WebhookHeaderInput] | None | Unset = UNSET, + rate_limit_per_sec: int | None | Unset = UNSET, + url: str | None | Unset = UNSET, + extra_headers: t.Mapping[str, str] | None = None, + extra_query: t.Mapping[str, object] | None = None, + extra_body: t.Mapping[str, object] | None = None, + timeout: Timeout | Unset = UNSET, + max_retries: int | None = None, + ) -> WebhookEndpoint: + """Update a webhook endpoint + + Omitted fields are left untouched. Re-enabling a disabled endpoint resets its + consecutive failure count. + + :param description: Omit to leave unchanged; send `null` or an empty string to clear. + :param disabled: Re-enabling an endpoint also resets its consecutive failure count. + :param event_types: Replaces the subscription list. An empty array subscribes to every event type. + :param headers: Replaces the custom header list. An empty array removes every header. + :param rate_limit_per_sec: Omit to leave unchanged; send `null` to remove the rate limit.""" + response = await self._request( + ApiRequest( + method="patch", + path="/api/v1/webhooks/endpoints/{endpoint_id}", + path_params={ + "endpoint_id": endpoint_id, + }, + json_body=to_json_value( + UpdateWebhookEndpointRequest( + description=description, + disabled=disabled, + event_types=event_types, + headers=headers, + rate_limit_per_sec=rate_limit_per_sec, + url=url, + ), + UpdateWebhookEndpointRequest, + ), + error_types={ + "default": _models.RestErrorResponse, + }, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + max_retries=max_retries, + ) + ) + return decode_response(response, WebhookEndpoint) + + def list_deliveries( + self, + endpoint_id: str, + *, + status: WebhookDeliveryStatus | None = None, + page: int | None = None, + per_page: int | None = None, + extra_headers: t.Mapping[str, str] | None = None, + extra_query: t.Mapping[str, object] | None = None, + extra_body: t.Mapping[str, object] | None = None, + timeout: Timeout | Unset = UNSET, + max_retries: int | None = None, + ) -> AsyncPaginator[ + WebhookDelivery, AsyncWebhookEndpointsEndpointsListDeliveriesPage + ]: + """List deliveries for a webhook endpoint + + :param status: Only return deliveries in this state. + :param page: Page number (0-indexed) + :param per_page: Number of items per page""" + + async def fetch(page_param: int | None) -> WebhookDeliveryListResponse: + response = await self._request( + ApiRequest( + method="get", + path="/api/v1/webhooks/endpoints/{endpoint_id}/deliveries", + path_params={ + "endpoint_id": endpoint_id, + }, + query_params=serialize_query_params( + { + "status": status, + "page": page_param, + "per_page": per_page, + }, + ), + error_types={ + "default": _models.RestErrorResponse, + }, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + max_retries=max_retries, + ) + ) + return decode_response(response, WebhookDeliveryListResponse) + + paging = PagePaging[WebhookDeliveryListResponse, WebhookDelivery]( + items=lambda body: body.data, + total_pages=lambda body: step( + body.pagination_meta, lambda v: v.total_pages + ), + first_page=0, + ) + return AsyncPaginator( + lambda: AsyncWebhookEndpointsEndpointsListDeliveriesPage._first( + fetch, page, paging + ) + ) + + async def rotate_secret( + self, + endpoint_id: str, + *, + extra_headers: t.Mapping[str, str] | None = None, + extra_query: t.Mapping[str, object] | None = None, + extra_body: t.Mapping[str, object] | None = None, + timeout: Timeout | Unset = UNSET, + max_retries: int | None = None, + ) -> WebhookEndpointSecret: + """Rotate a webhook endpoint secret + + The previous secret keeps signing alongside the new one for 24 hours, so consumers + can roll over without dropping events.""" + response = await self._request( + ApiRequest( + method="post", + path="/api/v1/webhooks/endpoints/{endpoint_id}/rotate-secret", + path_params={ + "endpoint_id": endpoint_id, + }, + error_types={ + "default": _models.RestErrorResponse, + }, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + max_retries=max_retries, + ) + ) + return decode_response(response, WebhookEndpointSecret) + + async def retrieve_secret( + self, + endpoint_id: str, + *, + extra_headers: t.Mapping[str, str] | None = None, + extra_query: t.Mapping[str, object] | None = None, + extra_body: t.Mapping[str, object] | None = None, + timeout: Timeout | Unset = UNSET, + max_retries: int | None = None, + ) -> WebhookEndpointSecret: + """Reveal a webhook endpoint secret""" + response = await self._request( + ApiRequest( + method="get", + path="/api/v1/webhooks/endpoints/{endpoint_id}/secret", + path_params={ + "endpoint_id": endpoint_id, + }, + error_types={ + "default": _models.RestErrorResponse, + }, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + max_retries=max_retries, + ) + ) + return decode_response(response, WebhookEndpointSecret) + + +class AsyncWebhookEndpointsEndpointsWithRawResponse: + """The methods of :class:`AsyncWebhookEndpointsEndpoints`, returning an :class:`APIResponse`.""" + + def __init__(self, resource: AsyncWebhookEndpointsEndpoints) -> None: + self.list = async_to_raw_response_wrapper(resource.list) + self.create = async_to_raw_response_wrapper(resource.create) + self.retrieve = async_to_raw_response_wrapper(resource.retrieve) + self.delete = async_to_raw_response_wrapper(resource.delete) + self.update = async_to_raw_response_wrapper(resource.update) + self.list_deliveries = async_to_raw_response_wrapper(resource.list_deliveries) + self.rotate_secret = async_to_raw_response_wrapper(resource.rotate_secret) + self.retrieve_secret = async_to_raw_response_wrapper(resource.retrieve_secret) + + +class WebhookEndpointsEndpoints(ApiBaseSync): + """Webhook endpoints endpoints API.""" + + @property + def with_raw_response(self) -> WebhookEndpointsEndpointsWithRawResponse: + """These methods, returning an :class:`APIResponse` with the status and headers.""" + return WebhookEndpointsEndpointsWithRawResponse(self) + + def list( + self, + *, + extra_headers: t.Mapping[str, str] | None = None, + extra_query: t.Mapping[str, object] | None = None, + extra_body: t.Mapping[str, object] | None = None, + timeout: Timeout | Unset = UNSET, + max_retries: int | None = None, + ) -> WebhookEndpointListResponse: + """List webhook endpoints""" + response = self._request( + ApiRequest( + method="get", + path="/api/v1/webhooks/endpoints", + error_types={ + "default": _models.RestErrorResponse, + }, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + max_retries=max_retries, + ) + ) + return decode_response(response, WebhookEndpointListResponse) + + def create( + self, + *, + url: str, + description: str | None | Unset = UNSET, + event_types: builtins.list[str] | None = None, + headers: builtins.list[WebhookHeaderInput] | None = None, + rate_limit_per_sec: int | None | Unset = UNSET, + extra_headers: t.Mapping[str, str] | None = None, + extra_query: t.Mapping[str, object] | None = None, + extra_body: t.Mapping[str, object] | None = None, + timeout: Timeout | Unset = UNSET, + max_retries: int | None = None, + ) -> CreatedWebhookEndpoint: + """Create a webhook endpoint + + The signing secret is returned once, in this response only. + + :param event_types: Event types to subscribe to. Omit or leave empty to receive every event type. + :param headers: Custom headers sent with every delivery. + :param rate_limit_per_sec: Deliveries started per second, at most (1 to 1000). + :param url: HTTPS destination. Private and loopback addresses are rejected unless the instance is configured to allow them.""" + response = self._request( + ApiRequest( + method="post", + path="/api/v1/webhooks/endpoints", + json_body=to_json_value( + CreateWebhookEndpointRequest( + description=description, + event_types=event_types, + headers=headers, + rate_limit_per_sec=rate_limit_per_sec, + url=url, + ), + CreateWebhookEndpointRequest, + ), + error_types={ + "default": _models.RestErrorResponse, + }, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + max_retries=max_retries, + ) + ) + return decode_response(response, CreatedWebhookEndpoint) + + def retrieve( + self, + endpoint_id: str, + *, + extra_headers: t.Mapping[str, str] | None = None, + extra_query: t.Mapping[str, object] | None = None, + extra_body: t.Mapping[str, object] | None = None, + timeout: Timeout | Unset = UNSET, + max_retries: int | None = None, + ) -> WebhookEndpoint: + """Get a webhook endpoint""" + response = self._request( + ApiRequest( + method="get", + path="/api/v1/webhooks/endpoints/{endpoint_id}", + path_params={ + "endpoint_id": endpoint_id, + }, + error_types={ + "default": _models.RestErrorResponse, + }, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + max_retries=max_retries, + ) + ) + return decode_response(response, WebhookEndpoint) + + def delete( + self, + endpoint_id: str, + *, + extra_headers: t.Mapping[str, str] | None = None, + extra_query: t.Mapping[str, object] | None = None, + extra_body: t.Mapping[str, object] | None = None, + timeout: Timeout | Unset = UNSET, + max_retries: int | None = None, + ) -> None: + """Delete a webhook endpoint + + The endpoint is archived and its pending deliveries are cancelled.""" + self._request( + ApiRequest( + method="delete", + path="/api/v1/webhooks/endpoints/{endpoint_id}", + path_params={ + "endpoint_id": endpoint_id, + }, + error_types={ + "default": _models.RestErrorResponse, + }, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + max_retries=max_retries, + ) + ) + + def update( + self, + endpoint_id: str, + *, + description: str | None | Unset = UNSET, + disabled: bool | None | Unset = UNSET, + event_types: builtins.list[str] | None | Unset = UNSET, + headers: builtins.list[WebhookHeaderInput] | None | Unset = UNSET, + rate_limit_per_sec: int | None | Unset = UNSET, + url: str | None | Unset = UNSET, + extra_headers: t.Mapping[str, str] | None = None, + extra_query: t.Mapping[str, object] | None = None, + extra_body: t.Mapping[str, object] | None = None, + timeout: Timeout | Unset = UNSET, + max_retries: int | None = None, + ) -> WebhookEndpoint: + """Update a webhook endpoint + + Omitted fields are left untouched. Re-enabling a disabled endpoint resets its + consecutive failure count. + + :param description: Omit to leave unchanged; send `null` or an empty string to clear. + :param disabled: Re-enabling an endpoint also resets its consecutive failure count. + :param event_types: Replaces the subscription list. An empty array subscribes to every event type. + :param headers: Replaces the custom header list. An empty array removes every header. + :param rate_limit_per_sec: Omit to leave unchanged; send `null` to remove the rate limit.""" + response = self._request( + ApiRequest( + method="patch", + path="/api/v1/webhooks/endpoints/{endpoint_id}", + path_params={ + "endpoint_id": endpoint_id, + }, + json_body=to_json_value( + UpdateWebhookEndpointRequest( + description=description, + disabled=disabled, + event_types=event_types, + headers=headers, + rate_limit_per_sec=rate_limit_per_sec, + url=url, + ), + UpdateWebhookEndpointRequest, + ), + error_types={ + "default": _models.RestErrorResponse, + }, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + max_retries=max_retries, + ) + ) + return decode_response(response, WebhookEndpoint) + + def list_deliveries( + self, + endpoint_id: str, + *, + status: WebhookDeliveryStatus | None = None, + page: int | None = None, + per_page: int | None = None, + extra_headers: t.Mapping[str, str] | None = None, + extra_query: t.Mapping[str, object] | None = None, + extra_body: t.Mapping[str, object] | None = None, + timeout: Timeout | Unset = UNSET, + max_retries: int | None = None, + ) -> WebhookEndpointsEndpointsListDeliveriesPage: + """List deliveries for a webhook endpoint + + :param status: Only return deliveries in this state. + :param page: Page number (0-indexed) + :param per_page: Number of items per page""" + + def fetch(page_param: int | None) -> WebhookDeliveryListResponse: + response = self._request( + ApiRequest( + method="get", + path="/api/v1/webhooks/endpoints/{endpoint_id}/deliveries", + path_params={ + "endpoint_id": endpoint_id, + }, + query_params=serialize_query_params( + { + "status": status, + "page": page_param, + "per_page": per_page, + }, + ), + error_types={ + "default": _models.RestErrorResponse, + }, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + max_retries=max_retries, + ) + ) + return decode_response(response, WebhookDeliveryListResponse) + + paging = PagePaging[WebhookDeliveryListResponse, WebhookDelivery]( + items=lambda body: body.data, + total_pages=lambda body: step( + body.pagination_meta, lambda v: v.total_pages + ), + first_page=0, + ) + return WebhookEndpointsEndpointsListDeliveriesPage._first(fetch, page, paging) + + def rotate_secret( + self, + endpoint_id: str, + *, + extra_headers: t.Mapping[str, str] | None = None, + extra_query: t.Mapping[str, object] | None = None, + extra_body: t.Mapping[str, object] | None = None, + timeout: Timeout | Unset = UNSET, + max_retries: int | None = None, + ) -> WebhookEndpointSecret: + """Rotate a webhook endpoint secret + + The previous secret keeps signing alongside the new one for 24 hours, so consumers + can roll over without dropping events.""" + response = self._request( + ApiRequest( + method="post", + path="/api/v1/webhooks/endpoints/{endpoint_id}/rotate-secret", + path_params={ + "endpoint_id": endpoint_id, + }, + error_types={ + "default": _models.RestErrorResponse, + }, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + max_retries=max_retries, + ) + ) + return decode_response(response, WebhookEndpointSecret) + + def retrieve_secret( + self, + endpoint_id: str, + *, + extra_headers: t.Mapping[str, str] | None = None, + extra_query: t.Mapping[str, object] | None = None, + extra_body: t.Mapping[str, object] | None = None, + timeout: Timeout | Unset = UNSET, + max_retries: int | None = None, + ) -> WebhookEndpointSecret: + """Reveal a webhook endpoint secret""" + response = self._request( + ApiRequest( + method="get", + path="/api/v1/webhooks/endpoints/{endpoint_id}/secret", + path_params={ + "endpoint_id": endpoint_id, + }, + error_types={ + "default": _models.RestErrorResponse, + }, + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + max_retries=max_retries, + ) + ) + return decode_response(response, WebhookEndpointSecret) + + +class WebhookEndpointsEndpointsWithRawResponse: + """The methods of :class:`WebhookEndpointsEndpoints`, returning an :class:`APIResponse`.""" + + def __init__(self, resource: WebhookEndpointsEndpoints) -> None: + self.list = to_raw_response_wrapper(resource.list) + self.create = to_raw_response_wrapper(resource.create) + self.retrieve = to_raw_response_wrapper(resource.retrieve) + self.delete = to_raw_response_wrapper(resource.delete) + self.update = to_raw_response_wrapper(resource.update) + self.list_deliveries = to_raw_response_wrapper(resource.list_deliveries) + self.rotate_secret = to_raw_response_wrapper(resource.rotate_secret) + self.retrieve_secret = to_raw_response_wrapper(resource.retrieve_secret) diff --git a/meteroid/models/__init__.py b/meteroid/models/__init__.py index 3d07067..9a66570 100644 --- a/meteroid/models/__init__.py +++ b/meteroid/models/__init__.py @@ -104,6 +104,8 @@ from .create_product_request import CreateProductRequest from .create_subscription_add_on import CreateSubscriptionAddOn from .create_subscription_components import CreateSubscriptionComponents + from .create_webhook_endpoint_request import CreateWebhookEndpointRequest + from .created_webhook_endpoint import CreatedWebhookEndpoint from .credit_note import CreditNote from .credit_note_custom_properties_request import CreditNoteCustomPropertiesRequest from .credit_note_event import CreditNoteEvent @@ -394,6 +396,7 @@ from .update_feature_request import UpdateFeatureRequest from .update_metric_request import UpdateMetricRequest from .update_product_request import UpdateProductRequest + from .update_webhook_endpoint_request import UpdateWebhookEndpointRequest from .usage_fee import UsageFee from .usage_fee_structure import UsageFeeStructure from .usage_model_enum import UsageModelEnum, UsageModelEnumLiteral @@ -403,6 +406,23 @@ from .usage_response import UsageResponse from .volume_plan_pricing import VolumePlanPricing from .volume_pricing import VolumePricing + from .webhook_delivery import WebhookDelivery + from .webhook_delivery_id import WebhookDeliveryId + from .webhook_delivery_list_response import WebhookDeliveryListResponse + from .webhook_delivery_status import ( + WebhookDeliveryStatus, + WebhookDeliveryStatusLiteral, + ) + from .webhook_endpoint import WebhookEndpoint + from .webhook_endpoint_disabled_reason import ( + WebhookEndpointDisabledReason, + WebhookEndpointDisabledReasonLiteral, + ) + from .webhook_endpoint_id import WebhookEndpointId + from .webhook_endpoint_list_response import WebhookEndpointListResponse + from .webhook_endpoint_secret import WebhookEndpointSecret + from .webhook_header import WebhookHeader + from .webhook_header_input import WebhookHeaderInput _MODULES: dict[str, str] = { "AddOn": "add_on", @@ -504,6 +524,8 @@ "CreateProductRequest": "create_product_request", "CreateSubscriptionAddOn": "create_subscription_add_on", "CreateSubscriptionComponents": "create_subscription_components", + "CreateWebhookEndpointRequest": "create_webhook_endpoint_request", + "CreatedWebhookEndpoint": "created_webhook_endpoint", "CreditNote": "credit_note", "CreditNoteCustomPropertiesRequest": "credit_note_custom_properties_request", "CreditNoteEvent": "credit_note_event", @@ -787,6 +809,7 @@ "UpdateFeatureRequest": "update_feature_request", "UpdateMetricRequest": "update_metric_request", "UpdateProductRequest": "update_product_request", + "UpdateWebhookEndpointRequest": "update_webhook_endpoint_request", "UsageFee": "usage_fee", "UsageFeeStructure": "usage_fee_structure", "UsageModelEnum": "usage_model_enum", @@ -797,6 +820,19 @@ "UsageResponse": "usage_response", "VolumePlanPricing": "volume_plan_pricing", "VolumePricing": "volume_pricing", + "WebhookDelivery": "webhook_delivery", + "WebhookDeliveryId": "webhook_delivery_id", + "WebhookDeliveryListResponse": "webhook_delivery_list_response", + "WebhookDeliveryStatus": "webhook_delivery_status", + "WebhookDeliveryStatusLiteral": "webhook_delivery_status", + "WebhookEndpoint": "webhook_endpoint", + "WebhookEndpointDisabledReason": "webhook_endpoint_disabled_reason", + "WebhookEndpointDisabledReasonLiteral": "webhook_endpoint_disabled_reason", + "WebhookEndpointId": "webhook_endpoint_id", + "WebhookEndpointListResponse": "webhook_endpoint_list_response", + "WebhookEndpointSecret": "webhook_endpoint_secret", + "WebhookHeader": "webhook_header", + "WebhookHeaderInput": "webhook_header_input", } @@ -916,6 +952,8 @@ def __dir__() -> list[str]: "CreateProductRequest", "CreateSubscriptionAddOn", "CreateSubscriptionComponents", + "CreateWebhookEndpointRequest", + "CreatedWebhookEndpoint", "CreditNote", "CreditNoteCustomPropertiesRequest", "CreditNoteEvent", @@ -1199,6 +1237,7 @@ def __dir__() -> list[str]: "UpdateFeatureRequest", "UpdateMetricRequest", "UpdateProductRequest", + "UpdateWebhookEndpointRequest", "UsageFee", "UsageFeeStructure", "UsageModelEnum", @@ -1209,4 +1248,17 @@ def __dir__() -> list[str]: "UsageResponse", "VolumePlanPricing", "VolumePricing", + "WebhookDelivery", + "WebhookDeliveryId", + "WebhookDeliveryListResponse", + "WebhookDeliveryStatus", + "WebhookDeliveryStatusLiteral", + "WebhookEndpoint", + "WebhookEndpointDisabledReason", + "WebhookEndpointDisabledReasonLiteral", + "WebhookEndpointId", + "WebhookEndpointListResponse", + "WebhookEndpointSecret", + "WebhookHeader", + "WebhookHeaderInput", ] diff --git a/meteroid/models/create_webhook_endpoint_request.py b/meteroid/models/create_webhook_endpoint_request.py new file mode 100644 index 0000000..c437b81 --- /dev/null +++ b/meteroid/models/create_webhook_endpoint_request.py @@ -0,0 +1,30 @@ +# this file is @generated +from __future__ import annotations + +import dataclasses +import typing as t + +from ..serialization import UNSET, BaseModel, Unset + +if t.TYPE_CHECKING: + from .webhook_header_input import WebhookHeaderInput + + +@dataclasses.dataclass(kw_only=True) +class CreateWebhookEndpointRequest(BaseModel): + """The `CreateWebhookEndpointRequest` object.""" + + url: str + """HTTPS destination. Private and loopback addresses are rejected unless the + instance is configured to allow them.""" + + description: str | None | Unset = UNSET + + event_types: list[str] | None = None + """Event types to subscribe to. Omit or leave empty to receive every event type.""" + + headers: list[WebhookHeaderInput] | None = None + """Custom headers sent with every delivery.""" + + rate_limit_per_sec: int | None | Unset = UNSET + """Deliveries started per second, at most (1 to 1000).""" diff --git a/meteroid/models/created_webhook_endpoint.py b/meteroid/models/created_webhook_endpoint.py new file mode 100644 index 0000000..cf0fdee --- /dev/null +++ b/meteroid/models/created_webhook_endpoint.py @@ -0,0 +1,22 @@ +# this file is @generated +from __future__ import annotations + +import dataclasses +import typing as t + +from ..serialization import BaseModel + +if t.TYPE_CHECKING: + from .webhook_endpoint import WebhookEndpoint + + +@dataclasses.dataclass(kw_only=True) +class CreatedWebhookEndpoint(BaseModel): + """The signing secret is returned in full here and never again outside the reveal + and rotate endpoints.""" + + _FLATTENED: t.ClassVar[tuple[str, ...]] = ("webhook_endpoint",) + + webhook_endpoint: WebhookEndpoint + + secret: str diff --git a/meteroid/models/customer_create_request.py b/meteroid/models/customer_create_request.py index 63d1470..3404388 100644 --- a/meteroid/models/customer_create_request.py +++ b/meteroid/models/customer_create_request.py @@ -21,10 +21,6 @@ class CustomerCreateRequest(BaseModel): currency: Currency - custom_taxes: list[CustomTaxRate] - - invoicing_emails: list[str] - alias: str | None | Unset = UNSET billing_address: Address | None | Unset = UNSET @@ -41,6 +37,8 @@ class CustomerCreateRequest(BaseModel): """User-defined custom property values, keyed by definition `key`. Validated against the tenant's `CUSTOMER` property definitions. Omit to leave unset.""" + custom_taxes: list[CustomTaxRate] | None = None + customer_type: CustomerType | None = None """`INDIVIDUAL` requires `first_name`, `last_name`, and a billing-address country.""" @@ -49,6 +47,8 @@ class CustomerCreateRequest(BaseModel): first_name: str | None | Unset = UNSET + invoicing_emails: list[str] | None = None + invoicing_entity_id: InvoicingEntityId | None | Unset = UNSET invoicing_language: str | None | Unset = UNSET diff --git a/meteroid/models/event.py b/meteroid/models/event.py index be2f84f..78729db 100644 --- a/meteroid/models/event.py +++ b/meteroid/models/event.py @@ -2,8 +2,9 @@ from __future__ import annotations import dataclasses +from datetime import datetime -from ..serialization import BaseModel +from ..serialization import UNSET, BaseModel, Unset @dataclasses.dataclass(kw_only=True) @@ -19,9 +20,9 @@ class Event(BaseModel): event_id: str """Unique event identifier. Max 255 characters. A UUID or ULID is recommended.""" - timestamp: str - """RFC 3339 timestamp. Defaults to ingestion time if omitted. - Must be between 24 hours ago and 1 hour from now. Set `allow_backfilling` to remove the past limit.""" - properties: dict[str, str] | None = None """Arbitrary string key-value pairs used by billable metrics for filtering and aggregation.""" + + timestamp: datetime | None | Unset = UNSET + """RFC 3339 timestamp. Defaults to ingestion time if omitted. + Must be between 24 hours ago and 1 hour from now. Set `allow_backfilling` to remove the past limit.""" diff --git a/meteroid/models/plan.py b/meteroid/models/plan.py index 975f9cf..9cf2d25 100644 --- a/meteroid/models/plan.py +++ b/meteroid/models/plan.py @@ -9,6 +9,7 @@ if t.TYPE_CHECKING: from .available_parameters import AvailableParameters + from .currency import Currency from .entitlement import Entitlement from .minimum_commitment import MinimumCommitment from .plan_id import PlanId @@ -28,7 +29,7 @@ class Plan(BaseModel): created_at: datetime - currency: str + currency: Currency id: PlanId diff --git a/meteroid/models/plan_version_summary.py b/meteroid/models/plan_version_summary.py index 606a8ac..a69cf89 100644 --- a/meteroid/models/plan_version_summary.py +++ b/meteroid/models/plan_version_summary.py @@ -8,6 +8,7 @@ from ..serialization import BaseModel if t.TYPE_CHECKING: + from .currency import Currency from .plan_version_id import PlanVersionId @@ -17,7 +18,7 @@ class PlanVersionSummary(BaseModel): created_at: datetime - currency: str + currency: Currency id: PlanVersionId diff --git a/meteroid/models/update_webhook_endpoint_request.py b/meteroid/models/update_webhook_endpoint_request.py new file mode 100644 index 0000000..5224aff --- /dev/null +++ b/meteroid/models/update_webhook_endpoint_request.py @@ -0,0 +1,32 @@ +# this file is @generated +from __future__ import annotations + +import dataclasses +import typing as t + +from ..serialization import UNSET, BaseModel, Unset + +if t.TYPE_CHECKING: + from .webhook_header_input import WebhookHeaderInput + + +@dataclasses.dataclass(kw_only=True) +class UpdateWebhookEndpointRequest(BaseModel): + """The `UpdateWebhookEndpointRequest` object.""" + + description: str | None | Unset = UNSET + """Omit to leave unchanged; send `null` or an empty string to clear.""" + + disabled: bool | None | Unset = UNSET + """Re-enabling an endpoint also resets its consecutive failure count.""" + + event_types: list[str] | None | Unset = UNSET + """Replaces the subscription list. An empty array subscribes to every event type.""" + + headers: list[WebhookHeaderInput] | None | Unset = UNSET + """Replaces the custom header list. An empty array removes every header.""" + + rate_limit_per_sec: int | None | Unset = UNSET + """Omit to leave unchanged; send `null` to remove the rate limit.""" + + url: str | None | Unset = UNSET diff --git a/meteroid/models/webhook_delivery.py b/meteroid/models/webhook_delivery.py new file mode 100644 index 0000000..f3bbfb2 --- /dev/null +++ b/meteroid/models/webhook_delivery.py @@ -0,0 +1,44 @@ +# this file is @generated +from __future__ import annotations + +import dataclasses +import typing as t +from datetime import datetime + +from ..serialization import BaseModel + +if t.TYPE_CHECKING: + from .event_id import EventId + from .webhook_delivery_id import WebhookDeliveryId + from .webhook_delivery_status import WebhookDeliveryStatus + from .webhook_endpoint_id import WebhookEndpointId + + +@dataclasses.dataclass(kw_only=True) +class WebhookDelivery(BaseModel): + """One event queued for one endpoint, with the state of its retry cycle.""" + + attempt_count: int + + created_at: datetime + + endpoint_id: WebhookEndpointId + + event_type: str + + id: WebhookDeliveryId + + manual: bool + """True when the delivery was created by a resend or a test event.""" + + message_id: EventId + + status: WebhookDeliveryStatus + + completed_at: datetime | None = None + + last_error: str | None = None + + last_response_status: int | None = None + + next_attempt_at: datetime | None = None diff --git a/meteroid/models/webhook_delivery_id.py b/meteroid/models/webhook_delivery_id.py new file mode 100644 index 0000000..af3f0fa --- /dev/null +++ b/meteroid/models/webhook_delivery_id.py @@ -0,0 +1,4 @@ +# this file is @generated +import typing as t + +WebhookDeliveryId: t.TypeAlias = str diff --git a/meteroid/models/webhook_delivery_list_response.py b/meteroid/models/webhook_delivery_list_response.py new file mode 100644 index 0000000..17d626c --- /dev/null +++ b/meteroid/models/webhook_delivery_list_response.py @@ -0,0 +1,20 @@ +# this file is @generated +from __future__ import annotations + +import dataclasses +import typing as t + +from ..serialization import BaseModel + +if t.TYPE_CHECKING: + from .pagination_response import PaginationResponse + from .webhook_delivery import WebhookDelivery + + +@dataclasses.dataclass(kw_only=True) +class WebhookDeliveryListResponse(BaseModel): + """The `WebhookDeliveryListResponse` object.""" + + data: list[WebhookDelivery] + + pagination_meta: PaginationResponse diff --git a/meteroid/models/webhook_delivery_status.py b/meteroid/models/webhook_delivery_status.py new file mode 100644 index 0000000..1eb6931 --- /dev/null +++ b/meteroid/models/webhook_delivery_status.py @@ -0,0 +1,20 @@ +# this file is @generated +import typing as t + +from ..serialization import StrEnum + + +class WebhookDeliveryStatus(StrEnum): + """The values of `WebhookDeliveryStatus`; others are kept as received.""" + + PENDING = "PENDING" + IN_FLIGHT = "IN_FLIGHT" + SUCCEEDED = "SUCCEEDED" + FAILED = "FAILED" + CANCELLED = "CANCELLED" + + +WebhookDeliveryStatusLiteral: t.TypeAlias = t.Literal[ + "PENDING", "IN_FLIGHT", "SUCCEEDED", "FAILED", "CANCELLED" +] +"""The values of :class:`WebhookDeliveryStatus`, which arguments take as plain strings too.""" diff --git a/meteroid/models/webhook_endpoint.py b/meteroid/models/webhook_endpoint.py new file mode 100644 index 0000000..d7ead8c --- /dev/null +++ b/meteroid/models/webhook_endpoint.py @@ -0,0 +1,60 @@ +# this file is @generated +from __future__ import annotations + +import dataclasses +import typing as t +from datetime import datetime + +from ..serialization import BaseModel + +if t.TYPE_CHECKING: + from .webhook_endpoint_disabled_reason import WebhookEndpointDisabledReason + from .webhook_endpoint_id import WebhookEndpointId + from .webhook_header import WebhookHeader + + +@dataclasses.dataclass(kw_only=True) +class WebhookEndpoint(BaseModel): + """A destination Meteroid POSTs signed event payloads to.""" + + consecutive_failures: int + """Failures since the last success, reset to 0 on any 2xx.""" + + created_at: datetime + + disabled: bool + + event_types: list[str] + """Subscribed event types. Empty means every event type.""" + + headers: list[WebhookHeader] + """Custom headers sent with every delivery.""" + + id: WebhookEndpointId + + max_in_flight: int + """How many deliveries this endpoint may have in flight at once. Read-only; it is + set from the tenant's environment when the endpoint is created.""" + + needs_setup: bool + """A sensitive header still waits for its value; the endpoint cannot be enabled + until it is set.""" + + url: str + + description: str | None = None + + disabled_reason: WebhookEndpointDisabledReason | None = None + + last_failure_at: datetime | None = None + + last_success_at: datetime | None = None + + paused_until: datetime | None = None + """The endpoint was unreachable several times in a row: nothing is sent before + this time, then it is retried one delivery at a time until it answers again.""" + + rate_limit_per_sec: int | None = None + """Deliveries started per second, at most.""" + + updated_at: datetime | None = None diff --git a/meteroid/models/webhook_endpoint_disabled_reason.py b/meteroid/models/webhook_endpoint_disabled_reason.py new file mode 100644 index 0000000..b02a88e --- /dev/null +++ b/meteroid/models/webhook_endpoint_disabled_reason.py @@ -0,0 +1,18 @@ +# this file is @generated +import typing as t + +from ..serialization import StrEnum + + +class WebhookEndpointDisabledReason(StrEnum): + """The values of `WebhookEndpointDisabledReason`; others are kept as received.""" + + MANUAL = "MANUAL" + AUTO_FAILURES = "AUTO_FAILURES" + GONE = "GONE" + + +WebhookEndpointDisabledReasonLiteral: t.TypeAlias = t.Literal[ + "MANUAL", "AUTO_FAILURES", "GONE" +] +"""The values of :class:`WebhookEndpointDisabledReason`, which arguments take as plain strings too.""" diff --git a/meteroid/models/webhook_endpoint_id.py b/meteroid/models/webhook_endpoint_id.py new file mode 100644 index 0000000..9a93864 --- /dev/null +++ b/meteroid/models/webhook_endpoint_id.py @@ -0,0 +1,4 @@ +# this file is @generated +import typing as t + +WebhookEndpointId: t.TypeAlias = str diff --git a/meteroid/models/webhook_endpoint_list_response.py b/meteroid/models/webhook_endpoint_list_response.py new file mode 100644 index 0000000..a96c52d --- /dev/null +++ b/meteroid/models/webhook_endpoint_list_response.py @@ -0,0 +1,17 @@ +# this file is @generated +from __future__ import annotations + +import dataclasses +import typing as t + +from ..serialization import BaseModel + +if t.TYPE_CHECKING: + from .webhook_endpoint import WebhookEndpoint + + +@dataclasses.dataclass(kw_only=True) +class WebhookEndpointListResponse(BaseModel): + """The `WebhookEndpointListResponse` object.""" + + data: list[WebhookEndpoint] diff --git a/meteroid/models/webhook_endpoint_secret.py b/meteroid/models/webhook_endpoint_secret.py new file mode 100644 index 0000000..06cdc70 --- /dev/null +++ b/meteroid/models/webhook_endpoint_secret.py @@ -0,0 +1,13 @@ +# this file is @generated +from __future__ import annotations + +import dataclasses + +from ..serialization import BaseModel + + +@dataclasses.dataclass(kw_only=True) +class WebhookEndpointSecret(BaseModel): + """The `WebhookEndpointSecret` object.""" + + secret: str diff --git a/meteroid/models/webhook_header.py b/meteroid/models/webhook_header.py new file mode 100644 index 0000000..4f4a0bf --- /dev/null +++ b/meteroid/models/webhook_header.py @@ -0,0 +1,20 @@ +# this file is @generated +from __future__ import annotations + +import dataclasses + +from ..serialization import BaseModel + + +@dataclasses.dataclass(kw_only=True) +class WebhookHeader(BaseModel): + """A custom header sent with every delivery. A sensitive header never returns its + value; `set` says whether it has one.""" + + name: str + + sensitive: bool + + set: bool + + value: str | None = None diff --git a/meteroid/models/webhook_header_input.py b/meteroid/models/webhook_header_input.py new file mode 100644 index 0000000..c11bffb --- /dev/null +++ b/meteroid/models/webhook_header_input.py @@ -0,0 +1,18 @@ +# this file is @generated +from __future__ import annotations + +import dataclasses + +from ..serialization import UNSET, BaseModel, Unset + + +@dataclasses.dataclass(kw_only=True) +class WebhookHeaderInput(BaseModel): + """A custom header to send with every delivery. A sensitive header is write-only: + it is never returned, and on update sending it without a value keeps its value.""" + + name: str + + sensitive: bool | None = None + + value: str | None | Unset = UNSET diff --git a/tests/test_customers.py b/tests/test_customers.py index b9fb0cd..862397d 100644 --- a/tests/test_customers.py +++ b/tests/test_customers.py @@ -30,10 +30,7 @@ def test_create(self) -> None: ) call( client.customers.create, - body=decode( - CustomerCreateRequest, - '{"currency":"ERN","custom_taxes":[{"name":"sample","rate":"sample","tax_code":"sample"}],"invoicing_emails":["sample"]}', - ), + body=decode(CustomerCreateRequest, '{"currency":"ERN"}'), ) self.assertEqual(requests, ["POST /api/v1/customers"]) diff --git a/tests/test_events.py b/tests/test_events.py index 37d5ab4..9b2f8b0 100644 --- a/tests/test_events.py +++ b/tests/test_events.py @@ -14,7 +14,7 @@ def test_ingest(self) -> None: client.events.ingest, body=decode( IngestEventsRequest, - '{"events":[{"code":"sample","customer_id":"sample","event_id":"sample","timestamp":"sample"}]}', + '{"events":[{"code":"sample","customer_id":"sample","event_id":"sample"}]}', ), ) self.assertEqual(requests, ["POST /api/v1/events/ingest"]) diff --git a/tests/test_plans.py b/tests/test_plans.py index 31dbbb6..e7bc300 100644 --- a/tests/test_plans.py +++ b/tests/test_plans.py @@ -46,7 +46,7 @@ def test_list(self) -> None: client, requests = mock( 200, "application/json", - '{"data":[{"available_parameters":{},"created_at":"2024-03-15T10:30:45.123+02:00","currency":"sample","id":"plan_id_78","name":"sample","net_terms":-2147483648,"plan_type":"FREE","price_components":[{"id":"price_component_id_82","name":"sample"}],"product_family":{"id":"product_family_id_59","name":"sample"},"status":"INACTIVE","tax_inclusive":true,"version":-2147483648,"version_id":"plan_version_id_92"}],"pagination_meta":{"page":-123456789,"per_page":-123456789,"total_items":-9007199254740993,"total_pages":123456789}}', + '{"data":[{"available_parameters":{},"created_at":"2024-03-15T10:30:45.123+02:00","currency":"WST","id":"plan_id_78","name":"sample","net_terms":-2147483648,"plan_type":"FREE","price_components":[{"id":"price_component_id_82","name":"sample"}],"product_family":{"id":"product_family_id_59","name":"sample"},"status":"INACTIVE","tax_inclusive":true,"version":-2147483648,"version_id":"plan_version_id_92"}],"pagination_meta":{"page":-123456789,"per_page":-123456789,"total_items":-9007199254740993,"total_pages":123456789}}', ) call(client.plans.list) self.assertEqual(requests, ["GET /api/v1/plans"]) @@ -55,7 +55,7 @@ def test_create(self) -> None: client, requests = mock( 200, "application/json", - '{"available_parameters":{},"created_at":"2023-12-31T23:59:59.999-05:30","currency":"sample","id":"plan_id_13","name":"sample","net_terms":2147483647,"plan_type":"FREE","price_components":[{"id":"price_component_id_38","name":"sample"}],"product_family":{"id":"product_family_id_66","name":"sample"},"status":"ARCHIVED","tax_inclusive":false,"version":123456789,"version_id":"plan_version_id_84"}', + '{"available_parameters":{},"created_at":"2023-12-31T23:59:59.999-05:30","currency":"COP","id":"plan_id_13","name":"sample","net_terms":2147483647,"plan_type":"FREE","price_components":[{"id":"price_component_id_38","name":"sample"}],"product_family":{"id":"product_family_id_66","name":"sample"},"status":"ARCHIVED","tax_inclusive":false,"version":123456789,"version_id":"plan_version_id_84"}', ) call( client.plans.create, @@ -70,7 +70,7 @@ def test_retrieve(self) -> None: client, requests = mock( 200, "application/json", - '{"available_parameters":{},"created_at":"2023-12-31T23:59:59.999-05:30","currency":"sample","id":"plan_id_13","name":"sample","net_terms":2147483647,"plan_type":"FREE","price_components":[{"id":"price_component_id_38","name":"sample"}],"product_family":{"id":"product_family_id_66","name":"sample"},"status":"ARCHIVED","tax_inclusive":false,"version":123456789,"version_id":"plan_version_id_84"}', + '{"available_parameters":{},"created_at":"2023-12-31T23:59:59.999-05:30","currency":"COP","id":"plan_id_13","name":"sample","net_terms":2147483647,"plan_type":"FREE","price_components":[{"id":"price_component_id_38","name":"sample"}],"product_family":{"id":"product_family_id_66","name":"sample"},"status":"ARCHIVED","tax_inclusive":false,"version":123456789,"version_id":"plan_version_id_84"}', ) call(client.plans.retrieve, "plan_id") self.assertEqual(requests, ["GET /api/v1/plans/plan_id"]) @@ -79,7 +79,7 @@ def test_replace(self) -> None: client, requests = mock( 200, "application/json", - '{"available_parameters":{},"created_at":"2023-12-31T23:59:59.999-05:30","currency":"sample","id":"plan_id_13","name":"sample","net_terms":2147483647,"plan_type":"FREE","price_components":[{"id":"price_component_id_38","name":"sample"}],"product_family":{"id":"product_family_id_66","name":"sample"},"status":"ARCHIVED","tax_inclusive":false,"version":123456789,"version_id":"plan_version_id_84"}', + '{"available_parameters":{},"created_at":"2023-12-31T23:59:59.999-05:30","currency":"COP","id":"plan_id_13","name":"sample","net_terms":2147483647,"plan_type":"FREE","price_components":[{"id":"price_component_id_38","name":"sample"}],"product_family":{"id":"product_family_id_66","name":"sample"},"status":"ARCHIVED","tax_inclusive":false,"version":123456789,"version_id":"plan_version_id_84"}', ) call( client.plans.replace, @@ -95,7 +95,7 @@ def test_update(self) -> None: client, requests = mock( 200, "application/json", - '{"available_parameters":{},"created_at":"2023-12-31T23:59:59.999-05:30","currency":"sample","id":"plan_id_13","name":"sample","net_terms":2147483647,"plan_type":"FREE","price_components":[{"id":"price_component_id_38","name":"sample"}],"product_family":{"id":"product_family_id_66","name":"sample"},"status":"ARCHIVED","tax_inclusive":false,"version":123456789,"version_id":"plan_version_id_84"}', + '{"available_parameters":{},"created_at":"2023-12-31T23:59:59.999-05:30","currency":"COP","id":"plan_id_13","name":"sample","net_terms":2147483647,"plan_type":"FREE","price_components":[{"id":"price_component_id_38","name":"sample"}],"product_family":{"id":"product_family_id_66","name":"sample"},"status":"ARCHIVED","tax_inclusive":false,"version":123456789,"version_id":"plan_version_id_84"}', ) call(client.plans.update, "plan_id", body=decode(PatchPlanRequest, "{}")) self.assertEqual(requests, ["PATCH /api/v1/plans/plan_id"]) @@ -109,7 +109,7 @@ def test_publish(self) -> None: client, requests = mock( 200, "application/json", - '{"available_parameters":{},"created_at":"2023-12-31T23:59:59.999-05:30","currency":"sample","id":"plan_id_13","name":"sample","net_terms":2147483647,"plan_type":"FREE","price_components":[{"id":"price_component_id_38","name":"sample"}],"product_family":{"id":"product_family_id_66","name":"sample"},"status":"ARCHIVED","tax_inclusive":false,"version":123456789,"version_id":"plan_version_id_84"}', + '{"available_parameters":{},"created_at":"2023-12-31T23:59:59.999-05:30","currency":"COP","id":"plan_id_13","name":"sample","net_terms":2147483647,"plan_type":"FREE","price_components":[{"id":"price_component_id_38","name":"sample"}],"product_family":{"id":"product_family_id_66","name":"sample"},"status":"ARCHIVED","tax_inclusive":false,"version":123456789,"version_id":"plan_version_id_84"}', ) call(client.plans.publish, "plan_id") self.assertEqual(requests, ["POST /api/v1/plans/plan_id/publish"]) diff --git a/tests/test_plans_versions.py b/tests/test_plans_versions.py index e576334..001dabc 100644 --- a/tests/test_plans_versions.py +++ b/tests/test_plans_versions.py @@ -37,7 +37,7 @@ def test_list(self) -> None: client, requests = mock( 200, "application/json", - '{"data":[{"created_at":"2023-12-31T23:59:59.999-05:30","currency":"sample","id":"plan_version_id_2","is_draft":true,"version":-2147483648}],"pagination_meta":{"page":-123456789,"per_page":-123456789,"total_items":-9007199254740993,"total_pages":123456789}}', + '{"data":[{"created_at":"2023-12-31T23:59:59.999-05:30","currency":"CVE","id":"plan_version_id_2","is_draft":true,"version":-2147483648}],"pagination_meta":{"page":-123456789,"per_page":-123456789,"total_items":-9007199254740993,"total_pages":123456789}}', ) call(client.plans.versions.list, "plan_id") self.assertEqual(requests, ["GET /api/v1/plans/plan_id/versions"]) diff --git a/tests/test_webhook_endpoints.py b/tests/test_webhook_endpoints.py new file mode 100644 index 0000000..2fed0eb --- /dev/null +++ b/tests/test_webhook_endpoints.py @@ -0,0 +1,18 @@ +# this file is @generated +# ruff: noqa: I001 (where the package sorts depends on whether it is generated yet) +import unittest + +from perseid_mock import call, mock + + +class WebhookEndpointsTest(unittest.TestCase): + def test_resend_webhook_delivery(self) -> None: + client, requests = mock( + 200, + "application/json", + '{"attempt_count":-2147483648,"created_at":"2023-12-31T23:59:59.999-05:30","endpoint_id":"webhook_endpoint_id_44","event_type":"sample","id":"webhook_delivery_id_90","manual":false,"message_id":"event_id_90","status":"IN_FLIGHT"}', + ) + call(client.webhook_endpoints.resend_webhook_delivery, "delivery_id") + self.assertEqual( + requests, ["POST /api/v1/webhooks/deliveries/delivery_id/resend"] + ) diff --git a/tests/test_webhook_endpoints_endpoints.py b/tests/test_webhook_endpoints_endpoints.py new file mode 100644 index 0000000..881a869 --- /dev/null +++ b/tests/test_webhook_endpoints_endpoints.py @@ -0,0 +1,82 @@ +# this file is @generated +# ruff: noqa: I001 (where the package sorts depends on whether it is generated yet) +import unittest + +from meteroid.models import CreateWebhookEndpointRequest, UpdateWebhookEndpointRequest + +from perseid_mock import call, decode, mock + + +class WebhookEndpointsEndpointsTest(unittest.TestCase): + def test_list(self) -> None: + client, requests = mock( + 200, + "application/json", + '{"data":[{"consecutive_failures":-123456789,"created_at":"2023-12-31T23:59:59.999-05:30","disabled":true,"event_types":["sample"],"headers":[{"name":"sample","sensitive":false,"set":true}],"id":"webhook_endpoint_id_25","max_in_flight":-2147483648,"needs_setup":false,"url":"sample"}]}', + ) + call(client.webhook_endpoints.endpoints.list) + self.assertEqual(requests, ["GET /api/v1/webhooks/endpoints"]) + + def test_create(self) -> None: + client, requests = mock( + 200, + "application/json", + '{"consecutive_failures":-123456789,"created_at":"2023-12-31T23:59:59.999-05:30","disabled":true,"event_types":["sample"],"headers":[{"name":"sample","sensitive":false,"set":true}],"id":"webhook_endpoint_id_25","max_in_flight":-2147483648,"needs_setup":false,"url":"sample","secret":"sample"}', + ) + call( + client.webhook_endpoints.endpoints.create, + body=decode(CreateWebhookEndpointRequest, '{"url":"sample"}'), + ) + self.assertEqual(requests, ["POST /api/v1/webhooks/endpoints"]) + + def test_retrieve(self) -> None: + client, requests = mock( + 200, + "application/json", + '{"consecutive_failures":-2147483648,"created_at":"2024-03-15T10:30:45.123+02:00","disabled":true,"event_types":["sample"],"headers":[{"name":"sample","sensitive":true,"set":true}],"id":"webhook_endpoint_id_40","max_in_flight":-123456789,"needs_setup":true,"url":"sample"}', + ) + call(client.webhook_endpoints.endpoints.retrieve, "endpoint_id") + self.assertEqual(requests, ["GET /api/v1/webhooks/endpoints/endpoint_id"]) + + def test_delete(self) -> None: + client, requests = mock(204, None, "") + call(client.webhook_endpoints.endpoints.delete, "endpoint_id") + self.assertEqual(requests, ["DELETE /api/v1/webhooks/endpoints/endpoint_id"]) + + def test_update(self) -> None: + client, requests = mock( + 200, + "application/json", + '{"consecutive_failures":-2147483648,"created_at":"2024-03-15T10:30:45.123+02:00","disabled":true,"event_types":["sample"],"headers":[{"name":"sample","sensitive":true,"set":true}],"id":"webhook_endpoint_id_40","max_in_flight":-123456789,"needs_setup":true,"url":"sample"}', + ) + call( + client.webhook_endpoints.endpoints.update, + "endpoint_id", + body=decode(UpdateWebhookEndpointRequest, "{}"), + ) + self.assertEqual(requests, ["PATCH /api/v1/webhooks/endpoints/endpoint_id"]) + + def test_list_deliveries(self) -> None: + client, requests = mock( + 200, + "application/json", + '{"data":[{"attempt_count":-123456789,"created_at":"2024-03-15T10:30:45.123+02:00","endpoint_id":"webhook_endpoint_id_90","event_type":"sample","id":"webhook_delivery_id_0","manual":false,"message_id":"event_id_67","status":"SUCCEEDED"}],"pagination_meta":{"page":-123456789,"per_page":-123456789,"total_items":-9007199254740993,"total_pages":123456789}}', + ) + call(client.webhook_endpoints.endpoints.list_deliveries, "endpoint_id") + self.assertEqual( + requests, ["GET /api/v1/webhooks/endpoints/endpoint_id/deliveries"] + ) + + def test_rotate_secret(self) -> None: + client, requests = mock(200, "application/json", '{"secret":"sample"}') + call(client.webhook_endpoints.endpoints.rotate_secret, "endpoint_id") + self.assertEqual( + requests, ["POST /api/v1/webhooks/endpoints/endpoint_id/rotate-secret"] + ) + + def test_retrieve_secret(self) -> None: + client, requests = mock(200, "application/json", '{"secret":"sample"}') + call(client.webhook_endpoints.endpoints.retrieve_secret, "endpoint_id") + self.assertEqual( + requests, ["GET /api/v1/webhooks/endpoints/endpoint_id/secret"] + )