diff --git a/mpt_api_client/resources/helpdesk/forms.py b/mpt_api_client/resources/helpdesk/forms.py new file mode 100644 index 0000000..f1afa03 --- /dev/null +++ b/mpt_api_client/resources/helpdesk/forms.py @@ -0,0 +1,54 @@ +from mpt_api_client.http import AsyncService, Service +from mpt_api_client.http.mixins import ( + AsyncCollectionMixin, + AsyncManagedResourceMixin, + CollectionMixin, + ManagedResourceMixin, +) +from mpt_api_client.models import Model, ResourceData + + +class Form(Model): + """Helpdesk Form resource.""" + + +class FormsServiceConfig: + """Helpdesk Forms service configuration.""" + + _endpoint = "/public/v1/helpdesk/forms" + _model_class = Form + _collection_key = "data" + + +class FormsService( + ManagedResourceMixin[Form], + CollectionMixin[Form], + Service[Form], + FormsServiceConfig, +): + """Helpdesk Forms service.""" + + def publish(self, resource_id: str, resource_data: ResourceData | None = None) -> Form: + """Switch form to published state.""" + return self._resource_action(resource_id, "POST", "publish", json=resource_data) + + def unpublish(self, resource_id: str, resource_data: ResourceData | None = None) -> Form: + """Switch form to unpublished state.""" + return self._resource_action(resource_id, "POST", "unpublish", json=resource_data) + + +class AsyncFormsService( + AsyncManagedResourceMixin[Form], + AsyncCollectionMixin[Form], + AsyncService[Form], + FormsServiceConfig, +): + """Async Helpdesk Forms service.""" + + async def publish(self, resource_id: str, resource_data: ResourceData | None = None) -> Form: + """Switch form to published state.""" + return await self._resource_action(resource_id, "POST", "publish", json=resource_data) + + async def unpublish(self, resource_id: str, resource_data: ResourceData | None = None) -> Form: + """Switch form to unpublished state.""" + return await self._resource_action(resource_id, "POST", "unpublish", json=resource_data) diff --git a/mpt_api_client/resources/helpdesk/helpdesk.py b/mpt_api_client/resources/helpdesk/helpdesk.py index d405c3c..2de613b 100644 --- a/mpt_api_client/resources/helpdesk/helpdesk.py +++ b/mpt_api_client/resources/helpdesk/helpdesk.py @@ -2,6 +2,7 @@ from mpt_api_client.resources.helpdesk.cases import AsyncCasesService, CasesService from mpt_api_client.resources.helpdesk.channels import AsyncChannelsService, ChannelsService from mpt_api_client.resources.helpdesk.chats import AsyncChatsService, ChatsService +from mpt_api_client.resources.helpdesk.forms import AsyncFormsService, FormsService from mpt_api_client.resources.helpdesk.parameter_groups import ( AsyncParameterGroupsService, ParameterGroupsService, @@ -44,6 +45,11 @@ def parameters(self) -> ParametersService: # noqa: WPS110 """Parameters service.""" return ParametersService(http_client=self.http_client) + @property + def forms(self) -> FormsService: + """Forms service.""" + return FormsService(http_client=self.http_client) + @property def parameter_groups(self) -> ParameterGroupsService: """Parameter groups service.""" @@ -81,6 +87,11 @@ def parameters(self) -> AsyncParametersService: # noqa: WPS110 """Async parameters service.""" return AsyncParametersService(http_client=self.http_client) + @property + def forms(self) -> AsyncFormsService: + """Async forms service.""" + return AsyncFormsService(http_client=self.http_client) + @property def parameter_groups(self) -> AsyncParameterGroupsService: """Async parameter groups service.""" diff --git a/pyproject.toml b/pyproject.toml index e1f5ae6..47a4c53 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -122,7 +122,7 @@ per-file-ignores = [ "mpt_api_client/resources/catalog/*.py: WPS110 WPS214 WPS215 WPS235", "mpt_api_client/resources/catalog/products.py: WPS204 WPS214 WPS215 WPS235", "mpt_api_client/resources/commerce/*.py: WPS235 WPS215", - "mpt_api_client/resources/helpdesk/*.py: WPS204 WPS215", + "mpt_api_client/resources/helpdesk/*.py: WPS204 WPS215 WPS214", "mpt_api_client/rql/query_builder.py: WPS110 WPS115 WPS210 WPS214", "tests/e2e/accounts/*.py: WPS430 WPS202", "tests/e2e/billing/*.py: WPS202 WPS421 WPS118", diff --git a/tests/e2e/helpdesk/forms/__init__.py b/tests/e2e/helpdesk/forms/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/e2e/helpdesk/forms/__init__.py @@ -0,0 +1 @@ + diff --git a/tests/e2e/helpdesk/forms/conftest.py b/tests/e2e/helpdesk/forms/conftest.py new file mode 100644 index 0000000..d2020b2 --- /dev/null +++ b/tests/e2e/helpdesk/forms/conftest.py @@ -0,0 +1,33 @@ +import pytest + +from tests.e2e.helper import ( + async_create_fixture_resource_and_delete, + create_fixture_resource_and_delete, +) + + +@pytest.fixture +def form_data(short_uuid): + return { + "name": f"E2E Helpdesk Form {short_uuid}", + "description": "E2E Created Helpdesk Form", + } + + +@pytest.fixture +def invalid_form_id(): + return "FRM-0000-0000" + + +@pytest.fixture +def created_form(mpt_ops, form_data): + with create_fixture_resource_and_delete(mpt_ops.helpdesk.forms, form_data) as form: + yield form + + +@pytest.fixture +async def async_created_form(async_mpt_ops, form_data): + async with async_create_fixture_resource_and_delete( + async_mpt_ops.helpdesk.forms, form_data + ) as form: + yield form diff --git a/tests/e2e/helpdesk/forms/test_async_forms.py b/tests/e2e/helpdesk/forms/test_async_forms.py new file mode 100644 index 0000000..611988c --- /dev/null +++ b/tests/e2e/helpdesk/forms/test_async_forms.py @@ -0,0 +1,60 @@ +import pytest + +from mpt_api_client.exceptions import MPTAPIError + +pytestmark = [pytest.mark.flaky] + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +async def test_get_form(async_mpt_ops, async_created_form): + result = await async_mpt_ops.helpdesk.forms.get(async_created_form.id) + + assert result.id == async_created_form.id + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +async def test_list_forms(async_mpt_ops): + result = await async_mpt_ops.helpdesk.forms.fetch_page(limit=1) + + assert len(result) > 0 + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +def test_create_form(async_created_form): + result = async_created_form + + assert result is not None + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +async def test_update_form(async_mpt_ops, async_created_form, short_uuid): + update_data = {"description": f"e2e update {short_uuid}"} + + result = await async_mpt_ops.helpdesk.forms.update(async_created_form.id, update_data) + + assert result.id == async_created_form.id + assert result.to_dict().get("description") == update_data["description"] + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +async def test_publish_form(async_mpt_ops, async_created_form): + result = await async_mpt_ops.helpdesk.forms.publish(async_created_form.id) + + assert result is not None + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +async def test_unpublish_form(async_mpt_ops, async_created_form): + result = await async_mpt_ops.helpdesk.forms.unpublish(async_created_form.id) + + assert result is not None + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +async def test_delete_form(async_mpt_ops, async_created_form): + await async_mpt_ops.helpdesk.forms.delete(async_created_form.id) # act + + +async def test_not_found(async_mpt_ops, invalid_form_id): + with pytest.raises(MPTAPIError): + await async_mpt_ops.helpdesk.forms.get(invalid_form_id) diff --git a/tests/e2e/helpdesk/forms/test_sync_forms.py b/tests/e2e/helpdesk/forms/test_sync_forms.py new file mode 100644 index 0000000..3100f68 --- /dev/null +++ b/tests/e2e/helpdesk/forms/test_sync_forms.py @@ -0,0 +1,60 @@ +import pytest + +from mpt_api_client.exceptions import MPTAPIError + +pytestmark = [pytest.mark.flaky] + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +def test_get_form(mpt_ops, created_form): + result = mpt_ops.helpdesk.forms.get(created_form.id) + + assert result.id == created_form.id + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +def test_list_forms(mpt_ops): + result = mpt_ops.helpdesk.forms.fetch_page(limit=1) + + assert len(result) > 0 + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +def test_create_form(created_form): + result = created_form + + assert result is not None + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +def test_update_form(mpt_ops, created_form, short_uuid): + update_data = {"description": f"e2e update {short_uuid}"} + + result = mpt_ops.helpdesk.forms.update(created_form.id, update_data) + + assert result.id == created_form.id + assert result.to_dict().get("description") == update_data["description"] + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +def test_publish_form(mpt_ops, created_form): + result = mpt_ops.helpdesk.forms.publish(created_form.id) + + assert result is not None + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +def test_unpublish_form(mpt_ops, created_form): + result = mpt_ops.helpdesk.forms.unpublish(created_form.id) + + assert result is not None + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +def test_delete_form(mpt_ops, created_form): + mpt_ops.helpdesk.forms.delete(created_form.id) # act + + +def test_not_found(mpt_ops, invalid_form_id): + with pytest.raises(MPTAPIError): + mpt_ops.helpdesk.forms.get(invalid_form_id) diff --git a/tests/unit/resources/helpdesk/test_forms.py b/tests/unit/resources/helpdesk/test_forms.py new file mode 100644 index 0000000..190bf1d --- /dev/null +++ b/tests/unit/resources/helpdesk/test_forms.py @@ -0,0 +1,93 @@ +import httpx +import pytest +import respx + +from mpt_api_client.resources.helpdesk.forms import AsyncFormsService, Form, FormsService + + +@pytest.fixture +def forms_service(http_client): + return FormsService(http_client=http_client) + + +@pytest.fixture +def async_forms_service(async_http_client): + return AsyncFormsService(http_client=async_http_client) + + +def test_endpoint(forms_service): + result = forms_service.path == "/public/v1/helpdesk/forms" + + assert result is True + + +def test_async_endpoint(async_forms_service): + result = async_forms_service.path == "/public/v1/helpdesk/forms" + + assert result is True + + +@pytest.mark.parametrize( + "method", + ["get", "create", "update", "delete", "fetch_page", "iterate", "publish", "unpublish"], +) +def test_methods_present(forms_service, method): + result = hasattr(forms_service, method) + + assert result is True + + +@pytest.mark.parametrize( + "method", + ["get", "create", "update", "delete", "fetch_page", "iterate", "publish", "unpublish"], +) +def test_async_methods_present(async_forms_service, method): + result = hasattr(async_forms_service, method) + + assert result is True + + +@pytest.mark.parametrize("action", ["publish", "unpublish"]) +def test_custom_resource_actions(forms_service, action): + response_expected_data = {"id": "FRM-1234-5678", "status": "Updated"} + with respx.mock: + mock_route = respx.post( + f"https://api.example.com/public/v1/helpdesk/forms/FRM-1234-5678/{action}" + ).mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + headers={"content-type": "application/json"}, + json=response_expected_data, + ) + ) + + result = getattr(forms_service, action)("FRM-1234-5678") + + assert mock_route.call_count == 1 + request = mock_route.calls[0].request + assert request.content == b"" + assert result.to_dict() == response_expected_data + assert isinstance(result, Form) + + +@pytest.mark.parametrize("action", ["publish", "unpublish"]) +async def test_async_custom_resource_actions(async_forms_service, action): + response_expected_data = {"id": "FRM-1234-5678", "status": "Updated"} + with respx.mock: + mock_route = respx.post( + f"https://api.example.com/public/v1/helpdesk/forms/FRM-1234-5678/{action}" + ).mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + headers={"content-type": "application/json"}, + json=response_expected_data, + ) + ) + + result = await getattr(async_forms_service, action)("FRM-1234-5678") + + assert mock_route.call_count == 1 + request = mock_route.calls[0].request + assert request.content == b"" + assert result.to_dict() == response_expected_data + assert isinstance(result, Form) diff --git a/tests/unit/resources/helpdesk/test_helpdesk.py b/tests/unit/resources/helpdesk/test_helpdesk.py index afb350a..fec3334 100644 --- a/tests/unit/resources/helpdesk/test_helpdesk.py +++ b/tests/unit/resources/helpdesk/test_helpdesk.py @@ -4,6 +4,7 @@ from mpt_api_client.resources.helpdesk.cases import AsyncCasesService, CasesService from mpt_api_client.resources.helpdesk.channels import AsyncChannelsService, ChannelsService from mpt_api_client.resources.helpdesk.chats import AsyncChatsService, ChatsService +from mpt_api_client.resources.helpdesk.forms import AsyncFormsService, FormsService from mpt_api_client.resources.helpdesk.parameter_groups import ( AsyncParameterGroupsService, ParameterGroupsService, @@ -38,6 +39,7 @@ def test_async_helpdesk_init(async_http_client): ("queues", QueuesService), ("parameters", ParametersService), ("parameter_groups", ParameterGroupsService), + ("forms", FormsService), ], ) def test_helpdesk_properties(http_client, attr_name, expected): @@ -57,6 +59,7 @@ def test_helpdesk_properties(http_client, attr_name, expected): ("queues", AsyncQueuesService), ("parameters", AsyncParametersService), ("parameter_groups", AsyncParameterGroupsService), + ("forms", AsyncFormsService), ], ) def test_async_helpdesk_properties(async_http_client, attr_name, expected):