diff --git a/mpt_api_client/resources/commerce/commerce.py b/mpt_api_client/resources/commerce/commerce.py index c089725f..7ea9f7cc 100644 --- a/mpt_api_client/resources/commerce/commerce.py +++ b/mpt_api_client/resources/commerce/commerce.py @@ -1,6 +1,10 @@ from mpt_api_client.http import AsyncHTTPClient, HTTPClient from mpt_api_client.resources.commerce.agreements import AgreementsService, AsyncAgreementsService from mpt_api_client.resources.commerce.orders import AsyncOrdersService, OrdersService +from mpt_api_client.resources.commerce.subscriptions import ( + AsyncSubscriptionsService, + SubscriptionsService, +) class Commerce: @@ -19,6 +23,11 @@ def orders(self) -> OrdersService: """Order service.""" return OrdersService(http_client=self.http_client) + @property + def subscriptions(self) -> SubscriptionsService: + """Subscription service.""" + return SubscriptionsService(http_client=self.http_client) + class AsyncCommerce: """Commerce MPT API Module.""" @@ -35,3 +44,8 @@ def agreements(self) -> AsyncAgreementsService: def orders(self) -> AsyncOrdersService: """Order service.""" return AsyncOrdersService(http_client=self.http_client) + + @property + def subscriptions(self) -> AsyncSubscriptionsService: + """Subscription service.""" + return AsyncSubscriptionsService(http_client=self.http_client) diff --git a/mpt_api_client/resources/commerce/subscriptions.py b/mpt_api_client/resources/commerce/subscriptions.py new file mode 100644 index 00000000..83f6dd46 --- /dev/null +++ b/mpt_api_client/resources/commerce/subscriptions.py @@ -0,0 +1,87 @@ +from mpt_api_client.http import ( + AsyncCreateMixin, + AsyncDeleteMixin, + AsyncService, + CreateMixin, + DeleteMixin, + Service, +) +from mpt_api_client.models import Model, ResourceData + + +class Subscription(Model): + """Subscription resource.""" + + +class SubscriptionsServiceConfig: + """Subscription service config.""" + + _endpoint = "/public/v1/commerce/subscriptions" + _model_class = Subscription + _collection_key = "data" + + +class SubscriptionsService( # noqa: WPS215 + CreateMixin[Subscription], + DeleteMixin, + Service[Subscription], + SubscriptionsServiceConfig, +): + """Subscription service.""" + + def render(self, resource_id: str) -> str: + """Render subscription template. + + Args: + resource_id: Subscription resource ID + + Returns: + Order template text in markdown format. + """ + response = self._resource_do_request(resource_id, "GET", "render") + return response.text + + def terminate(self, resource_id: str, resource_data: ResourceData) -> Subscription: + """Terminate subscription. + + Args: + resource_id: Order resource ID + resource_data: Order resource data + + Returns: + Subscription template text in markdown format. + """ + return self._resource_action(resource_id, "POST", "terminate", json=resource_data) + + +class AsyncSubscriptionsService( # noqa: WPS215 + AsyncCreateMixin[Subscription], + AsyncDeleteMixin, + AsyncService[Subscription], + SubscriptionsServiceConfig, +): + """Async Subscription service.""" + + async def render(self, resource_id: str) -> str: + """Render subscription template. + + Args: + resource_id: Subscription resource ID + + Returns: + Order template text in markdown format. + """ + response = await self._resource_do_request(resource_id, "GET", "render") + return response.text + + async def terminate(self, resource_id: str, resource_data: ResourceData) -> Subscription: + """Terminate subscription. + + Args: + resource_id: Order resource ID + resource_data: Order resource data + + Returns: + Subscription template text in markdown format. + """ + return await self._resource_action(resource_id, "POST", "terminate", json=resource_data) diff --git a/tests/resources/commerce/test_commerce.py b/tests/resources/commerce/test_commerce.py index 61654ebb..873c1828 100644 --- a/tests/resources/commerce/test_commerce.py +++ b/tests/resources/commerce/test_commerce.py @@ -1,7 +1,13 @@ +import pytest + from mpt_api_client.http import AsyncHTTPClient from mpt_api_client.resources.commerce import AsyncCommerce, Commerce from mpt_api_client.resources.commerce.agreements import AgreementsService, AsyncAgreementsService from mpt_api_client.resources.commerce.orders import AsyncOrdersService, OrdersService +from mpt_api_client.resources.commerce.subscriptions import ( + AsyncSubscriptionsService, + SubscriptionsService, +) def test_commerce_init(http_client): @@ -11,15 +17,6 @@ def test_commerce_init(http_client): assert commerce.http_client is http_client -def test_commerce_orders_property(http_client): - commerce = Commerce(http_client=http_client) - - orders_service = commerce.orders - - assert isinstance(orders_service, OrdersService) - assert orders_service.http_client is http_client - - def test_commerce_orders_multiple_calls(http_client): commerce = Commerce(http_client=http_client) @@ -38,39 +35,33 @@ def test_async_commerce_init(async_http_client: AsyncHTTPClient): assert commerce.http_client is async_http_client -def test_async_commerce_orders_property(async_http_client: AsyncHTTPClient): - commerce = AsyncCommerce(http_client=async_http_client) - - orders_service = commerce.orders - - assert isinstance(orders_service, AsyncOrdersService) - assert orders_service.http_client is async_http_client - - -def test_async_commerce_orders_multiple_calls(async_http_client: AsyncHTTPClient): - commerce = AsyncCommerce(http_client=async_http_client) - - orders_service = commerce.orders - orders_service_additional = commerce.orders - - assert orders_service is not orders_service_additional - assert isinstance(orders_service, AsyncOrdersService) - assert isinstance(orders_service_additional, AsyncOrdersService) - - -def test_async_agreements(async_http_client): - commerce = AsyncCommerce(http_client=async_http_client) +@pytest.mark.parametrize( + ("attr_name", "expected"), + [ + ("agreements", AgreementsService), + ("orders", OrdersService), + ("subscriptions", SubscriptionsService), + ], +) +def test_commerce_properties(http_client, attr_name, expected): + commerce = Commerce(http_client=http_client) - agreements = commerce.agreements + service = getattr(commerce, attr_name) - assert isinstance(agreements, AsyncAgreementsService) - assert agreements.http_client is async_http_client + assert isinstance(service, expected) -def test_agreements(http_client): - commerce = Commerce(http_client=http_client) +@pytest.mark.parametrize( + ("attr_name", "expected"), + [ + ("agreements", AsyncAgreementsService), + ("orders", AsyncOrdersService), + ("subscriptions", AsyncSubscriptionsService), + ], +) +def test_async_commerce_properties(http_client, attr_name, expected): + commerce = AsyncCommerce(http_client=http_client) - agreements = commerce.agreements + service = getattr(commerce, attr_name) - assert isinstance(agreements, AgreementsService) - assert agreements.http_client is http_client + assert isinstance(service, expected) diff --git a/tests/resources/commerce/test_subscriptions.py b/tests/resources/commerce/test_subscriptions.py new file mode 100644 index 00000000..73519011 --- /dev/null +++ b/tests/resources/commerce/test_subscriptions.py @@ -0,0 +1,88 @@ +import httpx +import pytest +import respx + +from mpt_api_client.resources.commerce.subscriptions import ( + AsyncSubscriptionsService, + SubscriptionsService, +) + + +@pytest.fixture +def subscriptions_service(http_client): + return SubscriptionsService(http_client=http_client) + + +@pytest.fixture +def async_subscriptions_service(async_http_client): + return AsyncSubscriptionsService(http_client=async_http_client) + + +async def test_async_terminate(async_subscriptions_service): + subscription_expected = {"id": "SUB-123", "status": "Terminated", "name": "Terminated SUB-123"} + with respx.mock: + respx.post( + "https://api.example.com/public/v1/commerce/subscriptions/SUB-123/terminate" + ).mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + json=subscription_expected, + ) + ) + + subscription_updated = await async_subscriptions_service.terminate( + "SUB-123", {"name": "Terminated SUB-123"} + ) + + assert subscription_updated.to_dict() == subscription_expected + + +async def test_async_render(async_subscriptions_service): + template_content = "# Subscription Template\n\nThis is a markdown template." + with respx.mock: + respx.get("https://api.example.com/public/v1/commerce/subscriptions/SUB-123/render").mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + headers={"content-type": "text/markdown"}, + content=template_content, + ) + ) + + template = await async_subscriptions_service.render("SUB-123") + + assert template == template_content + + +def test_terminate(subscriptions_service): + subscription_expected = {"id": "SUB-123", "status": "Terminated", "name": "Terminated SUB-123"} + with respx.mock: + respx.post( + "https://api.example.com/public/v1/commerce/subscriptions/SUB-123/terminate" + ).mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + json=subscription_expected, + ) + ) + + subscription_updated = subscriptions_service.terminate( + "SUB-123", {"name": "Terminated SUB-123"} + ) + + assert subscription_updated.to_dict() == subscription_expected + + +def test_render(subscriptions_service): + template_content = "# Subscription Template\n\nThis is a markdown template." + with respx.mock: + respx.get("https://api.example.com/public/v1/commerce/subscriptions/SUB-123/render").mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + headers={"content-type": "text/markdown"}, + content=template_content, + ) + ) + + template = subscriptions_service.render("SUB-123") + + assert template == template_content