diff --git a/mpt_api_client/resources/commerce/agreements.py b/mpt_api_client/resources/commerce/agreements.py index a9d2b8d..e95ffbc 100644 --- a/mpt_api_client/resources/commerce/agreements.py +++ b/mpt_api_client/resources/commerce/agreements.py @@ -10,6 +10,12 @@ AgreementsAttachmentService, AsyncAgreementsAttachmentService, ) +from mpt_api_client.resources.commerce.mixins import ( + AsyncRenderMixin, + AsyncTemplateMixin, + RenderMixin, + TemplateMixin, +) class Agreement(Model): @@ -25,6 +31,8 @@ class AgreementsServiceConfig: class AgreementsService( + RenderMixin[Agreement], + TemplateMixin[Agreement], ManagedResourceMixin[Agreement], CollectionMixin[Agreement], Service[Agreement], @@ -32,30 +40,6 @@ class AgreementsService( ): """Agreements service.""" - def template(self, agreement_id: str) -> str: - """Renders the template for the given Agreement id. - - Args: - agreement_id: Agreement ID. - - Returns: - Agreement template. - """ - response = self._resource_do_request(agreement_id, action="template") - return response.text - - def render(self, agreement_id: str) -> str: - """Renders the template for the given Agreement id. - - Args: - agreement_id: Agreement ID. - - Returns: - Rendered Agreement. - """ - response = self._resource_do_request(agreement_id, action="render") - return response.text - def attachments(self, agreement_id: str) -> AgreementsAttachmentService: """Get the attachments service for the given Agreement id. @@ -72,6 +56,8 @@ def attachments(self, agreement_id: str) -> AgreementsAttachmentService: class AsyncAgreementsService( + AsyncRenderMixin[Agreement], + AsyncTemplateMixin[Agreement], AsyncManagedResourceMixin[Agreement], AsyncCollectionMixin[Agreement], AsyncService[Agreement], @@ -79,30 +65,6 @@ class AsyncAgreementsService( ): """Agreements service.""" - async def template(self, agreement_id: str) -> str: - """Renders the template for the given Agreement id. - - Args: - agreement_id: Agreement ID. - - Returns: - Agreement template. - """ - response = await self._resource_do_request(agreement_id, action="template") - return response.text - - async def render(self, agreement_id: str) -> str: - """Renders the template for the given Agreement id. - - Args: - agreement_id: Agreement ID. - - Returns: - Rendered Agreement. - """ - response = await self._resource_do_request(agreement_id, action="render") - return response.text - def attachments(self, agreement_id: str) -> AsyncAgreementsAttachmentService: """Get the attachments service for the given Agreement id. diff --git a/mpt_api_client/resources/commerce/assets.py b/mpt_api_client/resources/commerce/assets.py index 19125f4..898477f 100644 --- a/mpt_api_client/resources/commerce/assets.py +++ b/mpt_api_client/resources/commerce/assets.py @@ -10,7 +10,12 @@ UpdateMixin, ) from mpt_api_client.models import Model -from mpt_api_client.models.model import ResourceData +from mpt_api_client.resources.commerce.mixins import ( + AsyncRenderMixin, + AsyncTerminateMixin, + RenderMixin, + TerminateMixin, +) class Asset(Model): @@ -33,68 +38,23 @@ class AssetService( CreateMixin[Asset], UpdateMixin[Asset], GetMixin[Asset], + TerminateMixin[Asset], + RenderMixin[Asset], CollectionMixin[Asset], Service[Asset], AssetServiceConfig, ): """Assets service.""" - def terminate(self, asset_id: str, resource_data: ResourceData | None = None) -> Asset: - """Terminate the given Asset id. - - Args: - asset_id: Asset ID. - resource_data: Resource data will be updated - """ - response = self._resource_do_request(asset_id, "POST", "terminate", json=resource_data) - return self._model_class.from_response(response) - - def render(self, asset_id: str) -> str: - """Renders the template for the given Asset id. - - Args: - asset_id: Asset ID. - - Returns: - Render asset template json. - """ - response = self._resource_do_request(asset_id, action="render") - - return response.text - class AsyncAssetService( AsyncCreateMixin[Asset], AsyncUpdateMixin[Asset], AsyncGetMixin[Asset], + AsyncTerminateMixin[Asset], + AsyncRenderMixin[Asset], AsyncCollectionMixin[Asset], AsyncService[Asset], AssetServiceConfig, ): """Asynchronous Assets service.""" - - async def terminate(self, asset_id: str, resource_data: ResourceData | None = None) -> Asset: - """Terminate the given Asset id. - - Args: - asset_id: Asset ID. - resource_data: Resource data will be updated - """ - response = await self._resource_do_request( - asset_id, "POST", "terminate", json=resource_data - ) - - return self._model_class.from_response(response) - - async def render(self, asset_id: str) -> str: - """Renders the template for the given Asset id. - - Args: - asset_id: Asset ID. - - Returns: - Render asset template string. - """ - response = await self._resource_do_request(asset_id, action="render") - - return response.text diff --git a/mpt_api_client/resources/commerce/mixins.py b/mpt_api_client/resources/commerce/mixins.py index 856ca76..e04c9f1 100644 --- a/mpt_api_client/resources/commerce/mixins.py +++ b/mpt_api_client/resources/commerce/mixins.py @@ -17,6 +17,38 @@ def terminate(self, resource_id: str, resource_data: ResourceData | None = None) return self._resource_action(resource_id, "POST", "terminate", json=resource_data) # type: ignore[attr-defined, no-any-return] +class RenderMixin[Model]: + """Render resource mixin.""" + + def render(self, resource_id: str) -> str: + """Render resource. + + Args: + resource_id: Resource ID + + Returns: + Rendered resource. + """ + response = self._resource_do_request(resource_id, action="render") # type: ignore[attr-defined] + return response.text # type: ignore[no-any-return] + + +class TemplateMixin[Model]: + """Template resource mixin.""" + + def template(self, resource_id: str) -> str: + """Get resource template. + + Args: + resource_id: Resource ID + + Returns: + Resource template. + """ + response = self._resource_do_request(resource_id, action="template") # type: ignore[attr-defined] + return response.text # type: ignore[no-any-return] + + class AsyncTerminateMixin[Model]: """Asynchronous terminate resource mixin.""" @@ -31,3 +63,35 @@ async def terminate(self, resource_id: str, resource_data: ResourceData | None = Terminated resource. """ return await self._resource_action(resource_id, "POST", "terminate", json=resource_data) # type: ignore[attr-defined, no-any-return] + + +class AsyncRenderMixin[Model]: + """Asynchronous render resource mixin.""" + + async def render(self, resource_id: str) -> str: + """Render resource. + + Args: + resource_id: Resource ID + + Returns: + Rendered resource. + """ + response = await self._resource_do_request(resource_id, action="render") # type: ignore[attr-defined] + return response.text # type: ignore[no-any-return] + + +class AsyncTemplateMixin[Model]: + """Asynchronous template resource mixin.""" + + async def template(self, resource_id: str) -> str: + """Get resource template. + + Args: + resource_id: Resource ID + + Returns: + Resource template. + """ + response = await self._resource_do_request(resource_id, action="template") # type: ignore[attr-defined] + return response.text # type: ignore[no-any-return] diff --git a/mpt_api_client/resources/commerce/orders.py b/mpt_api_client/resources/commerce/orders.py index c599e74..1e9f833 100644 --- a/mpt_api_client/resources/commerce/orders.py +++ b/mpt_api_client/resources/commerce/orders.py @@ -9,6 +9,12 @@ ManagedResourceMixin, ) from mpt_api_client.models import Model, ResourceData +from mpt_api_client.resources.commerce.mixins import ( + AsyncRenderMixin, + AsyncTemplateMixin, + RenderMixin, + TemplateMixin, +) from mpt_api_client.resources.commerce.orders_asset import ( AsyncOrdersAssetService, OrdersAssetService, @@ -32,6 +38,8 @@ class OrdersServiceConfig: class OrdersService( # noqa: WPS215 WPS214 + RenderMixin[Order], + TemplateMixin[Order], ManagedResourceMixin[Order], CollectionMixin[Order], Service[Order], @@ -93,30 +101,6 @@ def notify(self, resource_id: str, user: ResourceData) -> None: """ self._resource_do_request(resource_id, "POST", "notify", json=user) - def template(self, resource_id: str) -> str: - """Render order template. - - Args: - resource_id: Order resource ID - - Returns: - Order template text in markdown format. - """ - response = self._resource_do_request(resource_id, "GET", "template") - return response.text - - def render(self, resource_id: str) -> str: - """Render the order template for the given order ID. - - Args: - resource_id: Order resource ID - - Returns: - Rendered order as HTML. - """ - response = self._resource_do_request(resource_id, "GET", "render") - return response.text - def quote(self, resource_id: str, resource_data: ResourceData | None = None) -> Order: """Quote the order. @@ -159,6 +143,8 @@ def assets(self, order_id: str) -> OrdersAssetService: class AsyncOrdersService( # noqa: WPS215 WPS214 + AsyncRenderMixin[Order], + AsyncTemplateMixin[Order], AsyncManagedResourceMixin[Order], AsyncCollectionMixin[Order], AsyncService[Order], @@ -235,30 +221,6 @@ async def notify(self, resource_id: str, resource_data: ResourceData) -> None: """ await self._resource_do_request(resource_id, "POST", "notify", json=resource_data) - async def template(self, resource_id: str) -> str: - """Render order template. - - Args: - resource_id: Order resource ID - - Returns: - Order template text in markdown format. - """ - response = await self._resource_do_request(resource_id, "GET", "template") - return response.text - - async def render(self, resource_id: str) -> str: - """Render the order template for the given order ID. - - Args: - resource_id: Order resource ID - - Returns: - Rendered order as HTML. - """ - response = await self._resource_do_request(resource_id, "GET", "render") - return response.text - async def quote(self, resource_id: str, resource_data: ResourceData | None = None) -> Order: """Quote the order. diff --git a/mpt_api_client/resources/commerce/orders_asset.py b/mpt_api_client/resources/commerce/orders_asset.py index cb58cba..9d29058 100644 --- a/mpt_api_client/resources/commerce/orders_asset.py +++ b/mpt_api_client/resources/commerce/orders_asset.py @@ -6,6 +6,7 @@ ManagedResourceMixin, ) from mpt_api_client.models import Model +from mpt_api_client.resources.commerce.mixins import AsyncRenderMixin, RenderMixin class OrdersAsset(Model): @@ -21,6 +22,7 @@ class OrdersAssetServiceConfig: class OrdersAssetService( # noqa: WPS215 + RenderMixin[OrdersAsset], ManagedResourceMixin[OrdersAsset], CollectionMixin[OrdersAsset], Service[OrdersAsset], @@ -28,43 +30,12 @@ class OrdersAssetService( # noqa: WPS215 ): """Orders Asset service.""" - def render(self, resource_id: str) -> str: - """Render order asset template. - - Args: - resource_id: Order asset resource ID - - Returns: - Order asset template text in markdown format. - """ - response = self._resource_do_request( - resource_id, - "GET", - "render", - ) - return response.text - class AsyncOrdersAssetService( # noqa: WPS215 + AsyncRenderMixin[OrdersAsset], AsyncManagedResourceMixin[OrdersAsset], AsyncCollectionMixin[OrdersAsset], AsyncService[OrdersAsset], OrdersAssetServiceConfig, ): """Async Orders Asset service.""" - - async def render(self, resource_id: str) -> str: - """Render order asset template. - - Args: - resource_id: Order asset resource ID - - Returns: - Order asset template text in markdown format. - """ - response = await self._resource_do_request( - resource_id, - "GET", - "render", - ) - return response.text diff --git a/mpt_api_client/resources/commerce/subscriptions.py b/mpt_api_client/resources/commerce/subscriptions.py index 1df4180..f111a99 100644 --- a/mpt_api_client/resources/commerce/subscriptions.py +++ b/mpt_api_client/resources/commerce/subscriptions.py @@ -13,7 +13,12 @@ UpdateMixin, ) from mpt_api_client.models import Model -from mpt_api_client.resources.commerce.mixins import AsyncTerminateMixin, TerminateMixin +from mpt_api_client.resources.commerce.mixins import ( + AsyncRenderMixin, + AsyncTerminateMixin, + RenderMixin, + TerminateMixin, +) class Subscription(Model): @@ -34,23 +39,12 @@ class SubscriptionsService( # noqa: WPS215 GetMixin[Subscription], CollectionMixin[Subscription], TerminateMixin[Subscription], + RenderMixin[Subscription], 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 - class AsyncSubscriptionsService( # noqa: WPS215 AsyncCreateMixin[Subscription], @@ -58,19 +52,8 @@ class AsyncSubscriptionsService( # noqa: WPS215 AsyncGetMixin[Subscription], AsyncCollectionMixin[Subscription], AsyncTerminateMixin[Subscription], + AsyncRenderMixin[Subscription], 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 diff --git a/tests/unit/resources/commerce/test_agreements.py b/tests/unit/resources/commerce/test_agreements.py index 6fe2380..0ca3d99 100644 --- a/tests/unit/resources/commerce/test_agreements.py +++ b/tests/unit/resources/commerce/test_agreements.py @@ -46,48 +46,6 @@ def test_template(http_client): assert result == "# Order Template\n\nThis is a markdown template." -def test_render(http_client): - agreements_service = AgreementsService(http_client=http_client) - rendered_content = "# Order Template\n\nThis is a markdown template." - with respx.mock: - mock_route = respx.get( - "https://api.example.com/public/v1/commerce/agreements/AGR-123/render" - ).mock( - return_value=httpx.Response( - status_code=httpx.codes.OK, - headers={"content-type": "text/html"}, - content=rendered_content, - ) - ) - - result = agreements_service.render("AGR-123") - - assert mock_route.called - assert mock_route.call_count == 1 - assert result == rendered_content - - -async def test_async_render(async_http_client): - async_agreements_service = AsyncAgreementsService(http_client=async_http_client) - rendered_content = "# Order Template\n\nThis is a markdown template." - with respx.mock: - mock_route = respx.get( - "https://api.example.com/public/v1/commerce/agreements/AGR-123/render" - ).mock( - return_value=httpx.Response( - status_code=httpx.codes.OK, - headers={"content-type": "text/html"}, - content=rendered_content, - ) - ) - - result = await async_agreements_service.render("AGR-123") - - assert mock_route.called - assert mock_route.call_count == 1 - assert result == rendered_content - - def test_attachments_service(http_client): agreements_service = AgreementsService(http_client=http_client) @@ -106,7 +64,7 @@ def test_async_attachments_service(http_client): assert result.endpoint_params == {"agreement_id": "AGR-123"} -@pytest.mark.parametrize("method", ["create", "update", "get"]) +@pytest.mark.parametrize("method", ["create", "update", "get", "render", "template"]) def test_mixins_present(http_client, method): service = AgreementsService(http_client=http_client) @@ -115,7 +73,7 @@ def test_mixins_present(http_client, method): assert result is True -@pytest.mark.parametrize("method", ["create", "update", "get"]) +@pytest.mark.parametrize("method", ["create", "update", "get", "render", "template"]) def test_async_mixins_present(async_http_client, method): service = AgreementsService(http_client=async_http_client) diff --git a/tests/unit/resources/commerce/test_assets.py b/tests/unit/resources/commerce/test_assets.py index 31a6526..3941871 100644 --- a/tests/unit/resources/commerce/test_assets.py +++ b/tests/unit/resources/commerce/test_assets.py @@ -1,8 +1,5 @@ -import httpx import pytest -import respx -from mpt_api_client.constants import APPLICATION_JSON from mpt_api_client.resources.commerce.assets import AssetService, AsyncAssetService @@ -16,82 +13,6 @@ def async_assets_service(async_http_client): return AsyncAssetService(http_client=async_http_client) -async def test_async_render(async_assets_service): - render_response = {"id": "ASSET-123", "title": "Sample Asset"} - with respx.mock: - respx.get("https://api.example.com/public/v1/commerce/assets/ASSET-123/render").mock( - return_value=httpx.Response( - status_code=httpx.codes.OK, - headers={"content-type": APPLICATION_JSON}, - json=render_response, - ) - ) - - result = await async_assets_service.render("ASSET-123") - - assert result is not None - - -def test_render(assets_service): - render_response = {"id": "ASSET-123", "title": "Sample Asset"} - with respx.mock: - mock_route = respx.get( - "https://api.example.com/public/v1/commerce/assets/ASSET-123/render" - ).mock( - return_value=httpx.Response( - status_code=httpx.codes.OK, - headers={"content-type": APPLICATION_JSON}, - json=render_response, - ) - ) - - result = assets_service.render("ASSET-123") - - assert mock_route.called - assert mock_route.call_count == 1 - assert result is not None - - -def test_terminate(assets_service): - response_data = {"id": "ASSET-123", "status": "terminated"} - with respx.mock: - mock_route = respx.post( - "https://api.example.com/public/v1/commerce/assets/ASSET-123/terminate" - ).mock( - return_value=httpx.Response( - status_code=httpx.codes.OK, - headers={"content-type": APPLICATION_JSON}, - json=response_data, - ) - ) - - result = assets_service.terminate("ASSET-123") - - assert mock_route.called - assert mock_route.call_count == 1 - assert result is not None - - -async def test_async_terminate(async_assets_service): - response_data = {"id": "ASSET-123", "status": "terminated"} - with respx.mock: - mock_route = respx.post( - "https://api.example.com/public/v1/commerce/assets/ASSET-123/terminate" - ).mock( - return_value=httpx.Response( - status_code=httpx.codes.OK, - headers={"content-type": APPLICATION_JSON}, - json=response_data, - ) - ) - - result = await async_assets_service.terminate("ASSET-123") - - assert mock_route.called - assert mock_route.call_count == 1 - assert result is not None - - @pytest.mark.parametrize("method", ["create", "update", "get", "render", "terminate"]) def test_assets_service_methods(assets_service, method): result = hasattr(assets_service, method) diff --git a/tests/unit/resources/commerce/test_mixins.py b/tests/unit/resources/commerce/test_mixins.py index 23f5212..f19f4a0 100644 --- a/tests/unit/resources/commerce/test_mixins.py +++ b/tests/unit/resources/commerce/test_mixins.py @@ -4,7 +4,9 @@ from mpt_api_client.http import AsyncService, Service from mpt_api_client.resources.commerce.mixins import ( + AsyncRenderMixin, AsyncTerminateMixin, + RenderMixin, TerminateMixin, ) from tests.unit.conftest import DummyModel @@ -26,6 +28,22 @@ class AsyncDummyTerminateService( _model_class = DummyModel +class DummyRenderService( + RenderMixin[DummyModel], + Service[DummyModel], +): + _endpoint = "public/v1/dummy/render" + _model_class = DummyModel + + +class AsyncDummyRenderService( + AsyncRenderMixin[DummyModel], + AsyncService[DummyModel], +): + _endpoint = "public/v1/dummy/render" + _model_class = DummyModel + + @pytest.fixture def dummy_terminate_service(http_client): return DummyTerminateService(http_client=http_client) @@ -36,6 +54,16 @@ def async_dummy_terminate_service(async_http_client): return AsyncDummyTerminateService(http_client=async_http_client) +@pytest.fixture +def dummy_render_service(http_client): + return DummyRenderService(http_client=http_client) + + +@pytest.fixture +def async_dummy_render_service(async_http_client): + return AsyncDummyRenderService(http_client=async_http_client) + + def test_terminate_with_data(dummy_terminate_service): dummy_expected = {"id": "DUMMY-123", "status": "Terminated", "name": "Terminated DUMMY-123"} with respx.mock: @@ -48,7 +76,7 @@ def test_terminate_with_data(dummy_terminate_service): result = dummy_terminate_service.terminate("DUMMY-123", {"name": "Terminated DUMMY-123"}) - assert result is not None + assert result.to_dict() == dummy_expected def test_terminate(dummy_terminate_service): @@ -63,7 +91,7 @@ def test_terminate(dummy_terminate_service): result = dummy_terminate_service.terminate("DUMMY-124") - assert result is not None + assert result.to_dict() == dummy_expected async def test_async_terminate_with_data(async_dummy_terminate_service): @@ -80,7 +108,7 @@ async def test_async_terminate_with_data(async_dummy_terminate_service): "DUMMY-123", {"name": "Terminated DUMMY-123"} ) - assert result is not None + assert result.to_dict() == dummy_expected async def test_async_terminate(async_dummy_terminate_service): @@ -95,4 +123,36 @@ async def test_async_terminate(async_dummy_terminate_service): result = await async_dummy_terminate_service.terminate("DUMMY-124") - assert result is not None + assert result.to_dict() == dummy_expected + + +def test_render(dummy_render_service): + rendered_content = "