diff --git a/api.md b/api.md index b92e499..8956ead 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) · [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) · [Product families](#product-families) · [Products](#products) · [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) ## Add ons @@ -23,10 +23,17 @@ model (`AddOnListResponse`) with the paging members `items`, `has_next_page()`, | `client.add_ons.retrieve(addon_id: str) -> AddOn` | `GET /api/v1/addons/{addon_id}` | [`AddOn`](meteroid/models/add_on.py) | | `client.add_ons.update(addon_id: str, *, description=..., max_instances_per_subscription=..., name=..., price_id=..., self_serviceable=...) -> AddOn` | `PATCH /api/v1/addons/{addon_id}` | [`AddOn`](meteroid/models/add_on.py) | | `client.add_ons.archive(addon_id: str) -> None` | `POST /api/v1/addons/{addon_id}/archive` | nothing | -| `client.add_ons.list_entitlements(addon_id: str) -> ResolvedEntitlementListResponse` | `GET /api/v1/addons/{addon_id}/entitlements` | [`ResolvedEntitlementListResponse`](meteroid/models/resolved_entitlement_list_response.py) | -| `client.add_ons.create_entitlement(addon_id: str, *, entitlements: list[EntitlementSpecRequest]) -> EntitlementListResponse` | `POST /api/v1/addons/{addon_id}/entitlements` | [`EntitlementListResponse`](meteroid/models/entitlement_list_response.py) | | `client.add_ons.unarchive(addon_id: str) -> None` | `POST /api/v1/addons/{addon_id}/unarchive` | nothing | +### Add ons entitlements + +[`client.add_ons.entitlements`](meteroid/api/add_ons_entitlements.py) + +| Method | Request | Returns | +| --- | --- | --- | +| `client.add_ons.entitlements.list(addon_id: str) -> ResolvedEntitlementListResponse` | `GET /api/v1/addons/{addon_id}/entitlements` | [`ResolvedEntitlementListResponse`](meteroid/models/resolved_entitlement_list_response.py) | +| `client.add_ons.entitlements.create(addon_id: str, *, entitlements: list[EntitlementSpecRequest]) -> EntitlementListResponse` | `POST /api/v1/addons/{addon_id}/entitlements` | [`EntitlementListResponse`](meteroid/models/entitlement_list_response.py) | + ## Batch jobs [`client.batch_jobs`](meteroid/api/batch_jobs.py) @@ -204,15 +211,22 @@ model (`AddOnListResponse`) with the paging members `items`, `has_next_page()`, | `client.plans.create_plan_version_entitlement(plan_version_id: str, *, entitlements: list[EntitlementSpecRequest]) -> EntitlementListResponse` | `POST /api/v1/plan-versions/{plan_version_id}/entitlements` | [`EntitlementListResponse`](meteroid/models/entitlement_list_response.py) | | `client.plans.list(*, product_family_id=..., search=..., status=..., plan_type=..., order_by=..., page=..., per_page=...) -> PlansListPage` | `GET /api/v1/plans` | [`PlanListResponse`](meteroid/models/plan_list_response.py) pages of [`Plan`](meteroid/models/plan.py) | | `client.plans.create(*, components: list[PriceComponentInput], currency: str, name: str, plan_type: PlanTypeEnum, product_family_id: ProductFamilyId, status: PlanStatusEnum, add_ons=..., billing=..., description=..., entitlements=..., self_service_rank=..., tax_inclusive=..., trial=...) -> Plan` | `POST /api/v1/plans` | [`Plan`](meteroid/models/plan.py) | -| `client.plans.update_version_minimum(plan_version_id: str, *, amount: str, scope: MinimumCommitmentScope) -> MinimumCommitment` | `PUT /api/v1/plans/versions/{plan_version_id}/minimum` | [`MinimumCommitment`](meteroid/models/minimum_commitment.py) | -| `client.plans.delete_version_minimum(plan_version_id: str) -> None` | `DELETE /api/v1/plans/versions/{plan_version_id}/minimum` | nothing | | `client.plans.retrieve(plan_id: str, *, version=...) -> Plan` | `GET /api/v1/plans/{plan_id}` | [`Plan`](meteroid/models/plan.py) | | `client.plans.replace(plan_id: str, *, components: list[PriceComponentInput], currency: str, name: str, add_ons=..., billing=..., description=..., entitlements=..., minimum_commitment=..., status=..., tax_inclusive=..., trial=...) -> Plan` | `PUT /api/v1/plans/{plan_id}` | [`Plan`](meteroid/models/plan.py) | | `client.plans.update(plan_id: str, *, description=..., name=..., self_service_rank=...) -> Plan` | `PATCH /api/v1/plans/{plan_id}` | [`Plan`](meteroid/models/plan.py) | | `client.plans.archive(plan_id: str) -> None` | `POST /api/v1/plans/{plan_id}/archive` | nothing | | `client.plans.publish(plan_id: str) -> Plan` | `POST /api/v1/plans/{plan_id}/publish` | [`Plan`](meteroid/models/plan.py) | | `client.plans.unarchive(plan_id: str) -> None` | `POST /api/v1/plans/{plan_id}/unarchive` | nothing | -| `client.plans.list_versions(plan_id: str, *, page=..., per_page=...) -> PlansListVersionsPage` | `GET /api/v1/plans/{plan_id}/versions` | [`PlanVersionListResponse`](meteroid/models/plan_version_list_response.py) pages of [`PlanVersionSummary`](meteroid/models/plan_version_summary.py) | + +### Plans versions + +[`client.plans.versions`](meteroid/api/plans_versions.py) + +| Method | Request | Returns | +| --- | --- | --- | +| `client.plans.versions.update_minimum(plan_version_id: str, *, amount: str, scope: MinimumCommitmentScope) -> MinimumCommitment` | `PUT /api/v1/plans/versions/{plan_version_id}/minimum` | [`MinimumCommitment`](meteroid/models/minimum_commitment.py) | +| `client.plans.versions.delete_minimum(plan_version_id: str) -> None` | `DELETE /api/v1/plans/versions/{plan_version_id}/minimum` | nothing | +| `client.plans.versions.list(plan_id: str, *, page=..., per_page=...) -> PlansVersionsListPage` | `GET /api/v1/plans/{plan_id}/versions` | [`PlanVersionListResponse`](meteroid/models/plan_version_list_response.py) pages of [`PlanVersionSummary`](meteroid/models/plan_version_summary.py) | ## Product families @@ -235,10 +249,17 @@ model (`AddOnListResponse`) with the paging members `items`, `has_next_page()`, | `client.products.retrieve(product_id: str) -> Product` | `GET /api/v1/products/{product_id}` | [`Product`](meteroid/models/product.py) | | `client.products.update(product_id: str, *, description=..., fee_structure=..., name=...) -> Product` | `PATCH /api/v1/products/{product_id}` | [`Product`](meteroid/models/product.py) | | `client.products.archive(product_id: str) -> None` | `POST /api/v1/products/{product_id}/archive` | nothing | -| `client.products.list_entitlements(product_id: str) -> ResolvedEntitlementListResponse` | `GET /api/v1/products/{product_id}/entitlements` | [`ResolvedEntitlementListResponse`](meteroid/models/resolved_entitlement_list_response.py) | -| `client.products.create_entitlement(product_id: str, *, entitlements: list[EntitlementSpecRequest]) -> EntitlementListResponse` | `POST /api/v1/products/{product_id}/entitlements` | [`EntitlementListResponse`](meteroid/models/entitlement_list_response.py) | | `client.products.unarchive(product_id: str) -> None` | `POST /api/v1/products/{product_id}/unarchive` | nothing | +### Products entitlements + +[`client.products.entitlements`](meteroid/api/products_entitlements.py) + +| Method | Request | Returns | +| --- | --- | --- | +| `client.products.entitlements.list(product_id: str) -> ResolvedEntitlementListResponse` | `GET /api/v1/products/{product_id}/entitlements` | [`ResolvedEntitlementListResponse`](meteroid/models/resolved_entitlement_list_response.py) | +| `client.products.entitlements.create(product_id: str, *, entitlements: list[EntitlementSpecRequest]) -> EntitlementListResponse` | `POST /api/v1/products/{product_id}/entitlements` | [`EntitlementListResponse`](meteroid/models/entitlement_list_response.py) | + ## Subscriptions [`client.subscriptions`](meteroid/api/subscriptions.py) diff --git a/meteroid/api/__init__.py b/meteroid/api/__init__.py index 0328548..780ab2c 100644 --- a/meteroid/api/__init__.py +++ b/meteroid/api/__init__.py @@ -51,7 +51,7 @@ AsyncInvoicesListPage, AsyncMetricsListPage, AsyncPlansListPage, - AsyncPlansListVersionsPage, + AsyncPlansVersionsListPage, AsyncProductFamiliesListPage, AsyncProductsListPage, AsyncSubscriptionsListPage, @@ -65,12 +65,13 @@ InvoicesListPage, MetricsListPage, PlansListPage, - PlansListVersionsPage, + PlansVersionsListPage, ProductFamiliesListPage, ProductsListPage, SubscriptionsListPage, ) from .add_ons import AddOns, AsyncAddOns + from .add_ons_entitlements import AddOnsEntitlements, AsyncAddOnsEntitlements from .batch_jobs import AsyncBatchJobs, BatchJobs from .checkout_sessions import AsyncCheckoutSessions, CheckoutSessions from .connect import AsyncConnect, Connect @@ -86,8 +87,10 @@ from .oauth import AsyncOauth, Oauth from .oauth_apps import AsyncOauthApps, OauthApps from .plans import AsyncPlans, Plans + from .plans_versions import AsyncPlansVersions, PlansVersions from .product_families import AsyncProductFamilies, ProductFamilies from .products import AsyncProducts, Products + from .products_entitlements import AsyncProductsEntitlements, ProductsEntitlements from .subscriptions import AsyncSubscriptions, Subscriptions from .usage import AsyncUsage, Usage @@ -95,6 +98,8 @@ _MODULES: dict[str, str] = { "AsyncAddOns": "add_ons", "AddOns": "add_ons", + "AsyncAddOnsEntitlements": "add_ons_entitlements", + "AddOnsEntitlements": "add_ons_entitlements", "AsyncBatchJobs": "batch_jobs", "BatchJobs": "batch_jobs", "AsyncCheckoutSessions": "checkout_sessions", @@ -125,10 +130,14 @@ "OauthApps": "oauth_apps", "AsyncPlans": "plans", "Plans": "plans", + "AsyncPlansVersions": "plans_versions", + "PlansVersions": "plans_versions", "AsyncProductFamilies": "product_families", "ProductFamilies": "product_families", "AsyncProducts": "products", "Products": "products", + "AsyncProductsEntitlements": "products_entitlements", + "ProductsEntitlements": "products_entitlements", "AsyncSubscriptions": "subscriptions", "Subscriptions": "subscriptions", "AsyncUsage": "usage", @@ -155,8 +164,8 @@ "MetricsListPage": "_pages", "AsyncPlansListPage": "_pages", "PlansListPage": "_pages", - "AsyncPlansListVersionsPage": "_pages", - "PlansListVersionsPage": "_pages", + "AsyncPlansVersionsListPage": "_pages", + "PlansVersionsListPage": "_pages", "AsyncProductFamiliesListPage": "_pages", "ProductFamiliesListPage": "_pages", "AsyncProductsListPage": "_pages", @@ -209,6 +218,8 @@ def __dir__() -> list[str]: "UnprocessableEntityError", "AsyncAddOns", "AddOns", + "AsyncAddOnsEntitlements", + "AddOnsEntitlements", "AsyncBatchJobs", "BatchJobs", "AsyncCheckoutSessions", @@ -239,10 +250,14 @@ def __dir__() -> list[str]: "OauthApps", "AsyncPlans", "Plans", + "AsyncPlansVersions", + "PlansVersions", "AsyncProductFamilies", "ProductFamilies", "AsyncProducts", "Products", + "AsyncProductsEntitlements", + "ProductsEntitlements", "AsyncSubscriptions", "Subscriptions", "AsyncUsage", @@ -269,8 +284,8 @@ def __dir__() -> list[str]: "MetricsListPage", "AsyncPlansListPage", "PlansListPage", - "AsyncPlansListVersionsPage", - "PlansListVersionsPage", + "AsyncPlansVersionsListPage", + "PlansVersionsListPage", "AsyncProductFamiliesListPage", "ProductFamiliesListPage", "AsyncProductsListPage", diff --git a/meteroid/api/_pages.py b/meteroid/api/_pages.py index e717de6..d1026d2 100644 --- a/meteroid/api/_pages.py +++ b/meteroid/api/_pages.py @@ -60,8 +60,8 @@ "MetricsListPage", "AsyncPlansListPage", "PlansListPage", - "AsyncPlansListVersionsPage", - "PlansListVersionsPage", + "AsyncPlansVersionsListPage", + "PlansVersionsListPage", "AsyncProductFamiliesListPage", "ProductFamiliesListPage", "AsyncProductsListPage", @@ -187,13 +187,13 @@ class AsyncPlansListPage(AsyncPage[Plan, PlanListResponse], PlanListResponse): """A page of :class:`Plan`, and the :class:`PlanListResponse` response of its request.""" -class PlansListVersionsPage( +class PlansVersionsListPage( SyncPage[PlanVersionSummary, PlanVersionListResponse], PlanVersionListResponse ): """A page of :class:`PlanVersionSummary`, and the :class:`PlanVersionListResponse` response of its request.""" -class AsyncPlansListVersionsPage( +class AsyncPlansVersionsListPage( AsyncPage[PlanVersionSummary, PlanVersionListResponse], PlanVersionListResponse ): """A page of :class:`PlanVersionSummary`, and the :class:`PlanVersionListResponse` response of its request.""" diff --git a/meteroid/api/_pagination.py b/meteroid/api/_pagination.py index 743ada5..6742872 100644 --- a/meteroid/api/_pagination.py +++ b/meteroid/api/_pagination.py @@ -73,10 +73,12 @@ def _more(self, body: _Body, items: t.Sequence[_Item]) -> bool: @dataclasses.dataclass(frozen=True) class CursorPaging(Paging[_Body, _Item, str]): - """The next page starts after a cursor: one the page gives, or the last item's.""" + """The next page starts after a cursor: one the page gives, or the last item's. Paging + ``backwards``, it ends before the first item's.""" next_cursor: t.Callable[[_Body], str | Unset | None] | None = None item_cursor: t.Callable[[_Item], str | Unset | None] | None = None + backwards: bool = False def next( self, param: str | None, body: _Body, items: t.Sequence[_Item] @@ -86,7 +88,7 @@ def next( if self.next_cursor is not None: cursor = self.next_cursor(body) elif self.item_cursor is not None: - cursor = self.item_cursor(items[-1]) + cursor = self.item_cursor(items[0] if self.backwards else items[-1]) else: cursor = None return ( diff --git a/meteroid/api/add_ons.py b/meteroid/api/add_ons.py index e35da4b..bf983f5 100644 --- a/meteroid/api/add_ons.py +++ b/meteroid/api/add_ons.py @@ -3,7 +3,7 @@ from __future__ import annotations -import builtins +import functools import typing as t from .. import models as _models @@ -11,12 +11,8 @@ AddOn, AddOnListResponse, CreateAddOnRequest, - CreateEntitlementsRequest, - EntitlementListResponse, - EntitlementSpecRequest, PriceId, ProductId, - ResolvedEntitlementListResponse, UpdateAddOnRequest, ) from ..serialization import UNSET, Unset, to_json_value @@ -30,6 +26,12 @@ step, ) from ._response import async_to_raw_response_wrapper, to_raw_response_wrapper +from .add_ons_entitlements import ( + AddOnsEntitlements, + AddOnsEntitlementsWithRawResponse, + AsyncAddOnsEntitlements, + AsyncAddOnsEntitlementsWithRawResponse, +) from .common import ( ApiBaseAsync, ApiBaseSync, @@ -48,6 +50,11 @@ def with_raw_response(self) -> AsyncAddOnsWithRawResponse: """These methods, returning an :class:`APIResponse` with the status and headers.""" return AsyncAddOnsWithRawResponse(self) + @functools.cached_property + def entitlements(self) -> AsyncAddOnsEntitlements: + """The entitlements API.""" + return AsyncAddOnsEntitlements(self._cfg, self._httpx_client) + def list( self, *, @@ -253,75 +260,6 @@ async def archive( ) ) - async def list_entitlements( - self, - addon_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, - ) -> ResolvedEntitlementListResponse: - """List add-on entitlements""" - response = await self._request( - ApiRequest( - method="get", - path="/api/v1/addons/{addon_id}/entitlements", - path_params={ - "addon_id": addon_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, ResolvedEntitlementListResponse) - - async def create_entitlement( - self, - addon_id: str, - *, - entitlements: builtins.list[EntitlementSpecRequest], - 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, - ) -> EntitlementListResponse: - """Create add-on entitlements - - Entitlements already present on this add-on are skipped.""" - response = await self._request( - ApiRequest( - method="post", - path="/api/v1/addons/{addon_id}/entitlements", - path_params={ - "addon_id": addon_id, - }, - json_body=to_json_value( - CreateEntitlementsRequest( - entitlements=entitlements, - ), - CreateEntitlementsRequest, - ), - 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, EntitlementListResponse) - async def unarchive( self, addon_id: str, @@ -356,19 +294,19 @@ class AsyncAddOnsWithRawResponse: """The methods of :class:`AsyncAddOns`, returning an :class:`APIResponse`.""" def __init__(self, resource: AsyncAddOns) -> None: + self._resource = resource 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.update = async_to_raw_response_wrapper(resource.update) self.archive = async_to_raw_response_wrapper(resource.archive) - self.list_entitlements = async_to_raw_response_wrapper( - resource.list_entitlements - ) - self.create_entitlement = async_to_raw_response_wrapper( - resource.create_entitlement - ) self.unarchive = async_to_raw_response_wrapper(resource.unarchive) + @property + def entitlements(self) -> AsyncAddOnsEntitlementsWithRawResponse: + """The entitlements API.""" + return AsyncAddOnsEntitlementsWithRawResponse(self._resource.entitlements) + class AddOns(ApiBaseSync): """Add ons API.""" @@ -378,6 +316,11 @@ def with_raw_response(self) -> AddOnsWithRawResponse: """These methods, returning an :class:`APIResponse` with the status and headers.""" return AddOnsWithRawResponse(self) + @functools.cached_property + def entitlements(self) -> AddOnsEntitlements: + """The entitlements API.""" + return AddOnsEntitlements(self._cfg, self._httpx_client) + def list( self, *, @@ -583,75 +526,6 @@ def archive( ) ) - def list_entitlements( - self, - addon_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, - ) -> ResolvedEntitlementListResponse: - """List add-on entitlements""" - response = self._request( - ApiRequest( - method="get", - path="/api/v1/addons/{addon_id}/entitlements", - path_params={ - "addon_id": addon_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, ResolvedEntitlementListResponse) - - def create_entitlement( - self, - addon_id: str, - *, - entitlements: builtins.list[EntitlementSpecRequest], - 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, - ) -> EntitlementListResponse: - """Create add-on entitlements - - Entitlements already present on this add-on are skipped.""" - response = self._request( - ApiRequest( - method="post", - path="/api/v1/addons/{addon_id}/entitlements", - path_params={ - "addon_id": addon_id, - }, - json_body=to_json_value( - CreateEntitlementsRequest( - entitlements=entitlements, - ), - CreateEntitlementsRequest, - ), - 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, EntitlementListResponse) - def unarchive( self, addon_id: str, @@ -686,11 +560,15 @@ class AddOnsWithRawResponse: """The methods of :class:`AddOns`, returning an :class:`APIResponse`.""" def __init__(self, resource: AddOns) -> None: + self._resource = resource 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.update = to_raw_response_wrapper(resource.update) self.archive = to_raw_response_wrapper(resource.archive) - self.list_entitlements = to_raw_response_wrapper(resource.list_entitlements) - self.create_entitlement = to_raw_response_wrapper(resource.create_entitlement) self.unarchive = to_raw_response_wrapper(resource.unarchive) + + @property + def entitlements(self) -> AddOnsEntitlementsWithRawResponse: + """The entitlements API.""" + return AddOnsEntitlementsWithRawResponse(self._resource.entitlements) diff --git a/meteroid/api/add_ons_entitlements.py b/meteroid/api/add_ons_entitlements.py new file mode 100644 index 0000000..e200069 --- /dev/null +++ b/meteroid/api/add_ons_entitlements.py @@ -0,0 +1,196 @@ +# this file is @generated +"""Add ons entitlements API.""" + +from __future__ import annotations + +import builtins +import typing as t + +from .. import models as _models +from ..models import ( + CreateEntitlementsRequest, + EntitlementListResponse, + EntitlementSpecRequest, + ResolvedEntitlementListResponse, +) +from ..serialization import UNSET, Unset, to_json_value +from ._response import async_to_raw_response_wrapper, to_raw_response_wrapper +from .common import ( + ApiBaseAsync, + ApiBaseSync, + ApiRequest, + Timeout, + decode_response, +) + + +class AsyncAddOnsEntitlements(ApiBaseAsync): + """Add ons entitlements API, for asyncio.""" + + @property + def with_raw_response(self) -> AsyncAddOnsEntitlementsWithRawResponse: + """These methods, returning an :class:`APIResponse` with the status and headers.""" + return AsyncAddOnsEntitlementsWithRawResponse(self) + + async def list( + self, + addon_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, + ) -> ResolvedEntitlementListResponse: + """List add-on entitlements""" + response = await self._request( + ApiRequest( + method="get", + path="/api/v1/addons/{addon_id}/entitlements", + path_params={ + "addon_id": addon_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, ResolvedEntitlementListResponse) + + async def create( + self, + addon_id: str, + *, + entitlements: builtins.list[EntitlementSpecRequest], + 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, + ) -> EntitlementListResponse: + """Create add-on entitlements + + Entitlements already present on this add-on are skipped.""" + response = await self._request( + ApiRequest( + method="post", + path="/api/v1/addons/{addon_id}/entitlements", + path_params={ + "addon_id": addon_id, + }, + json_body=to_json_value( + CreateEntitlementsRequest( + entitlements=entitlements, + ), + CreateEntitlementsRequest, + ), + 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, EntitlementListResponse) + + +class AsyncAddOnsEntitlementsWithRawResponse: + """The methods of :class:`AsyncAddOnsEntitlements`, returning an :class:`APIResponse`.""" + + def __init__(self, resource: AsyncAddOnsEntitlements) -> None: + self.list = async_to_raw_response_wrapper(resource.list) + self.create = async_to_raw_response_wrapper(resource.create) + + +class AddOnsEntitlements(ApiBaseSync): + """Add ons entitlements API.""" + + @property + def with_raw_response(self) -> AddOnsEntitlementsWithRawResponse: + """These methods, returning an :class:`APIResponse` with the status and headers.""" + return AddOnsEntitlementsWithRawResponse(self) + + def list( + self, + addon_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, + ) -> ResolvedEntitlementListResponse: + """List add-on entitlements""" + response = self._request( + ApiRequest( + method="get", + path="/api/v1/addons/{addon_id}/entitlements", + path_params={ + "addon_id": addon_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, ResolvedEntitlementListResponse) + + def create( + self, + addon_id: str, + *, + entitlements: builtins.list[EntitlementSpecRequest], + 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, + ) -> EntitlementListResponse: + """Create add-on entitlements + + Entitlements already present on this add-on are skipped.""" + response = self._request( + ApiRequest( + method="post", + path="/api/v1/addons/{addon_id}/entitlements", + path_params={ + "addon_id": addon_id, + }, + json_body=to_json_value( + CreateEntitlementsRequest( + entitlements=entitlements, + ), + CreateEntitlementsRequest, + ), + 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, EntitlementListResponse) + + +class AddOnsEntitlementsWithRawResponse: + """The methods of :class:`AddOnsEntitlements`, returning an :class:`APIResponse`.""" + + def __init__(self, resource: AddOnsEntitlements) -> None: + self.list = to_raw_response_wrapper(resource.list) + self.create = to_raw_response_wrapper(resource.create) diff --git a/meteroid/api/client.py b/meteroid/api/client.py index 4021d48..e4a8b7f 100644 --- a/meteroid/api/client.py +++ b/meteroid/api/client.py @@ -4,6 +4,7 @@ from __future__ import annotations import dataclasses +import functools import os import typing as t @@ -283,140 +284,140 @@ def __enter__(self) -> Meteroid: def __exit__(self, *exc_info: object) -> None: self.close() - @property + @functools.cached_property def add_ons(self) -> AddOns: """The add ons API.""" from .add_ons import AddOns return AddOns(self._cfg, self._httpx_client) - @property + @functools.cached_property def batch_jobs(self) -> BatchJobs: """The batch jobs API.""" from .batch_jobs import BatchJobs return BatchJobs(self._cfg, self._httpx_client) - @property + @functools.cached_property def checkout_sessions(self) -> CheckoutSessions: """The checkout sessions API.""" from .checkout_sessions import CheckoutSessions return CheckoutSessions(self._cfg, self._httpx_client) - @property + @functools.cached_property def connect(self) -> Connect: """The connect API.""" from .connect import Connect return Connect(self._cfg, self._httpx_client) - @property + @functools.cached_property def coupons(self) -> Coupons: """The coupons API.""" from .coupons import Coupons return Coupons(self._cfg, self._httpx_client) - @property + @functools.cached_property def credit_notes(self) -> CreditNotes: """The credit notes API.""" from .credit_notes import CreditNotes return CreditNotes(self._cfg, self._httpx_client) - @property + @functools.cached_property def custom_properties(self) -> CustomProperties: """The custom properties API.""" from .custom_properties import CustomProperties return CustomProperties(self._cfg, self._httpx_client) - @property + @functools.cached_property def customers(self) -> Customers: """The customers API.""" from .customers import Customers return Customers(self._cfg, self._httpx_client) - @property + @functools.cached_property def entitlements(self) -> Entitlements: """The entitlements API.""" from .entitlements import Entitlements return Entitlements(self._cfg, self._httpx_client) - @property + @functools.cached_property def events(self) -> Events: """The events API.""" from .events import Events return Events(self._cfg, self._httpx_client) - @property + @functools.cached_property def features(self) -> Features: """The features API.""" from .features import Features return Features(self._cfg, self._httpx_client) - @property + @functools.cached_property def invoices(self) -> Invoices: """The invoices API.""" from .invoices import Invoices return Invoices(self._cfg, self._httpx_client) - @property + @functools.cached_property def metrics(self) -> Metrics: """The metrics API.""" from .metrics import Metrics return Metrics(self._cfg, self._httpx_client) - @property + @functools.cached_property def oauth(self) -> Oauth: """The oauth API.""" from .oauth import Oauth return Oauth(self._cfg, self._httpx_client) - @property + @functools.cached_property def oauth_apps(self) -> OauthApps: """The oauth apps API.""" from .oauth_apps import OauthApps return OauthApps(self._cfg, self._httpx_client) - @property + @functools.cached_property def plans(self) -> Plans: """The plans API.""" from .plans import Plans return Plans(self._cfg, self._httpx_client) - @property + @functools.cached_property def product_families(self) -> ProductFamilies: """The product families API.""" from .product_families import ProductFamilies return ProductFamilies(self._cfg, self._httpx_client) - @property + @functools.cached_property def products(self) -> Products: """The products API.""" from .products import Products return Products(self._cfg, self._httpx_client) - @property + @functools.cached_property def subscriptions(self) -> Subscriptions: """The subscriptions API.""" from .subscriptions import Subscriptions return Subscriptions(self._cfg, self._httpx_client) - @property + @functools.cached_property def usage(self) -> Usage: """The usage API.""" from .usage import Usage @@ -677,140 +678,140 @@ async def __aenter__(self) -> AsyncMeteroid: async def __aexit__(self, *exc_info: object) -> None: await self.aclose() - @property + @functools.cached_property def add_ons(self) -> AsyncAddOns: """The add ons API.""" from .add_ons import AsyncAddOns return AsyncAddOns(self._cfg, self._httpx_client) - @property + @functools.cached_property def batch_jobs(self) -> AsyncBatchJobs: """The batch jobs API.""" from .batch_jobs import AsyncBatchJobs return AsyncBatchJobs(self._cfg, self._httpx_client) - @property + @functools.cached_property def checkout_sessions(self) -> AsyncCheckoutSessions: """The checkout sessions API.""" from .checkout_sessions import AsyncCheckoutSessions return AsyncCheckoutSessions(self._cfg, self._httpx_client) - @property + @functools.cached_property def connect(self) -> AsyncConnect: """The connect API.""" from .connect import AsyncConnect return AsyncConnect(self._cfg, self._httpx_client) - @property + @functools.cached_property def coupons(self) -> AsyncCoupons: """The coupons API.""" from .coupons import AsyncCoupons return AsyncCoupons(self._cfg, self._httpx_client) - @property + @functools.cached_property def credit_notes(self) -> AsyncCreditNotes: """The credit notes API.""" from .credit_notes import AsyncCreditNotes return AsyncCreditNotes(self._cfg, self._httpx_client) - @property + @functools.cached_property def custom_properties(self) -> AsyncCustomProperties: """The custom properties API.""" from .custom_properties import AsyncCustomProperties return AsyncCustomProperties(self._cfg, self._httpx_client) - @property + @functools.cached_property def customers(self) -> AsyncCustomers: """The customers API.""" from .customers import AsyncCustomers return AsyncCustomers(self._cfg, self._httpx_client) - @property + @functools.cached_property def entitlements(self) -> AsyncEntitlements: """The entitlements API.""" from .entitlements import AsyncEntitlements return AsyncEntitlements(self._cfg, self._httpx_client) - @property + @functools.cached_property def events(self) -> AsyncEvents: """The events API.""" from .events import AsyncEvents return AsyncEvents(self._cfg, self._httpx_client) - @property + @functools.cached_property def features(self) -> AsyncFeatures: """The features API.""" from .features import AsyncFeatures return AsyncFeatures(self._cfg, self._httpx_client) - @property + @functools.cached_property def invoices(self) -> AsyncInvoices: """The invoices API.""" from .invoices import AsyncInvoices return AsyncInvoices(self._cfg, self._httpx_client) - @property + @functools.cached_property def metrics(self) -> AsyncMetrics: """The metrics API.""" from .metrics import AsyncMetrics return AsyncMetrics(self._cfg, self._httpx_client) - @property + @functools.cached_property def oauth(self) -> AsyncOauth: """The oauth API.""" from .oauth import AsyncOauth return AsyncOauth(self._cfg, self._httpx_client) - @property + @functools.cached_property def oauth_apps(self) -> AsyncOauthApps: """The oauth apps API.""" from .oauth_apps import AsyncOauthApps return AsyncOauthApps(self._cfg, self._httpx_client) - @property + @functools.cached_property def plans(self) -> AsyncPlans: """The plans API.""" from .plans import AsyncPlans return AsyncPlans(self._cfg, self._httpx_client) - @property + @functools.cached_property def product_families(self) -> AsyncProductFamilies: """The product families API.""" from .product_families import AsyncProductFamilies return AsyncProductFamilies(self._cfg, self._httpx_client) - @property + @functools.cached_property def products(self) -> AsyncProducts: """The products API.""" from .products import AsyncProducts return AsyncProducts(self._cfg, self._httpx_client) - @property + @functools.cached_property def subscriptions(self) -> AsyncSubscriptions: """The subscriptions API.""" from .subscriptions import AsyncSubscriptions return AsyncSubscriptions(self._cfg, self._httpx_client) - @property + @functools.cached_property def usage(self) -> AsyncUsage: """The usage API.""" from .usage import AsyncUsage diff --git a/meteroid/api/plans.py b/meteroid/api/plans.py index e63a85e..3ca3af6 100644 --- a/meteroid/api/plans.py +++ b/meteroid/api/plans.py @@ -4,6 +4,7 @@ from __future__ import annotations import builtins +import functools import typing as t from .. import models as _models @@ -13,9 +14,7 @@ CreatePlanRequest, EntitlementListResponse, EntitlementSpecRequest, - MinimumCommitment, MinimumCommitmentInput, - MinimumCommitmentScope, PatchPlanRequest, Plan, PlanAddOnInput, @@ -24,8 +23,6 @@ PlanStatusEnumLiteral, PlanTypeEnum, PlanTypeEnumLiteral, - PlanVersionListResponse, - PlanVersionSummary, PriceComponentInput, ProductFamilyId, ReplacePlanRequest, @@ -35,9 +32,7 @@ from ..serialization import UNSET, Unset, to_json_value from ._pages import ( AsyncPlansListPage, - AsyncPlansListVersionsPage, PlansListPage, - PlansListVersionsPage, ) from ._pagination import ( AsyncPaginator, @@ -53,6 +48,12 @@ decode_response, serialize_query_params, ) +from .plans_versions import ( + AsyncPlansVersions, + AsyncPlansVersionsWithRawResponse, + PlansVersions, + PlansVersionsWithRawResponse, +) class AsyncPlans(ApiBaseAsync): @@ -63,6 +64,11 @@ def with_raw_response(self) -> AsyncPlansWithRawResponse: """These methods, returning an :class:`APIResponse` with the status and headers.""" return AsyncPlansWithRawResponse(self) + @functools.cached_property + def versions(self) -> AsyncPlansVersions: + """The versions API.""" + return AsyncPlansVersions(self._cfg, self._httpx_client) + async def list_plan_version_entitlements( self, plan_version_id: str, @@ -257,76 +263,6 @@ async def create( ) return decode_response(response, Plan) - async def update_version_minimum( - self, - plan_version_id: str, - *, - amount: str, - scope: MinimumCommitmentScope, - 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, - ) -> MinimumCommitment: - """Set or replace the plan-level minimum commitment for a draft plan version. - - :param amount: Decimal string in the plan currency, e.g. "100.00".""" - response = await self._request( - ApiRequest( - method="put", - path="/api/v1/plans/versions/{plan_version_id}/minimum", - path_params={ - "plan_version_id": plan_version_id, - }, - json_body=to_json_value( - MinimumCommitment( - amount=amount, - scope=scope, - ), - MinimumCommitment, - ), - 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, MinimumCommitment) - - async def delete_version_minimum( - self, - plan_version_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: - """Remove the plan-level minimum commitment for a draft plan version.""" - await self._request( - ApiRequest( - method="delete", - path="/api/v1/plans/versions/{plan_version_id}/minimum", - path_params={ - "plan_version_id": plan_version_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 retrieve( self, plan_id: str, @@ -566,65 +502,12 @@ async def unarchive( ) ) - def list_versions( - self, - plan_id: str, - *, - 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[PlanVersionSummary, AsyncPlansListVersionsPage]: - """List plan versions - - :param page: Page number (0-indexed) - :param per_page: Number of items per page""" - - async def fetch(page_param: int | None) -> PlanVersionListResponse: - response = await self._request( - ApiRequest( - method="get", - path="/api/v1/plans/{plan_id}/versions", - path_params={ - "plan_id": plan_id, - }, - query_params=serialize_query_params( - { - "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, PlanVersionListResponse) - - paging = PagePaging[PlanVersionListResponse, PlanVersionSummary]( - items=lambda body: body.data, - total_pages=lambda body: step( - body.pagination_meta, lambda v: v.total_pages - ), - first_page=0, - ) - return AsyncPaginator( - lambda: AsyncPlansListVersionsPage._first(fetch, page, paging) - ) - class AsyncPlansWithRawResponse: """The methods of :class:`AsyncPlans`, returning an :class:`APIResponse`.""" def __init__(self, resource: AsyncPlans) -> None: + self._resource = resource self.list_plan_version_entitlements = async_to_raw_response_wrapper( resource.list_plan_version_entitlements ) @@ -633,19 +516,17 @@ def __init__(self, resource: AsyncPlans) -> None: ) self.list = async_to_raw_response_wrapper(resource.list) self.create = async_to_raw_response_wrapper(resource.create) - self.update_version_minimum = async_to_raw_response_wrapper( - resource.update_version_minimum - ) - self.delete_version_minimum = async_to_raw_response_wrapper( - resource.delete_version_minimum - ) self.retrieve = async_to_raw_response_wrapper(resource.retrieve) self.replace = async_to_raw_response_wrapper(resource.replace) self.update = async_to_raw_response_wrapper(resource.update) self.archive = async_to_raw_response_wrapper(resource.archive) self.publish = async_to_raw_response_wrapper(resource.publish) self.unarchive = async_to_raw_response_wrapper(resource.unarchive) - self.list_versions = async_to_raw_response_wrapper(resource.list_versions) + + @property + def versions(self) -> AsyncPlansVersionsWithRawResponse: + """The versions API.""" + return AsyncPlansVersionsWithRawResponse(self._resource.versions) class Plans(ApiBaseSync): @@ -656,6 +537,11 @@ def with_raw_response(self) -> PlansWithRawResponse: """These methods, returning an :class:`APIResponse` with the status and headers.""" return PlansWithRawResponse(self) + @functools.cached_property + def versions(self) -> PlansVersions: + """The versions API.""" + return PlansVersions(self._cfg, self._httpx_client) + def list_plan_version_entitlements( self, plan_version_id: str, @@ -850,76 +736,6 @@ def create( ) return decode_response(response, Plan) - def update_version_minimum( - self, - plan_version_id: str, - *, - amount: str, - scope: MinimumCommitmentScope, - 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, - ) -> MinimumCommitment: - """Set or replace the plan-level minimum commitment for a draft plan version. - - :param amount: Decimal string in the plan currency, e.g. "100.00".""" - response = self._request( - ApiRequest( - method="put", - path="/api/v1/plans/versions/{plan_version_id}/minimum", - path_params={ - "plan_version_id": plan_version_id, - }, - json_body=to_json_value( - MinimumCommitment( - amount=amount, - scope=scope, - ), - MinimumCommitment, - ), - 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, MinimumCommitment) - - def delete_version_minimum( - self, - plan_version_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: - """Remove the plan-level minimum commitment for a draft plan version.""" - self._request( - ApiRequest( - method="delete", - path="/api/v1/plans/versions/{plan_version_id}/minimum", - path_params={ - "plan_version_id": plan_version_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 retrieve( self, plan_id: str, @@ -1159,63 +975,12 @@ def unarchive( ) ) - def list_versions( - self, - plan_id: str, - *, - 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, - ) -> PlansListVersionsPage: - """List plan versions - - :param page: Page number (0-indexed) - :param per_page: Number of items per page""" - - def fetch(page_param: int | None) -> PlanVersionListResponse: - response = self._request( - ApiRequest( - method="get", - path="/api/v1/plans/{plan_id}/versions", - path_params={ - "plan_id": plan_id, - }, - query_params=serialize_query_params( - { - "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, PlanVersionListResponse) - - paging = PagePaging[PlanVersionListResponse, PlanVersionSummary]( - items=lambda body: body.data, - total_pages=lambda body: step( - body.pagination_meta, lambda v: v.total_pages - ), - first_page=0, - ) - return PlansListVersionsPage._first(fetch, page, paging) - class PlansWithRawResponse: """The methods of :class:`Plans`, returning an :class:`APIResponse`.""" def __init__(self, resource: Plans) -> None: + self._resource = resource self.list_plan_version_entitlements = to_raw_response_wrapper( resource.list_plan_version_entitlements ) @@ -1224,16 +989,14 @@ def __init__(self, resource: Plans) -> None: ) self.list = to_raw_response_wrapper(resource.list) self.create = to_raw_response_wrapper(resource.create) - self.update_version_minimum = to_raw_response_wrapper( - resource.update_version_minimum - ) - self.delete_version_minimum = to_raw_response_wrapper( - resource.delete_version_minimum - ) self.retrieve = to_raw_response_wrapper(resource.retrieve) self.replace = to_raw_response_wrapper(resource.replace) self.update = to_raw_response_wrapper(resource.update) self.archive = to_raw_response_wrapper(resource.archive) self.publish = to_raw_response_wrapper(resource.publish) self.unarchive = to_raw_response_wrapper(resource.unarchive) - self.list_versions = to_raw_response_wrapper(resource.list_versions) + + @property + def versions(self) -> PlansVersionsWithRawResponse: + """The versions API.""" + return PlansVersionsWithRawResponse(self._resource.versions) diff --git a/meteroid/api/plans_versions.py b/meteroid/api/plans_versions.py new file mode 100644 index 0000000..ccbb40c --- /dev/null +++ b/meteroid/api/plans_versions.py @@ -0,0 +1,315 @@ +# this file is @generated +"""Plans versions API.""" + +from __future__ import annotations + +import typing as t + +from .. import models as _models +from ..models import ( + MinimumCommitment, + MinimumCommitmentScope, + PlanVersionListResponse, + PlanVersionSummary, +) +from ..serialization import UNSET, Unset, to_json_value +from ._pages import ( + AsyncPlansVersionsListPage, + PlansVersionsListPage, +) +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 AsyncPlansVersions(ApiBaseAsync): + """Plans versions API, for asyncio.""" + + @property + def with_raw_response(self) -> AsyncPlansVersionsWithRawResponse: + """These methods, returning an :class:`APIResponse` with the status and headers.""" + return AsyncPlansVersionsWithRawResponse(self) + + async def update_minimum( + self, + plan_version_id: str, + *, + amount: str, + scope: MinimumCommitmentScope, + 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, + ) -> MinimumCommitment: + """Set or replace the plan-level minimum commitment for a draft plan version. + + :param amount: Decimal string in the plan currency, e.g. "100.00".""" + response = await self._request( + ApiRequest( + method="put", + path="/api/v1/plans/versions/{plan_version_id}/minimum", + path_params={ + "plan_version_id": plan_version_id, + }, + json_body=to_json_value( + MinimumCommitment( + amount=amount, + scope=scope, + ), + MinimumCommitment, + ), + 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, MinimumCommitment) + + async def delete_minimum( + self, + plan_version_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: + """Remove the plan-level minimum commitment for a draft plan version.""" + await self._request( + ApiRequest( + method="delete", + path="/api/v1/plans/versions/{plan_version_id}/minimum", + path_params={ + "plan_version_id": plan_version_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 list( + self, + plan_id: str, + *, + 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[PlanVersionSummary, AsyncPlansVersionsListPage]: + """List plan versions + + :param page: Page number (0-indexed) + :param per_page: Number of items per page""" + + async def fetch(page_param: int | None) -> PlanVersionListResponse: + response = await self._request( + ApiRequest( + method="get", + path="/api/v1/plans/{plan_id}/versions", + path_params={ + "plan_id": plan_id, + }, + query_params=serialize_query_params( + { + "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, PlanVersionListResponse) + + paging = PagePaging[PlanVersionListResponse, PlanVersionSummary]( + items=lambda body: body.data, + total_pages=lambda body: step( + body.pagination_meta, lambda v: v.total_pages + ), + first_page=0, + ) + return AsyncPaginator( + lambda: AsyncPlansVersionsListPage._first(fetch, page, paging) + ) + + +class AsyncPlansVersionsWithRawResponse: + """The methods of :class:`AsyncPlansVersions`, returning an :class:`APIResponse`.""" + + def __init__(self, resource: AsyncPlansVersions) -> None: + self.update_minimum = async_to_raw_response_wrapper(resource.update_minimum) + self.delete_minimum = async_to_raw_response_wrapper(resource.delete_minimum) + self.list = async_to_raw_response_wrapper(resource.list) + + +class PlansVersions(ApiBaseSync): + """Plans versions API.""" + + @property + def with_raw_response(self) -> PlansVersionsWithRawResponse: + """These methods, returning an :class:`APIResponse` with the status and headers.""" + return PlansVersionsWithRawResponse(self) + + def update_minimum( + self, + plan_version_id: str, + *, + amount: str, + scope: MinimumCommitmentScope, + 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, + ) -> MinimumCommitment: + """Set or replace the plan-level minimum commitment for a draft plan version. + + :param amount: Decimal string in the plan currency, e.g. "100.00".""" + response = self._request( + ApiRequest( + method="put", + path="/api/v1/plans/versions/{plan_version_id}/minimum", + path_params={ + "plan_version_id": plan_version_id, + }, + json_body=to_json_value( + MinimumCommitment( + amount=amount, + scope=scope, + ), + MinimumCommitment, + ), + 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, MinimumCommitment) + + def delete_minimum( + self, + plan_version_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: + """Remove the plan-level minimum commitment for a draft plan version.""" + self._request( + ApiRequest( + method="delete", + path="/api/v1/plans/versions/{plan_version_id}/minimum", + path_params={ + "plan_version_id": plan_version_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 list( + self, + plan_id: str, + *, + 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, + ) -> PlansVersionsListPage: + """List plan versions + + :param page: Page number (0-indexed) + :param per_page: Number of items per page""" + + def fetch(page_param: int | None) -> PlanVersionListResponse: + response = self._request( + ApiRequest( + method="get", + path="/api/v1/plans/{plan_id}/versions", + path_params={ + "plan_id": plan_id, + }, + query_params=serialize_query_params( + { + "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, PlanVersionListResponse) + + paging = PagePaging[PlanVersionListResponse, PlanVersionSummary]( + items=lambda body: body.data, + total_pages=lambda body: step( + body.pagination_meta, lambda v: v.total_pages + ), + first_page=0, + ) + return PlansVersionsListPage._first(fetch, page, paging) + + +class PlansVersionsWithRawResponse: + """The methods of :class:`PlansVersions`, returning an :class:`APIResponse`.""" + + def __init__(self, resource: PlansVersions) -> None: + self.update_minimum = to_raw_response_wrapper(resource.update_minimum) + self.delete_minimum = to_raw_response_wrapper(resource.delete_minimum) + self.list = to_raw_response_wrapper(resource.list) diff --git a/meteroid/api/products.py b/meteroid/api/products.py index 38adea5..085a0e5 100644 --- a/meteroid/api/products.py +++ b/meteroid/api/products.py @@ -3,20 +3,16 @@ from __future__ import annotations -import builtins +import functools import typing as t from .. import models as _models from ..models import ( - CreateEntitlementsRequest, CreateProductRequest, - EntitlementListResponse, - EntitlementSpecRequest, Product, ProductFamilyId, ProductFeeStructure, ProductListResponse, - ResolvedEntitlementListResponse, UpdateProductRequest, ) from ..serialization import UNSET, Unset, to_json_value @@ -38,6 +34,12 @@ decode_response, serialize_query_params, ) +from .products_entitlements import ( + AsyncProductsEntitlements, + AsyncProductsEntitlementsWithRawResponse, + ProductsEntitlements, + ProductsEntitlementsWithRawResponse, +) class AsyncProducts(ApiBaseAsync): @@ -48,6 +50,11 @@ def with_raw_response(self) -> AsyncProductsWithRawResponse: """These methods, returning an :class:`APIResponse` with the status and headers.""" return AsyncProductsWithRawResponse(self) + @functools.cached_property + def entitlements(self) -> AsyncProductsEntitlements: + """The entitlements API.""" + return AsyncProductsEntitlements(self._cfg, self._httpx_client) + def list( self, *, @@ -246,81 +253,6 @@ async def archive( ) ) - async def list_entitlements( - self, - product_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, - ) -> ResolvedEntitlementListResponse: - """List product entitlements""" - response = await self._request( - ApiRequest( - method="get", - path="/api/v1/products/{product_id}/entitlements", - path_params={ - "product_id": product_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, ResolvedEntitlementListResponse) - - async def create_entitlement( - self, - product_id: str, - *, - entitlements: builtins.list[EntitlementSpecRequest], - 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, - ) -> EntitlementListResponse: - """Create product entitlements - - A product has no entitlement rows of its own: its entitlements are the feature-level - defaults of the features scoped to it, which is what `GET` on this path resolves. Every - spec must therefore target a feature belonging to `product_id`. Features that already - carry a default entitlement are skipped. - - Specs are validated up front, but the writes are not atomic: each feature is written on - its own, so a failure part-way can leave earlier specs committed. Retrying is safe.""" - response = await self._request( - ApiRequest( - method="post", - path="/api/v1/products/{product_id}/entitlements", - path_params={ - "product_id": product_id, - }, - json_body=to_json_value( - CreateEntitlementsRequest( - entitlements=entitlements, - ), - CreateEntitlementsRequest, - ), - 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, EntitlementListResponse) - async def unarchive( self, product_id: str, @@ -355,19 +287,19 @@ class AsyncProductsWithRawResponse: """The methods of :class:`AsyncProducts`, returning an :class:`APIResponse`.""" def __init__(self, resource: AsyncProducts) -> None: + self._resource = resource 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.update = async_to_raw_response_wrapper(resource.update) self.archive = async_to_raw_response_wrapper(resource.archive) - self.list_entitlements = async_to_raw_response_wrapper( - resource.list_entitlements - ) - self.create_entitlement = async_to_raw_response_wrapper( - resource.create_entitlement - ) self.unarchive = async_to_raw_response_wrapper(resource.unarchive) + @property + def entitlements(self) -> AsyncProductsEntitlementsWithRawResponse: + """The entitlements API.""" + return AsyncProductsEntitlementsWithRawResponse(self._resource.entitlements) + class Products(ApiBaseSync): """Products API.""" @@ -377,6 +309,11 @@ def with_raw_response(self) -> ProductsWithRawResponse: """These methods, returning an :class:`APIResponse` with the status and headers.""" return ProductsWithRawResponse(self) + @functools.cached_property + def entitlements(self) -> ProductsEntitlements: + """The entitlements API.""" + return ProductsEntitlements(self._cfg, self._httpx_client) + def list( self, *, @@ -575,81 +512,6 @@ def archive( ) ) - def list_entitlements( - self, - product_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, - ) -> ResolvedEntitlementListResponse: - """List product entitlements""" - response = self._request( - ApiRequest( - method="get", - path="/api/v1/products/{product_id}/entitlements", - path_params={ - "product_id": product_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, ResolvedEntitlementListResponse) - - def create_entitlement( - self, - product_id: str, - *, - entitlements: builtins.list[EntitlementSpecRequest], - 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, - ) -> EntitlementListResponse: - """Create product entitlements - - A product has no entitlement rows of its own: its entitlements are the feature-level - defaults of the features scoped to it, which is what `GET` on this path resolves. Every - spec must therefore target a feature belonging to `product_id`. Features that already - carry a default entitlement are skipped. - - Specs are validated up front, but the writes are not atomic: each feature is written on - its own, so a failure part-way can leave earlier specs committed. Retrying is safe.""" - response = self._request( - ApiRequest( - method="post", - path="/api/v1/products/{product_id}/entitlements", - path_params={ - "product_id": product_id, - }, - json_body=to_json_value( - CreateEntitlementsRequest( - entitlements=entitlements, - ), - CreateEntitlementsRequest, - ), - 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, EntitlementListResponse) - def unarchive( self, product_id: str, @@ -684,11 +546,15 @@ class ProductsWithRawResponse: """The methods of :class:`Products`, returning an :class:`APIResponse`.""" def __init__(self, resource: Products) -> None: + self._resource = resource 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.update = to_raw_response_wrapper(resource.update) self.archive = to_raw_response_wrapper(resource.archive) - self.list_entitlements = to_raw_response_wrapper(resource.list_entitlements) - self.create_entitlement = to_raw_response_wrapper(resource.create_entitlement) self.unarchive = to_raw_response_wrapper(resource.unarchive) + + @property + def entitlements(self) -> ProductsEntitlementsWithRawResponse: + """The entitlements API.""" + return ProductsEntitlementsWithRawResponse(self._resource.entitlements) diff --git a/meteroid/api/products_entitlements.py b/meteroid/api/products_entitlements.py new file mode 100644 index 0000000..85a8aa4 --- /dev/null +++ b/meteroid/api/products_entitlements.py @@ -0,0 +1,208 @@ +# this file is @generated +"""Products entitlements API.""" + +from __future__ import annotations + +import builtins +import typing as t + +from .. import models as _models +from ..models import ( + CreateEntitlementsRequest, + EntitlementListResponse, + EntitlementSpecRequest, + ResolvedEntitlementListResponse, +) +from ..serialization import UNSET, Unset, to_json_value +from ._response import async_to_raw_response_wrapper, to_raw_response_wrapper +from .common import ( + ApiBaseAsync, + ApiBaseSync, + ApiRequest, + Timeout, + decode_response, +) + + +class AsyncProductsEntitlements(ApiBaseAsync): + """Products entitlements API, for asyncio.""" + + @property + def with_raw_response(self) -> AsyncProductsEntitlementsWithRawResponse: + """These methods, returning an :class:`APIResponse` with the status and headers.""" + return AsyncProductsEntitlementsWithRawResponse(self) + + async def list( + self, + product_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, + ) -> ResolvedEntitlementListResponse: + """List product entitlements""" + response = await self._request( + ApiRequest( + method="get", + path="/api/v1/products/{product_id}/entitlements", + path_params={ + "product_id": product_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, ResolvedEntitlementListResponse) + + async def create( + self, + product_id: str, + *, + entitlements: builtins.list[EntitlementSpecRequest], + 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, + ) -> EntitlementListResponse: + """Create product entitlements + + A product has no entitlement rows of its own: its entitlements are the feature-level + defaults of the features scoped to it, which is what `GET` on this path resolves. Every + spec must therefore target a feature belonging to `product_id`. Features that already + carry a default entitlement are skipped. + + Specs are validated up front, but the writes are not atomic: each feature is written on + its own, so a failure part-way can leave earlier specs committed. Retrying is safe.""" + response = await self._request( + ApiRequest( + method="post", + path="/api/v1/products/{product_id}/entitlements", + path_params={ + "product_id": product_id, + }, + json_body=to_json_value( + CreateEntitlementsRequest( + entitlements=entitlements, + ), + CreateEntitlementsRequest, + ), + 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, EntitlementListResponse) + + +class AsyncProductsEntitlementsWithRawResponse: + """The methods of :class:`AsyncProductsEntitlements`, returning an :class:`APIResponse`.""" + + def __init__(self, resource: AsyncProductsEntitlements) -> None: + self.list = async_to_raw_response_wrapper(resource.list) + self.create = async_to_raw_response_wrapper(resource.create) + + +class ProductsEntitlements(ApiBaseSync): + """Products entitlements API.""" + + @property + def with_raw_response(self) -> ProductsEntitlementsWithRawResponse: + """These methods, returning an :class:`APIResponse` with the status and headers.""" + return ProductsEntitlementsWithRawResponse(self) + + def list( + self, + product_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, + ) -> ResolvedEntitlementListResponse: + """List product entitlements""" + response = self._request( + ApiRequest( + method="get", + path="/api/v1/products/{product_id}/entitlements", + path_params={ + "product_id": product_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, ResolvedEntitlementListResponse) + + def create( + self, + product_id: str, + *, + entitlements: builtins.list[EntitlementSpecRequest], + 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, + ) -> EntitlementListResponse: + """Create product entitlements + + A product has no entitlement rows of its own: its entitlements are the feature-level + defaults of the features scoped to it, which is what `GET` on this path resolves. Every + spec must therefore target a feature belonging to `product_id`. Features that already + carry a default entitlement are skipped. + + Specs are validated up front, but the writes are not atomic: each feature is written on + its own, so a failure part-way can leave earlier specs committed. Retrying is safe.""" + response = self._request( + ApiRequest( + method="post", + path="/api/v1/products/{product_id}/entitlements", + path_params={ + "product_id": product_id, + }, + json_body=to_json_value( + CreateEntitlementsRequest( + entitlements=entitlements, + ), + CreateEntitlementsRequest, + ), + 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, EntitlementListResponse) + + +class ProductsEntitlementsWithRawResponse: + """The methods of :class:`ProductsEntitlements`, returning an :class:`APIResponse`.""" + + def __init__(self, resource: ProductsEntitlements) -> None: + self.list = to_raw_response_wrapper(resource.list) + self.create = to_raw_response_wrapper(resource.create) diff --git a/tests/test_add_ons.py b/tests/test_add_ons.py index c6bcab5..76083b8 100644 --- a/tests/test_add_ons.py +++ b/tests/test_add_ons.py @@ -2,11 +2,7 @@ # ruff: noqa: I001 (where the package sorts depends on whether it is generated yet) import unittest -from meteroid.models import ( - CreateAddOnRequest, - UpdateAddOnRequest, - CreateEntitlementsRequest, -) +from meteroid.models import CreateAddOnRequest, UpdateAddOnRequest from perseid_mock import call, decode, mock @@ -59,31 +55,6 @@ def test_archive(self) -> None: call(client.add_ons.archive, "addon_id") self.assertEqual(requests, ["POST /api/v1/addons/addon_id/archive"]) - def test_list_entitlements(self) -> None: - client, requests = mock( - 200, - "application/json", - '{"data":[{"feature":{"code":"sample","id":"feature_id_53","name":"sample"},"value":{"type":"BOOLEAN","enabled":false}}]}', - ) - call(client.add_ons.list_entitlements, "addon_id") - self.assertEqual(requests, ["GET /api/v1/addons/addon_id/entitlements"]) - - def test_create_entitlement(self) -> None: - client, requests = mock( - 200, - "application/json", - '{"data":[{"created_at":"2023-12-31T23:59:59.999-05:30","feature_id":"feature_id_39","id":"entitlement_id_2","updated_at":"2024-03-15T10:30:45.123+02:00","value":{"type":"BOOLEAN","enabled":false}}]}', - ) - call( - client.add_ons.create_entitlement, - "addon_id", - body=decode( - CreateEntitlementsRequest, - '{"entitlements":[{"feature_id":"feature_id_9","value":{"type":"BOOLEAN","enabled":false}}]}', - ), - ) - self.assertEqual(requests, ["POST /api/v1/addons/addon_id/entitlements"]) - def test_unarchive(self) -> None: client, requests = mock(204, None, "") call(client.add_ons.unarchive, "addon_id") diff --git a/tests/test_add_ons_entitlements.py b/tests/test_add_ons_entitlements.py new file mode 100644 index 0000000..70b2a95 --- /dev/null +++ b/tests/test_add_ons_entitlements.py @@ -0,0 +1,34 @@ +# this file is @generated +# ruff: noqa: I001 (where the package sorts depends on whether it is generated yet) +import unittest + +from meteroid.models import CreateEntitlementsRequest + +from perseid_mock import call, decode, mock + + +class AddOnsEntitlementsTest(unittest.TestCase): + def test_list(self) -> None: + client, requests = mock( + 200, + "application/json", + '{"data":[{"feature":{"code":"sample","id":"feature_id_53","name":"sample"},"value":{"type":"BOOLEAN","enabled":false}}]}', + ) + call(client.add_ons.entitlements.list, "addon_id") + self.assertEqual(requests, ["GET /api/v1/addons/addon_id/entitlements"]) + + def test_create(self) -> None: + client, requests = mock( + 200, + "application/json", + '{"data":[{"created_at":"2023-12-31T23:59:59.999-05:30","feature_id":"feature_id_39","id":"entitlement_id_2","updated_at":"2024-03-15T10:30:45.123+02:00","value":{"type":"BOOLEAN","enabled":false}}]}', + ) + call( + client.add_ons.entitlements.create, + "addon_id", + body=decode( + CreateEntitlementsRequest, + '{"entitlements":[{"feature_id":"feature_id_9","value":{"type":"BOOLEAN","enabled":false}}]}', + ), + ) + self.assertEqual(requests, ["POST /api/v1/addons/addon_id/entitlements"]) diff --git a/tests/test_plans.py b/tests/test_plans.py index 1e6a7a7..31dbbb6 100644 --- a/tests/test_plans.py +++ b/tests/test_plans.py @@ -5,7 +5,6 @@ from meteroid.models import ( CreateEntitlementsRequest, CreatePlanRequest, - MinimumCommitment, ReplacePlanRequest, PatchPlanRequest, ) @@ -67,31 +66,6 @@ def test_create(self) -> None: ) self.assertEqual(requests, ["POST /api/v1/plans"]) - def test_update_version_minimum(self) -> None: - client, requests = mock( - 200, - "application/json", - '{"amount":"sample","scope":{"type":"all_components"}}', - ) - call( - client.plans.update_version_minimum, - "plan_version_id", - body=decode( - MinimumCommitment, - '{"amount":"sample","scope":{"type":"all_components"}}', - ), - ) - self.assertEqual( - requests, ["PUT /api/v1/plans/versions/plan_version_id/minimum"] - ) - - def test_delete_version_minimum(self) -> None: - client, requests = mock(204, None, "") - call(client.plans.delete_version_minimum, "plan_version_id") - self.assertEqual( - requests, ["DELETE /api/v1/plans/versions/plan_version_id/minimum"] - ) - def test_retrieve(self) -> None: client, requests = mock( 200, @@ -144,12 +118,3 @@ def test_unarchive(self) -> None: client, requests = mock(204, None, "") call(client.plans.unarchive, "plan_id") self.assertEqual(requests, ["POST /api/v1/plans/plan_id/unarchive"]) - - def test_list_versions(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}}', - ) - call(client.plans.list_versions, "plan_id") - self.assertEqual(requests, ["GET /api/v1/plans/plan_id/versions"]) diff --git a/tests/test_plans_versions.py b/tests/test_plans_versions.py new file mode 100644 index 0000000..e576334 --- /dev/null +++ b/tests/test_plans_versions.py @@ -0,0 +1,43 @@ +# this file is @generated +# ruff: noqa: I001 (where the package sorts depends on whether it is generated yet) +import unittest + +from meteroid.models import MinimumCommitment + +from perseid_mock import call, decode, mock + + +class PlansVersionsTest(unittest.TestCase): + def test_update_minimum(self) -> None: + client, requests = mock( + 200, + "application/json", + '{"amount":"sample","scope":{"type":"all_components"}}', + ) + call( + client.plans.versions.update_minimum, + "plan_version_id", + body=decode( + MinimumCommitment, + '{"amount":"sample","scope":{"type":"all_components"}}', + ), + ) + self.assertEqual( + requests, ["PUT /api/v1/plans/versions/plan_version_id/minimum"] + ) + + def test_delete_minimum(self) -> None: + client, requests = mock(204, None, "") + call(client.plans.versions.delete_minimum, "plan_version_id") + self.assertEqual( + requests, ["DELETE /api/v1/plans/versions/plan_version_id/minimum"] + ) + + 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}}', + ) + call(client.plans.versions.list, "plan_id") + self.assertEqual(requests, ["GET /api/v1/plans/plan_id/versions"]) diff --git a/tests/test_products.py b/tests/test_products.py index a22f117..0a587bb 100644 --- a/tests/test_products.py +++ b/tests/test_products.py @@ -2,11 +2,7 @@ # ruff: noqa: I001 (where the package sorts depends on whether it is generated yet) import unittest -from meteroid.models import ( - CreateProductRequest, - UpdateProductRequest, - CreateEntitlementsRequest, -) +from meteroid.models import CreateProductRequest, UpdateProductRequest from perseid_mock import call, decode, mock @@ -63,31 +59,6 @@ def test_archive(self) -> None: call(client.products.archive, "product_id") self.assertEqual(requests, ["POST /api/v1/products/product_id/archive"]) - def test_list_entitlements(self) -> None: - client, requests = mock( - 200, - "application/json", - '{"data":[{"feature":{"code":"sample","id":"feature_id_53","name":"sample"},"value":{"type":"BOOLEAN","enabled":false}}]}', - ) - call(client.products.list_entitlements, "product_id") - self.assertEqual(requests, ["GET /api/v1/products/product_id/entitlements"]) - - def test_create_entitlement(self) -> None: - client, requests = mock( - 200, - "application/json", - '{"data":[{"created_at":"2023-12-31T23:59:59.999-05:30","feature_id":"feature_id_39","id":"entitlement_id_2","updated_at":"2024-03-15T10:30:45.123+02:00","value":{"type":"BOOLEAN","enabled":false}}]}', - ) - call( - client.products.create_entitlement, - "product_id", - body=decode( - CreateEntitlementsRequest, - '{"entitlements":[{"feature_id":"feature_id_9","value":{"type":"BOOLEAN","enabled":false}}]}', - ), - ) - self.assertEqual(requests, ["POST /api/v1/products/product_id/entitlements"]) - def test_unarchive(self) -> None: client, requests = mock(204, None, "") call(client.products.unarchive, "product_id") diff --git a/tests/test_products_entitlements.py b/tests/test_products_entitlements.py new file mode 100644 index 0000000..291d7da --- /dev/null +++ b/tests/test_products_entitlements.py @@ -0,0 +1,34 @@ +# this file is @generated +# ruff: noqa: I001 (where the package sorts depends on whether it is generated yet) +import unittest + +from meteroid.models import CreateEntitlementsRequest + +from perseid_mock import call, decode, mock + + +class ProductsEntitlementsTest(unittest.TestCase): + def test_list(self) -> None: + client, requests = mock( + 200, + "application/json", + '{"data":[{"feature":{"code":"sample","id":"feature_id_53","name":"sample"},"value":{"type":"BOOLEAN","enabled":false}}]}', + ) + call(client.products.entitlements.list, "product_id") + self.assertEqual(requests, ["GET /api/v1/products/product_id/entitlements"]) + + def test_create(self) -> None: + client, requests = mock( + 200, + "application/json", + '{"data":[{"created_at":"2023-12-31T23:59:59.999-05:30","feature_id":"feature_id_39","id":"entitlement_id_2","updated_at":"2024-03-15T10:30:45.123+02:00","value":{"type":"BOOLEAN","enabled":false}}]}', + ) + call( + client.products.entitlements.create, + "product_id", + body=decode( + CreateEntitlementsRequest, + '{"entitlements":[{"feature_id":"feature_id_9","value":{"type":"BOOLEAN","enabled":false}}]}', + ), + ) + self.assertEqual(requests, ["POST /api/v1/products/product_id/entitlements"])