diff --git a/mpt_api_client/resources/helpdesk/chat_attachments.py b/mpt_api_client/resources/helpdesk/chat_attachments.py new file mode 100644 index 0000000..f5e6c78 --- /dev/null +++ b/mpt_api_client/resources/helpdesk/chat_attachments.py @@ -0,0 +1,42 @@ +from mpt_api_client.http import AsyncService, Service, mixins +from mpt_api_client.models import Model + + +class ChatAttachment(Model): + """Helpdesk Chat Attachment resource.""" + + +class ChatAttachmentsServiceConfig: + """Helpdesk Chat Attachments service configuration.""" + + _endpoint = "/public/v1/helpdesk/chats/{chat_id}/attachments" + _model_class = ChatAttachment + _collection_key = "data" + _upload_file_key = "file" + _upload_data_key = "attachment" + + +class ChatAttachmentsService( + mixins.CreateFileMixin[ChatAttachment], + mixins.UpdateMixin[ChatAttachment], + mixins.DownloadFileMixin[ChatAttachment], + mixins.DeleteMixin, + mixins.GetMixin[ChatAttachment], + mixins.CollectionMixin[ChatAttachment], + Service[ChatAttachment], + ChatAttachmentsServiceConfig, +): + """Helpdesk Chat Attachments service.""" + + +class AsyncChatAttachmentsService( + mixins.AsyncCreateFileMixin[ChatAttachment], + mixins.AsyncUpdateMixin[ChatAttachment], + mixins.AsyncDownloadFileMixin[ChatAttachment], + mixins.AsyncDeleteMixin, + mixins.AsyncGetMixin[ChatAttachment], + mixins.AsyncCollectionMixin[ChatAttachment], + AsyncService[ChatAttachment], + ChatAttachmentsServiceConfig, +): + """Async Helpdesk Chat Attachments service.""" diff --git a/mpt_api_client/resources/helpdesk/chats.py b/mpt_api_client/resources/helpdesk/chats.py index 62dcbeb..e77e014 100644 --- a/mpt_api_client/resources/helpdesk/chats.py +++ b/mpt_api_client/resources/helpdesk/chats.py @@ -10,6 +10,10 @@ UpdateMixin, ) from mpt_api_client.models import Model +from mpt_api_client.resources.helpdesk.chat_attachments import ( + AsyncChatAttachmentsService, + ChatAttachmentsService, +) from mpt_api_client.resources.helpdesk.chat_messages import ( AsyncChatMessagesService, ChatMessagesService, @@ -38,6 +42,12 @@ class ChatsService( ): """Helpdesk Chats service.""" + def attachments(self, chat_id: str) -> ChatAttachmentsService: + """Return chat attachments service.""" + return ChatAttachmentsService( + http_client=self.http_client, endpoint_params={"chat_id": chat_id} + ) + def messages(self, chat_id: str) -> ChatMessagesService: """Return chat messages service.""" return ChatMessagesService( @@ -55,6 +65,12 @@ class AsyncChatsService( ): """Async Helpdesk Chats service.""" + def attachments(self, chat_id: str) -> AsyncChatAttachmentsService: + """Return async chat attachments service.""" + return AsyncChatAttachmentsService( + http_client=self.http_client, endpoint_params={"chat_id": chat_id} + ) + def messages(self, chat_id: str) -> AsyncChatMessagesService: """Return async chat messages service.""" return AsyncChatMessagesService( diff --git a/tests/e2e/helpdesk/chats/attachment/__init__.py b/tests/e2e/helpdesk/chats/attachment/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/e2e/helpdesk/chats/attachment/conftest.py b/tests/e2e/helpdesk/chats/attachment/conftest.py new file mode 100644 index 0000000..5b09f17 --- /dev/null +++ b/tests/e2e/helpdesk/chats/attachment/conftest.py @@ -0,0 +1,53 @@ +import pytest + +from mpt_api_client.exceptions import MPTAPIError + + +@pytest.fixture +def chat_attachments_service(mpt_ops, chat_id): + return mpt_ops.helpdesk.chats.attachments(chat_id) + + +@pytest.fixture +def async_chat_attachments_service(async_mpt_ops, chat_id): + return async_mpt_ops.helpdesk.chats.attachments(chat_id) + + +@pytest.fixture +def chat_attachment_data(short_uuid): + attachment_name = f"e2e attachment - {short_uuid}" + return { + "name": attachment_name, + "description": attachment_name, + } + + +@pytest.fixture +def created_chat_attachment(chat_attachments_service, chat_attachment_data, pdf_fd): + chat_attachment = chat_attachments_service.create(chat_attachment_data, file=pdf_fd) + yield chat_attachment + try: + chat_attachments_service.delete(chat_attachment.id) + except MPTAPIError as error: + print( # noqa: WPS421 + f"TEARDOWN - Unable to delete chat attachment {chat_attachment.id}: {error.title}" + ) + + +@pytest.fixture +async def async_created_chat_attachment( + async_chat_attachments_service, chat_attachment_data, pdf_fd +): + chat_attachment = await async_chat_attachments_service.create(chat_attachment_data, file=pdf_fd) + yield chat_attachment + try: + await async_chat_attachments_service.delete(chat_attachment.id) + except MPTAPIError as error: + print( # noqa: WPS421 + f"TEARDOWN - Unable to delete chat attachment {chat_attachment.id}: {error.title}" + ) + + +@pytest.fixture +def invalid_chat_attachment_id(): + return "ATT-0000-0000-0000-0000" diff --git a/tests/e2e/helpdesk/chats/attachment/test_async_attachment.py b/tests/e2e/helpdesk/chats/attachment/test_async_attachment.py new file mode 100644 index 0000000..6e8d09a --- /dev/null +++ b/tests/e2e/helpdesk/chats/attachment/test_async_attachment.py @@ -0,0 +1,77 @@ +import pytest + +from mpt_api_client.exceptions import MPTAPIError + +pytestmark = [pytest.mark.flaky] + + +async def test_list_chat_attachments(async_chat_attachments_service, async_created_chat_attachment): + result = await async_chat_attachments_service.fetch_page(limit=1) + + assert len(result) > 0 + + +def test_create_chat_attachment(async_created_chat_attachment, chat_attachment_data): # noqa: AAA01 + assert async_created_chat_attachment.id is not None + assert async_created_chat_attachment.to_dict().get("name") == chat_attachment_data["name"] + + +async def test_get_chat_attachment(async_chat_attachments_service, async_created_chat_attachment): + result = await async_chat_attachments_service.get(async_created_chat_attachment.id) + + assert result.id == async_created_chat_attachment.id + + +async def test_update_chat_attachment( + async_chat_attachments_service, async_created_chat_attachment, short_uuid +): + updated_name = f"e2e updated attachment - {short_uuid}" + + result = await async_chat_attachments_service.update( + async_created_chat_attachment.id, + {"name": updated_name, "description": updated_name}, + ) + + assert result.id == async_created_chat_attachment.id + assert result.to_dict().get("name") == updated_name + + +async def test_download_chat_attachment( + async_chat_attachments_service, async_created_chat_attachment +): + result = await async_chat_attachments_service.download( + async_created_chat_attachment.id, + accept="application/pdf", + ) + + assert result.file_contents is not None + + +async def test_delete_chat_attachment(async_chat_attachments_service, chat_attachment_data, pdf_fd): + created = await async_chat_attachments_service.create(chat_attachment_data, file=pdf_fd) + + await async_chat_attachments_service.delete(created.id) + + +async def test_get_chat_attachment_not_found( + async_chat_attachments_service, invalid_chat_attachment_id +): + with pytest.raises(MPTAPIError, match=r"404 Not Found"): + await async_chat_attachments_service.get(invalid_chat_attachment_id) + + +async def test_update_chat_attachment_not_found( + async_chat_attachments_service, invalid_chat_attachment_id +): + with pytest.raises(MPTAPIError, match=r"404 Not Found"): + await async_chat_attachments_service.update( + invalid_chat_attachment_id, + {"description": "updated description"}, + ) + + +async def test_delete_chat_attachment_not_found( + async_chat_attachments_service, invalid_chat_attachment_id +): + with pytest.raises(MPTAPIError, match=r"404 Not Found"): + await async_chat_attachments_service.delete(invalid_chat_attachment_id) diff --git a/tests/e2e/helpdesk/chats/attachment/test_sync_attachment.py b/tests/e2e/helpdesk/chats/attachment/test_sync_attachment.py new file mode 100644 index 0000000..a40497f --- /dev/null +++ b/tests/e2e/helpdesk/chats/attachment/test_sync_attachment.py @@ -0,0 +1,64 @@ +import pytest + +from mpt_api_client.exceptions import MPTAPIError + +pytestmark = [pytest.mark.flaky] + + +def test_list_chat_attachments(chat_attachments_service, created_chat_attachment): + result = chat_attachments_service.fetch_page(limit=1) + + assert len(result) > 0 + + +def test_create_chat_attachment(created_chat_attachment, chat_attachment_data): # noqa: AAA01 + assert created_chat_attachment.id is not None + assert created_chat_attachment.to_dict().get("name") == chat_attachment_data["name"] + + +def test_get_chat_attachment(chat_attachments_service, created_chat_attachment): + result = chat_attachments_service.get(created_chat_attachment.id) + + assert result.id == created_chat_attachment.id + + +def test_update_chat_attachment(chat_attachments_service, created_chat_attachment, short_uuid): + updated_name = f"e2e updated attachment - {short_uuid}" + + result = chat_attachments_service.update( + created_chat_attachment.id, + {"name": updated_name, "description": updated_name}, + ) + + assert result.id == created_chat_attachment.id + assert result.to_dict().get("name") == updated_name + + +def test_download_chat_attachment(chat_attachments_service, created_chat_attachment): + result = chat_attachments_service.download(created_chat_attachment.id, accept="application/pdf") + + assert result.file_contents is not None + + +def test_delete_chat_attachment(chat_attachments_service, chat_attachment_data, pdf_fd): # noqa: AAA01 + created = chat_attachments_service.create(chat_attachment_data, file=pdf_fd) + + chat_attachments_service.delete(created.id) + + +def test_get_chat_attachment_not_found(chat_attachments_service, invalid_chat_attachment_id): + with pytest.raises(MPTAPIError, match=r"404 Not Found"): + chat_attachments_service.get(invalid_chat_attachment_id) + + +def test_update_chat_attachment_not_found(chat_attachments_service, invalid_chat_attachment_id): + with pytest.raises(MPTAPIError, match=r"404 Not Found"): + chat_attachments_service.update( + invalid_chat_attachment_id, + {"description": "updated description"}, + ) + + +def test_delete_chat_attachment_not_found(chat_attachments_service, invalid_chat_attachment_id): + with pytest.raises(MPTAPIError, match=r"404 Not Found"): + chat_attachments_service.delete(invalid_chat_attachment_id) diff --git a/tests/unit/resources/helpdesk/test_chat_attachments.py b/tests/unit/resources/helpdesk/test_chat_attachments.py new file mode 100644 index 0000000..6d5c1d1 --- /dev/null +++ b/tests/unit/resources/helpdesk/test_chat_attachments.py @@ -0,0 +1,51 @@ +import pytest + +from mpt_api_client.resources.helpdesk.chat_attachments import ( + AsyncChatAttachmentsService, + ChatAttachmentsService, +) + + +@pytest.fixture +def chat_attachments_service(http_client) -> ChatAttachmentsService: + return ChatAttachmentsService( + http_client=http_client, endpoint_params={"chat_id": "CHT-0000-0000-0001"} + ) + + +@pytest.fixture +def async_chat_attachments_service(async_http_client) -> AsyncChatAttachmentsService: + return AsyncChatAttachmentsService( + http_client=async_http_client, endpoint_params={"chat_id": "CHT-0000-0000-0001"} + ) + + +def test_endpoint(chat_attachments_service) -> None: + expected_path = "/public/v1/helpdesk/chats/CHT-0000-0000-0001/attachments" + + result = chat_attachments_service.path == expected_path + + assert result is True + + +def test_async_endpoint(async_chat_attachments_service) -> None: + result = ( + async_chat_attachments_service.path + == "/public/v1/helpdesk/chats/CHT-0000-0000-0001/attachments" + ) + + assert result is True + + +@pytest.mark.parametrize("method", ["get", "create", "update", "delete", "iterate", "download"]) +def test_methods_present(chat_attachments_service, method: str) -> None: + result = hasattr(chat_attachments_service, method) + + assert result is True + + +@pytest.mark.parametrize("method", ["get", "create", "update", "delete", "iterate", "download"]) +def test_async_methods_present(async_chat_attachments_service, method: str) -> None: + result = hasattr(async_chat_attachments_service, method) + + assert result is True diff --git a/tests/unit/resources/helpdesk/test_chats.py b/tests/unit/resources/helpdesk/test_chats.py index 7ad72a9..30a311e 100644 --- a/tests/unit/resources/helpdesk/test_chats.py +++ b/tests/unit/resources/helpdesk/test_chats.py @@ -1,5 +1,9 @@ import pytest +from mpt_api_client.resources.helpdesk.chat_attachments import ( + AsyncChatAttachmentsService, + ChatAttachmentsService, +) from mpt_api_client.resources.helpdesk.chat_messages import ( AsyncChatMessagesService, ChatMessagesService, @@ -44,8 +48,22 @@ def test_messages_service(chats_service): assert result.endpoint_params == {"chat_id": "CHT-0000-0000-0001"} +def test_attachments_service(chats_service): + result = chats_service.attachments("CHT-0000-0000-0001") + + assert isinstance(result, ChatAttachmentsService) + assert result.endpoint_params == {"chat_id": "CHT-0000-0000-0001"} + + def test_async_messages_service(async_chats_service): result = async_chats_service.messages("CHT-0000-0000-0001") assert isinstance(result, AsyncChatMessagesService) assert result.endpoint_params == {"chat_id": "CHT-0000-0000-0001"} + + +def test_async_attachments_service(async_chats_service): + result = async_chats_service.attachments("CHT-0000-0000-0001") + + assert isinstance(result, AsyncChatAttachmentsService) + assert result.endpoint_params == {"chat_id": "CHT-0000-0000-0001"}