diff --git a/mpt_api_client/resources/accounts/accounts.py b/mpt_api_client/resources/accounts/accounts.py index cb78ca48..00d707a9 100644 --- a/mpt_api_client/resources/accounts/accounts.py +++ b/mpt_api_client/resources/accounts/accounts.py @@ -1,6 +1,7 @@ from mpt_api_client.http import AsyncHTTPClient, HTTPClient from mpt_api_client.resources.accounts.account import AccountsService, AsyncAccountsService from mpt_api_client.resources.accounts.api_tokens import ApiTokensService, AsyncApiTokensService +from mpt_api_client.resources.accounts.buyers import AsyncBuyersService, BuyersService from mpt_api_client.resources.accounts.cloud_tenants import ( AsyncCloudTenantsService, CloudTenantsService, @@ -61,6 +62,11 @@ def cloud_tenants(self) -> CloudTenantsService: """Cloud Tenants service.""" return CloudTenantsService(http_client=self.http_client) + @property + def buyers(self) -> BuyersService: + """Buyers service.""" + return BuyersService(http_client=self.http_client) + class AsyncAccounts: """Async Accounts MPT API Module.""" @@ -107,3 +113,8 @@ def modules(self) -> AsyncModulesService: def cloud_tenants(self) -> AsyncCloudTenantsService: """Cloud Tenants service.""" return AsyncCloudTenantsService(http_client=self.http_client) + + @property + def buyers(self) -> AsyncBuyersService: + """Buyers service.""" + return AsyncBuyersService(http_client=self.http_client) diff --git a/mpt_api_client/resources/accounts/buyers.py b/mpt_api_client/resources/accounts/buyers.py new file mode 100644 index 00000000..aa141620 --- /dev/null +++ b/mpt_api_client/resources/accounts/buyers.py @@ -0,0 +1,95 @@ +from mpt_api_client.http import AsyncService, Service +from mpt_api_client.http.mixins import ( + AsyncCreateMixin, + AsyncDeleteMixin, + AsyncUpdateMixin, + CreateMixin, + DeleteMixin, + UpdateMixin, +) +from mpt_api_client.models import Model +from mpt_api_client.models.model import ResourceData +from mpt_api_client.resources.accounts.mixins import ( + ActivatableMixin, + AsyncActivatableMixin, + AsyncEnablableMixin, + AsyncValidateMixin, + EnablableMixin, + ValidateMixin, +) + + +class Buyer(Model): + """Buyer Model.""" + + +class BuyersServiceConfig: + """Buyers Service Configuration.""" + + _endpoint = "/public/v1/accounts/buyers" + _model_class = Buyer + _collection_key = "data" + + +class BuyersService( + CreateMixin[Buyer], + DeleteMixin, + UpdateMixin[Buyer], + ActivatableMixin[Buyer], + EnablableMixin[Buyer], + ValidateMixin[Buyer], + Service[Buyer], + BuyersServiceConfig, +): + """Buyers Service.""" + + def synchronize(self, resource_id: str, resource_data: ResourceData | None = None) -> Buyer: + """Synchronize a buyer. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return self._resource_action(resource_id, "POST", "synchronize", json=resource_data) + + def transfer(self, resource_id: str, resource_data: ResourceData | None = None) -> Buyer: + """Transfer a buyer. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return self._resource_action(resource_id, "POST", "transfer", json=resource_data) + + +class AsyncBuyersService( + AsyncCreateMixin[Buyer], + AsyncDeleteMixin, + AsyncUpdateMixin[Buyer], + AsyncActivatableMixin[Buyer], + AsyncEnablableMixin[Buyer], + AsyncValidateMixin[Buyer], + AsyncService[Buyer], + BuyersServiceConfig, +): + """Async Buyers Service.""" + + async def synchronize( + self, resource_id: str, resource_data: ResourceData | None = None + ) -> Buyer: + """Synchronize a buyer. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return await self._resource_action(resource_id, "POST", "synchronize", json=resource_data) + + async def transfer(self, resource_id: str, resource_data: ResourceData | None = None) -> Buyer: + """Transfer a buyer. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return await self._resource_action(resource_id, "POST", "transfer", json=resource_data) diff --git a/tests/resources/accounts/test_accounts.py b/tests/resources/accounts/test_accounts.py index 68544ab6..6c69d339 100644 --- a/tests/resources/accounts/test_accounts.py +++ b/tests/resources/accounts/test_accounts.py @@ -6,6 +6,7 @@ ApiTokensService, AsyncApiTokensService, ) +from mpt_api_client.resources.accounts.buyers import AsyncBuyersService, BuyersService from mpt_api_client.resources.accounts.cloud_tenants import ( AsyncCloudTenantsService, CloudTenantsService, @@ -41,6 +42,7 @@ def async_accounts(async_http_client): ("api_tokens", ApiTokensService), ("modules", ModulesService), ("cloud_tenants", CloudTenantsService), + ("buyers", BuyersService), ], ) def test_accounts_properties(accounts, property_name, expected_service_class): @@ -62,6 +64,7 @@ def test_accounts_properties(accounts, property_name, expected_service_class): ("api_tokens", AsyncApiTokensService), ("modules", AsyncModulesService), ("cloud_tenants", AsyncCloudTenantsService), + ("buyers", AsyncBuyersService), ], ) def test_async_accounts_properties(async_accounts, property_name, expected_service_class): diff --git a/tests/resources/accounts/test_buyers.py b/tests/resources/accounts/test_buyers.py new file mode 100644 index 00000000..f1991d5a --- /dev/null +++ b/tests/resources/accounts/test_buyers.py @@ -0,0 +1,161 @@ +import httpx +import pytest +import respx + +from mpt_api_client.resources.accounts.buyers import AsyncBuyersService, Buyer, BuyersService + + +@pytest.fixture +def buyers_service(http_client): + return BuyersService(http_client=http_client) + + +@pytest.fixture +def async_buyers_service(async_http_client): + return AsyncBuyersService(http_client=async_http_client) + + +@pytest.mark.parametrize( + "method", + [ + "get", + "create", + "update", + "delete", + "activate", + "deactivate", + "enable", + "disable", + "validate", + "synchronize", + "transfer", + ], +) +def test_mixins_present(buyers_service, method): + assert hasattr(buyers_service, method) + + +@pytest.mark.parametrize( + "method", + [ + "get", + "create", + "update", + "delete", + "activate", + "deactivate", + "enable", + "disable", + "validate", + "synchronize", + "transfer", + ], +) +def test_async_mixins_present(async_buyers_service, method): + assert hasattr(async_buyers_service, method) + + +@pytest.mark.parametrize( + ("action", "input_status"), + [ + ("synchronize", {"id": "OBJ-0000-0001", "status": "update"}), + ("transfer", {"id": "OBJ-0000-0001", "status": "update"}), + ], +) +def test_buyers_resource_action(buyers_service, action, input_status): + request_expected_content = b'{"id":"OBJ-0000-0001","status":"update"}' + response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} + with respx.mock: + mock_route = respx.post( + f"https://api.example.com/public/v1/accounts/buyers/OBJ-0000-0001/{action}" + ).mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + json=response_expected_data, + ) + ) + buyers_obj = getattr(buyers_service, action)("OBJ-0000-0001", input_status) + + request = mock_route.calls[0].request + + assert request.content == request_expected_content + assert buyers_obj.to_dict() == response_expected_data + assert isinstance(buyers_obj, Buyer) + + +@pytest.mark.parametrize( + ("action", "input_status"), + [("synchronize", None), ("transfer", None)], +) +def test_buyers_resouce_action_no_data(buyers_service, action, input_status): + request_expected_content = b"" + response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} + with respx.mock: + mock_route = respx.post( + f"https://api.example.com/public/v1/accounts/buyers/OBJ-0000-0001/{action}" + ).mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + json=response_expected_data, + ) + ) + buyers_obj = getattr(buyers_service, action)("OBJ-0000-0001", None) + + request = mock_route.calls[0].request + + assert request.content == request_expected_content + assert buyers_obj.to_dict() == response_expected_data + assert isinstance(buyers_obj, Buyer) + + +@pytest.mark.parametrize( + ("action", "input_status"), + [ + ("synchronize", {"id": "OBJ-0000-0001", "status": "update"}), + ("transfer", {"id": "OBJ-0000-0001", "status": "update"}), + ], +) +async def test_async_buyers_resource_action(async_buyers_service, action, input_status): + request_expected_content = b'{"id":"OBJ-0000-0001","status":"update"}' + response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} + with respx.mock: + mock_route = respx.post( + f"https://api.example.com/public/v1/accounts/buyers/OBJ-0000-0001/{action}" + ).mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + json=response_expected_data, + ) + ) + buyers_obj = await getattr(async_buyers_service, action)("OBJ-0000-0001", input_status) + + request = mock_route.calls[0].request + + assert request.content == request_expected_content + assert buyers_obj.to_dict() == response_expected_data + assert isinstance(buyers_obj, Buyer) + + +@pytest.mark.parametrize( + ("action", "input_status"), + [("synchronize", None), ("transfer", None)], +) +async def test_async_buyers_resource_action_no_data(async_buyers_service, action, input_status): + request_expected_content = b"" + response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} + with respx.mock: + mock_route = respx.post( + f"https://api.example.com/public/v1/accounts/buyers/OBJ-0000-0001/{action}" + ).mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + json=response_expected_data, + ) + ) + buyers_obj = await getattr(async_buyers_service, action)("OBJ-0000-0001", None) + + request = mock_route.calls[0].request + + assert request.content == request_expected_content + assert buyers_obj.to_dict() == response_expected_data + assert isinstance(buyers_obj, Buyer)