From ca33fd5bd869f7b25255bac5f9ad9d3d7609e8b4 Mon Sep 17 00:00:00 2001 From: Lukasz Lancucki Date: Tue, 24 Mar 2026 16:03:18 +0000 Subject: [PATCH] feat: add helpdesk channel api services and tests --- .../resources/helpdesk/channel_messages.py | 27 ++++++++ mpt_api_client/resources/helpdesk/channels.py | 56 ++++++++++++++++ mpt_api_client/resources/helpdesk/helpdesk.py | 11 ++++ tests/e2e/helpdesk/channels/conftest.py | 35 ++++++++++ .../helpdesk/channels/messages/conftest.py | 11 ++++ .../channels/messages/test_async_messages.py | 17 +++++ .../channels/messages/test_sync_messages.py | 17 +++++ .../helpdesk/channels/test_async_channels.py | 55 ++++++++++++++++ .../helpdesk/channels/test_sync_channels.py | 55 ++++++++++++++++ .../helpdesk/test_channel_messages.py | 66 +++++++++++++++++++ .../unit/resources/helpdesk/test_channels.py | 63 ++++++++++++++++++ .../unit/resources/helpdesk/test_helpdesk.py | 3 + 12 files changed, 416 insertions(+) create mode 100644 mpt_api_client/resources/helpdesk/channel_messages.py create mode 100644 mpt_api_client/resources/helpdesk/channels.py create mode 100644 tests/e2e/helpdesk/channels/conftest.py create mode 100644 tests/e2e/helpdesk/channels/messages/conftest.py create mode 100644 tests/e2e/helpdesk/channels/messages/test_async_messages.py create mode 100644 tests/e2e/helpdesk/channels/messages/test_sync_messages.py create mode 100644 tests/e2e/helpdesk/channels/test_async_channels.py create mode 100644 tests/e2e/helpdesk/channels/test_sync_channels.py create mode 100644 tests/unit/resources/helpdesk/test_channel_messages.py create mode 100644 tests/unit/resources/helpdesk/test_channels.py diff --git a/mpt_api_client/resources/helpdesk/channel_messages.py b/mpt_api_client/resources/helpdesk/channel_messages.py new file mode 100644 index 00000000..5bbcb4f1 --- /dev/null +++ b/mpt_api_client/resources/helpdesk/channel_messages.py @@ -0,0 +1,27 @@ +from mpt_api_client.http import AsyncService, Service +from mpt_api_client.http.mixins import AsyncCollectionMixin, CollectionMixin +from mpt_api_client.resources.helpdesk.chat_messages import ChatMessage + + +class ChannelMessagesServiceConfig: + """Helpdesk channel messages service configuration.""" + + _endpoint = "/public/v1/helpdesk/channels/{channel_id}/messages" + _model_class = ChatMessage + _collection_key = "data" + + +class ChannelMessagesService( + CollectionMixin[ChatMessage], + Service[ChatMessage], + ChannelMessagesServiceConfig, +): + """Helpdesk channel messages service.""" + + +class AsyncChannelMessagesService( + AsyncCollectionMixin[ChatMessage], + AsyncService[ChatMessage], + ChannelMessagesServiceConfig, +): + """Async helpdesk channel messages service.""" diff --git a/mpt_api_client/resources/helpdesk/channels.py b/mpt_api_client/resources/helpdesk/channels.py new file mode 100644 index 00000000..896df80c --- /dev/null +++ b/mpt_api_client/resources/helpdesk/channels.py @@ -0,0 +1,56 @@ +from mpt_api_client.http import AsyncService, Service, mixins +from mpt_api_client.models import Model +from mpt_api_client.resources.helpdesk.channel_messages import ( + AsyncChannelMessagesService, + ChannelMessagesService, +) + + +class Channel(Model): + """Helpdesk Channel resource.""" + + +class ChannelsServiceConfig: + """Helpdesk Channels service configuration.""" + + _endpoint = "/public/v1/helpdesk/channels" + _model_class = Channel + _collection_key = "data" + + +class ChannelsService( + mixins.CreateMixin[Channel], + mixins.UpdateMixin[Channel], + mixins.GetMixin[Channel], + mixins.DeleteMixin, + mixins.CollectionMixin[Channel], + Service[Channel], + ChannelsServiceConfig, +): + """Helpdesk Channels service.""" + + def messages(self, channel_id: str) -> ChannelMessagesService: + """Return channel messages service.""" + return ChannelMessagesService( + http_client=self.http_client, + endpoint_params={"channel_id": channel_id}, + ) + + +class AsyncChannelsService( + mixins.AsyncCreateMixin[Channel], + mixins.AsyncUpdateMixin[Channel], + mixins.AsyncGetMixin[Channel], + mixins.AsyncDeleteMixin, + mixins.AsyncCollectionMixin[Channel], + AsyncService[Channel], + ChannelsServiceConfig, +): + """Async Helpdesk Channels service.""" + + def messages(self, channel_id: str) -> AsyncChannelMessagesService: + """Return async channel messages service.""" + return AsyncChannelMessagesService( + http_client=self.http_client, + endpoint_params={"channel_id": channel_id}, + ) diff --git a/mpt_api_client/resources/helpdesk/helpdesk.py b/mpt_api_client/resources/helpdesk/helpdesk.py index dd0637f2..d405c3c6 100644 --- a/mpt_api_client/resources/helpdesk/helpdesk.py +++ b/mpt_api_client/resources/helpdesk/helpdesk.py @@ -1,5 +1,6 @@ from mpt_api_client.http import AsyncHTTPClient, HTTPClient 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.parameter_groups import ( AsyncParameterGroupsService, @@ -23,6 +24,11 @@ def chats(self) -> ChatsService: """Chats service.""" return ChatsService(http_client=self.http_client) + @property + def channels(self) -> ChannelsService: + """Channels service.""" + return ChannelsService(http_client=self.http_client) + @property def cases(self) -> CasesService: """Cases service.""" @@ -55,6 +61,11 @@ def chats(self) -> AsyncChatsService: """Async Chats service.""" return AsyncChatsService(http_client=self.http_client) + @property + def channels(self) -> AsyncChannelsService: + """Async Channels service.""" + return AsyncChannelsService(http_client=self.http_client) + @property def cases(self) -> AsyncCasesService: """Async Cases service.""" diff --git a/tests/e2e/helpdesk/channels/conftest.py b/tests/e2e/helpdesk/channels/conftest.py new file mode 100644 index 00000000..e4b5a0f3 --- /dev/null +++ b/tests/e2e/helpdesk/channels/conftest.py @@ -0,0 +1,35 @@ +import pytest + +from tests.e2e.helper import ( + async_create_fixture_resource_and_delete, + create_fixture_resource_and_delete, +) + + +@pytest.fixture +def channel_id(e2e_config): + return e2e_config["helpdesk.channel.id"] + + +@pytest.fixture +def invalid_channel_id(): + return "CHN-0000-0000-0000" + + +@pytest.fixture +def channel_data(short_uuid): + return {"name": f"E2E Channel {short_uuid}"} + + +@pytest.fixture +def created_channel(mpt_ops, channel_data): + with create_fixture_resource_and_delete(mpt_ops.helpdesk.channels, channel_data) as channel: + yield channel + + +@pytest.fixture +async def async_created_channel(async_mpt_ops, channel_data): + async with async_create_fixture_resource_and_delete( + async_mpt_ops.helpdesk.channels, channel_data + ) as channel: + yield channel diff --git a/tests/e2e/helpdesk/channels/messages/conftest.py b/tests/e2e/helpdesk/channels/messages/conftest.py new file mode 100644 index 00000000..28ed8725 --- /dev/null +++ b/tests/e2e/helpdesk/channels/messages/conftest.py @@ -0,0 +1,11 @@ +import pytest + + +@pytest.fixture +def channel_messages_service(mpt_ops, channel_id): + return mpt_ops.helpdesk.channels.messages(channel_id) + + +@pytest.fixture +def async_channel_messages_service(async_mpt_ops, channel_id): + return async_mpt_ops.helpdesk.channels.messages(channel_id) diff --git a/tests/e2e/helpdesk/channels/messages/test_async_messages.py b/tests/e2e/helpdesk/channels/messages/test_async_messages.py new file mode 100644 index 00000000..32670ceb --- /dev/null +++ b/tests/e2e/helpdesk/channels/messages/test_async_messages.py @@ -0,0 +1,17 @@ +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_list_channel_messages(async_channel_messages_service): + result = await async_channel_messages_service.fetch_page(limit=1) + + assert len(result) > 0 + + +async def test_list_channel_messages_not_found(async_mpt_ops, invalid_channel_id): + with pytest.raises(MPTAPIError, match=r"404 Not Found"): + await async_mpt_ops.helpdesk.channels.messages(invalid_channel_id).fetch_page(limit=1) diff --git a/tests/e2e/helpdesk/channels/messages/test_sync_messages.py b/tests/e2e/helpdesk/channels/messages/test_sync_messages.py new file mode 100644 index 00000000..011ee8ff --- /dev/null +++ b/tests/e2e/helpdesk/channels/messages/test_sync_messages.py @@ -0,0 +1,17 @@ +import pytest + +from mpt_api_client.exceptions import MPTAPIError + +pytestmark = [pytest.mark.flaky] + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +def test_list_channel_messages(channel_messages_service): + result = channel_messages_service.fetch_page(limit=1) + + assert len(result) > 0 + + +def test_list_channel_messages_not_found(mpt_ops, invalid_channel_id): + with pytest.raises(MPTAPIError, match=r"404 Not Found"): + mpt_ops.helpdesk.channels.messages(invalid_channel_id).fetch_page(limit=1) diff --git a/tests/e2e/helpdesk/channels/test_async_channels.py b/tests/e2e/helpdesk/channels/test_async_channels.py new file mode 100644 index 00000000..93a6d960 --- /dev/null +++ b/tests/e2e/helpdesk/channels/test_async_channels.py @@ -0,0 +1,55 @@ +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_channel(async_mpt_ops, channel_id): + service = async_mpt_ops.helpdesk.channels + + result = await service.get(channel_id) + + assert result.id == channel_id + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +async def test_list_channels(async_mpt_ops): + service = async_mpt_ops.helpdesk.channels + + result = await service.fetch_page(limit=1) + + assert len(result) > 0 + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +def test_create_channel(async_created_channel): + result = async_created_channel + + assert result.id is not None + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +async def test_update_channel(async_mpt_ops, async_created_channel, short_uuid): + service = async_mpt_ops.helpdesk.channels + new_name = f"E2E Updated Channel {short_uuid}" + + result = await service.update(async_created_channel.id, {"name": new_name}) + + assert result.id == async_created_channel.id + assert result.to_dict().get("name") == new_name + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +async def test_delete_channel(async_mpt_ops, async_created_channel): + result = async_created_channel + + await async_mpt_ops.helpdesk.channels.delete(result.id) + + +async def test_not_found(async_mpt_ops, invalid_channel_id): + service = async_mpt_ops.helpdesk.channels + + with pytest.raises(MPTAPIError): + await service.get(invalid_channel_id) diff --git a/tests/e2e/helpdesk/channels/test_sync_channels.py b/tests/e2e/helpdesk/channels/test_sync_channels.py new file mode 100644 index 00000000..fa21ccd0 --- /dev/null +++ b/tests/e2e/helpdesk/channels/test_sync_channels.py @@ -0,0 +1,55 @@ +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_channel(mpt_ops, channel_id): + service = mpt_ops.helpdesk.channels + + result = service.get(channel_id) + + assert result.id == channel_id + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +def test_list_channels(mpt_ops): + service = mpt_ops.helpdesk.channels + + result = service.fetch_page(limit=1) + + assert len(result) > 0 + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +def test_create_channel(created_channel): + result = created_channel + + assert result.id is not None + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +def test_update_channel(mpt_ops, created_channel, short_uuid): + service = mpt_ops.helpdesk.channels + new_name = f"E2E Updated Channel {short_uuid}" + + result = service.update(created_channel.id, {"name": new_name}) + + assert result.id == created_channel.id + assert result.to_dict().get("name") == new_name + + +@pytest.mark.skip(reason="Unskip after MPT-19124 completed") +def test_delete_channel(mpt_ops, created_channel): + result = created_channel + + mpt_ops.helpdesk.channels.delete(result.id) + + +def test_not_found(mpt_ops, invalid_channel_id): + service = mpt_ops.helpdesk.channels + + with pytest.raises(MPTAPIError): + service.get(invalid_channel_id) diff --git a/tests/unit/resources/helpdesk/test_channel_messages.py b/tests/unit/resources/helpdesk/test_channel_messages.py new file mode 100644 index 00000000..5a213d7e --- /dev/null +++ b/tests/unit/resources/helpdesk/test_channel_messages.py @@ -0,0 +1,66 @@ +import pytest + +from mpt_api_client.resources.helpdesk.channel_messages import ( + AsyncChannelMessagesService, + ChannelMessagesService, +) + + +@pytest.fixture +def channel_messages_service(http_client) -> ChannelMessagesService: + return ChannelMessagesService( + http_client=http_client, endpoint_params={"channel_id": "CHN-0000-0000-0001"} + ) + + +@pytest.fixture +def async_channel_messages_service(async_http_client) -> AsyncChannelMessagesService: + return AsyncChannelMessagesService( + http_client=async_http_client, + endpoint_params={"channel_id": "CHN-0000-0000-0001"}, + ) + + +def test_endpoint(channel_messages_service) -> None: + result = ( + channel_messages_service.path == "/public/v1/helpdesk/channels/CHN-0000-0000-0001/messages" + ) + + assert result is True + + +def test_async_endpoint(async_channel_messages_service) -> None: + result = ( + async_channel_messages_service.path + == "/public/v1/helpdesk/channels/CHN-0000-0000-0001/messages" + ) + + assert result is True + + +@pytest.mark.parametrize("method", ["fetch_page", "iterate"]) +def test_methods_present(channel_messages_service, method: str) -> None: + result = hasattr(channel_messages_service, method) + + assert result is True + + +@pytest.mark.parametrize("method", ["get", "create", "update", "delete"]) +def test_methods_absent(channel_messages_service, method: str) -> None: + result = hasattr(channel_messages_service, method) + + assert result is False + + +@pytest.mark.parametrize("method", ["fetch_page", "iterate"]) +def test_async_methods_present(async_channel_messages_service, method: str) -> None: + result = hasattr(async_channel_messages_service, method) + + assert result is True + + +@pytest.mark.parametrize("method", ["get", "create", "update", "delete"]) +def test_async_methods_absent(async_channel_messages_service, method: str) -> None: + result = hasattr(async_channel_messages_service, method) + + assert result is False diff --git a/tests/unit/resources/helpdesk/test_channels.py b/tests/unit/resources/helpdesk/test_channels.py new file mode 100644 index 00000000..fb497750 --- /dev/null +++ b/tests/unit/resources/helpdesk/test_channels.py @@ -0,0 +1,63 @@ +import pytest + +from mpt_api_client.resources.helpdesk.channel_messages import ( + AsyncChannelMessagesService, + ChannelMessagesService, +) +from mpt_api_client.resources.helpdesk.channels import AsyncChannelsService, ChannelsService + + +@pytest.fixture +def channels_service(http_client): + return ChannelsService(http_client=http_client) + + +@pytest.fixture +def async_channels_service(async_http_client): + return AsyncChannelsService(http_client=async_http_client) + + +def test_endpoint(channels_service) -> None: + result = channels_service.path == "/public/v1/helpdesk/channels" + + assert result is True + + +def test_async_endpoint(async_channels_service) -> None: + result = async_channels_service.path == "/public/v1/helpdesk/channels" + + assert result is True + + +@pytest.mark.parametrize( + "method", + ["get", "create", "update", "delete", "fetch_page", "iterate", "messages"], +) +def test_methods_present(channels_service, method): + result = hasattr(channels_service, method) + + assert result is True + + +@pytest.mark.parametrize( + "method", + ["get", "create", "update", "delete", "fetch_page", "iterate", "messages"], +) +def test_async_methods_present(async_channels_service, method): + result = hasattr(async_channels_service, method) + + assert result is True + + +def test_messages_service(channels_service): + result = channels_service.messages("CHN-0000-0000-0001") + + assert isinstance(result, ChannelMessagesService) + assert result.endpoint_params == {"channel_id": "CHN-0000-0000-0001"} + + +def test_async_messages_service(async_channels_service): + result = async_channels_service.messages("CHN-0000-0000-0001") + + assert isinstance(result, AsyncChannelMessagesService) + assert result.endpoint_params == {"channel_id": "CHN-0000-0000-0001"} diff --git a/tests/unit/resources/helpdesk/test_helpdesk.py b/tests/unit/resources/helpdesk/test_helpdesk.py index 4c2802cb..afb350a5 100644 --- a/tests/unit/resources/helpdesk/test_helpdesk.py +++ b/tests/unit/resources/helpdesk/test_helpdesk.py @@ -2,6 +2,7 @@ from mpt_api_client.resources.helpdesk import AsyncHelpdesk, Helpdesk 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.parameter_groups import ( AsyncParameterGroupsService, @@ -32,6 +33,7 @@ def test_async_helpdesk_init(async_http_client): ("attr_name", "expected"), [ ("chats", ChatsService), + ("channels", ChannelsService), ("cases", CasesService), ("queues", QueuesService), ("parameters", ParametersService), @@ -50,6 +52,7 @@ def test_helpdesk_properties(http_client, attr_name, expected): ("attr_name", "expected"), [ ("chats", AsyncChatsService), + ("channels", AsyncChannelsService), ("cases", AsyncCasesService), ("queues", AsyncQueuesService), ("parameters", AsyncParametersService),