Skip to content

Commit f2682b2

Browse files
committed
feat(helpdesk): introduce chat attachments module with sync and async support
1 parent ec60985 commit f2682b2

File tree

8 files changed

+321
-0
lines changed

8 files changed

+321
-0
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from mpt_api_client.http import AsyncService, Service, mixins
2+
from mpt_api_client.models import Model
3+
4+
5+
class ChatAttachment(Model):
6+
"""Helpdesk Chat Attachment resource."""
7+
8+
9+
class ChatAttachmentsServiceConfig:
10+
"""Helpdesk Chat Attachments service configuration."""
11+
12+
_endpoint = "/public/v1/helpdesk/chats/{chat_id}/attachments"
13+
_model_class = ChatAttachment
14+
_collection_key = "data"
15+
_upload_file_key = "file"
16+
_upload_data_key = "attachment"
17+
18+
19+
class ChatAttachmentsService(
20+
mixins.CreateFileMixin[ChatAttachment],
21+
mixins.UpdateMixin[ChatAttachment],
22+
mixins.DownloadFileMixin[ChatAttachment],
23+
mixins.DeleteMixin,
24+
mixins.GetMixin[ChatAttachment],
25+
mixins.CollectionMixin[ChatAttachment],
26+
Service[ChatAttachment],
27+
ChatAttachmentsServiceConfig,
28+
):
29+
"""Helpdesk Chat Attachments service."""
30+
31+
32+
class AsyncChatAttachmentsService(
33+
mixins.AsyncCreateFileMixin[ChatAttachment],
34+
mixins.AsyncUpdateMixin[ChatAttachment],
35+
mixins.AsyncDownloadFileMixin[ChatAttachment],
36+
mixins.AsyncDeleteMixin,
37+
mixins.AsyncGetMixin[ChatAttachment],
38+
mixins.AsyncCollectionMixin[ChatAttachment],
39+
AsyncService[ChatAttachment],
40+
ChatAttachmentsServiceConfig,
41+
):
42+
"""Async Helpdesk Chat Attachments service."""

mpt_api_client/resources/helpdesk/chats.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
UpdateMixin,
1111
)
1212
from mpt_api_client.models import Model
13+
from mpt_api_client.resources.helpdesk.chat_attachments import (
14+
AsyncChatAttachmentsService,
15+
ChatAttachmentsService,
16+
)
1317
from mpt_api_client.resources.helpdesk.chat_links import (
1418
AsyncChatLinksService,
1519
ChatLinksService,
@@ -42,6 +46,12 @@ class ChatsService(
4246
):
4347
"""Helpdesk Chats service."""
4448

49+
def attachments(self, chat_id: str) -> ChatAttachmentsService:
50+
"""Return chat attachments service."""
51+
return ChatAttachmentsService(
52+
http_client=self.http_client, endpoint_params={"chat_id": chat_id}
53+
)
54+
4555
def messages(self, chat_id: str) -> ChatMessagesService:
4656
"""Return chat messages service."""
4757
return ChatMessagesService(
@@ -63,6 +73,12 @@ class AsyncChatsService(
6373
):
6474
"""Async Helpdesk Chats service."""
6575

76+
def attachments(self, chat_id: str) -> AsyncChatAttachmentsService:
77+
"""Return async chat attachments service."""
78+
return AsyncChatAttachmentsService(
79+
http_client=self.http_client, endpoint_params={"chat_id": chat_id}
80+
)
81+
6682
def messages(self, chat_id: str) -> AsyncChatMessagesService:
6783
"""Return async chat messages service."""
6884
return AsyncChatMessagesService(

tests/e2e/helpdesk/chats/attachment/__init__.py

Whitespace-only changes.
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import pytest
2+
3+
from mpt_api_client.exceptions import MPTAPIError
4+
5+
6+
@pytest.fixture
7+
def chat_attachments_service(mpt_ops, chat_id):
8+
return mpt_ops.helpdesk.chats.attachments(chat_id)
9+
10+
11+
@pytest.fixture
12+
def async_chat_attachments_service(async_mpt_ops, chat_id):
13+
return async_mpt_ops.helpdesk.chats.attachments(chat_id)
14+
15+
16+
@pytest.fixture
17+
def chat_attachment_data(short_uuid):
18+
attachment_name = f"e2e attachment - {short_uuid}"
19+
return {
20+
"name": attachment_name,
21+
"description": attachment_name,
22+
}
23+
24+
25+
@pytest.fixture
26+
def created_chat_attachment(chat_attachments_service, chat_attachment_data, pdf_fd):
27+
chat_attachment = chat_attachments_service.create(chat_attachment_data, file=pdf_fd)
28+
yield chat_attachment
29+
try:
30+
chat_attachments_service.delete(chat_attachment.id)
31+
except MPTAPIError as error:
32+
print( # noqa: WPS421
33+
f"TEARDOWN - Unable to delete chat attachment {chat_attachment.id}: {error.title}"
34+
)
35+
36+
37+
@pytest.fixture
38+
async def async_created_chat_attachment(
39+
async_chat_attachments_service, chat_attachment_data, pdf_fd
40+
):
41+
chat_attachment = await async_chat_attachments_service.create(chat_attachment_data, file=pdf_fd)
42+
yield chat_attachment
43+
try:
44+
await async_chat_attachments_service.delete(chat_attachment.id)
45+
except MPTAPIError as error:
46+
print( # noqa: WPS421
47+
f"TEARDOWN - Unable to delete chat attachment {chat_attachment.id}: {error.title}"
48+
)
49+
50+
51+
@pytest.fixture
52+
def invalid_chat_attachment_id():
53+
return "ATT-0000-0000-0000-0000"
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import pytest
2+
3+
from mpt_api_client.exceptions import MPTAPIError
4+
5+
pytestmark = [pytest.mark.flaky]
6+
7+
8+
async def test_list_chat_attachments(async_chat_attachments_service, async_created_chat_attachment):
9+
result = await async_chat_attachments_service.fetch_page(limit=1)
10+
11+
assert len(result) > 0
12+
13+
14+
def test_create_chat_attachment(async_created_chat_attachment, chat_attachment_data): # noqa: AAA01
15+
assert async_created_chat_attachment.id is not None
16+
assert async_created_chat_attachment.to_dict().get("name") == chat_attachment_data["name"]
17+
18+
19+
async def test_get_chat_attachment(async_chat_attachments_service, async_created_chat_attachment):
20+
result = await async_chat_attachments_service.get(async_created_chat_attachment.id)
21+
22+
assert result.id == async_created_chat_attachment.id
23+
24+
25+
async def test_update_chat_attachment(
26+
async_chat_attachments_service, async_created_chat_attachment, short_uuid
27+
):
28+
updated_name = f"e2e updated attachment - {short_uuid}"
29+
30+
result = await async_chat_attachments_service.update(
31+
async_created_chat_attachment.id,
32+
{"name": updated_name, "description": updated_name},
33+
)
34+
35+
assert result.id == async_created_chat_attachment.id
36+
assert result.to_dict().get("name") == updated_name
37+
38+
39+
async def test_download_chat_attachment(
40+
async_chat_attachments_service, async_created_chat_attachment
41+
):
42+
result = await async_chat_attachments_service.download(
43+
async_created_chat_attachment.id,
44+
accept="application/pdf",
45+
)
46+
47+
assert result.file_contents is not None
48+
49+
50+
async def test_delete_chat_attachment(async_chat_attachments_service, chat_attachment_data, pdf_fd):
51+
created = await async_chat_attachments_service.create(chat_attachment_data, file=pdf_fd)
52+
53+
await async_chat_attachments_service.delete(created.id)
54+
55+
56+
async def test_get_chat_attachment_not_found(
57+
async_chat_attachments_service, invalid_chat_attachment_id
58+
):
59+
with pytest.raises(MPTAPIError, match=r"404 Not Found"):
60+
await async_chat_attachments_service.get(invalid_chat_attachment_id)
61+
62+
63+
async def test_update_chat_attachment_not_found(
64+
async_chat_attachments_service, invalid_chat_attachment_id
65+
):
66+
with pytest.raises(MPTAPIError, match=r"404 Not Found"):
67+
await async_chat_attachments_service.update(
68+
invalid_chat_attachment_id,
69+
{"description": "updated description"},
70+
)
71+
72+
73+
async def test_delete_chat_attachment_not_found(
74+
async_chat_attachments_service, invalid_chat_attachment_id
75+
):
76+
with pytest.raises(MPTAPIError, match=r"404 Not Found"):
77+
await async_chat_attachments_service.delete(invalid_chat_attachment_id)
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import pytest
2+
3+
from mpt_api_client.exceptions import MPTAPIError
4+
5+
pytestmark = [pytest.mark.flaky]
6+
7+
8+
def test_list_chat_attachments(chat_attachments_service, created_chat_attachment):
9+
result = chat_attachments_service.fetch_page(limit=1)
10+
11+
assert len(result) > 0
12+
13+
14+
def test_create_chat_attachment(created_chat_attachment, chat_attachment_data): # noqa: AAA01
15+
assert created_chat_attachment.id is not None
16+
assert created_chat_attachment.to_dict().get("name") == chat_attachment_data["name"]
17+
18+
19+
def test_get_chat_attachment(chat_attachments_service, created_chat_attachment):
20+
result = chat_attachments_service.get(created_chat_attachment.id)
21+
22+
assert result.id == created_chat_attachment.id
23+
24+
25+
def test_update_chat_attachment(chat_attachments_service, created_chat_attachment, short_uuid):
26+
updated_name = f"e2e updated attachment - {short_uuid}"
27+
28+
result = chat_attachments_service.update(
29+
created_chat_attachment.id,
30+
{"name": updated_name, "description": updated_name},
31+
)
32+
33+
assert result.id == created_chat_attachment.id
34+
assert result.to_dict().get("name") == updated_name
35+
36+
37+
def test_download_chat_attachment(chat_attachments_service, created_chat_attachment):
38+
result = chat_attachments_service.download(created_chat_attachment.id, accept="application/pdf")
39+
40+
assert result.file_contents is not None
41+
42+
43+
def test_delete_chat_attachment(chat_attachments_service, chat_attachment_data, pdf_fd): # noqa: AAA01
44+
created = chat_attachments_service.create(chat_attachment_data, file=pdf_fd)
45+
46+
chat_attachments_service.delete(created.id)
47+
48+
49+
def test_get_chat_attachment_not_found(chat_attachments_service, invalid_chat_attachment_id):
50+
with pytest.raises(MPTAPIError, match=r"404 Not Found"):
51+
chat_attachments_service.get(invalid_chat_attachment_id)
52+
53+
54+
def test_update_chat_attachment_not_found(chat_attachments_service, invalid_chat_attachment_id):
55+
with pytest.raises(MPTAPIError, match=r"404 Not Found"):
56+
chat_attachments_service.update(
57+
invalid_chat_attachment_id,
58+
{"description": "updated description"},
59+
)
60+
61+
62+
def test_delete_chat_attachment_not_found(chat_attachments_service, invalid_chat_attachment_id):
63+
with pytest.raises(MPTAPIError, match=r"404 Not Found"):
64+
chat_attachments_service.delete(invalid_chat_attachment_id)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import pytest
2+
3+
from mpt_api_client.resources.helpdesk.chat_attachments import (
4+
AsyncChatAttachmentsService,
5+
ChatAttachmentsService,
6+
)
7+
8+
9+
@pytest.fixture
10+
def chat_attachments_service(http_client) -> ChatAttachmentsService:
11+
return ChatAttachmentsService(
12+
http_client=http_client, endpoint_params={"chat_id": "CHT-0000-0000-0001"}
13+
)
14+
15+
16+
@pytest.fixture
17+
def async_chat_attachments_service(async_http_client) -> AsyncChatAttachmentsService:
18+
return AsyncChatAttachmentsService(
19+
http_client=async_http_client, endpoint_params={"chat_id": "CHT-0000-0000-0001"}
20+
)
21+
22+
23+
def test_endpoint(chat_attachments_service) -> None:
24+
expected_path = "/public/v1/helpdesk/chats/CHT-0000-0000-0001/attachments"
25+
26+
result = chat_attachments_service.path == expected_path
27+
28+
assert result is True
29+
30+
31+
def test_async_endpoint(async_chat_attachments_service) -> None:
32+
result = (
33+
async_chat_attachments_service.path
34+
== "/public/v1/helpdesk/chats/CHT-0000-0000-0001/attachments"
35+
)
36+
37+
assert result is True
38+
39+
40+
@pytest.mark.parametrize("method", ["get", "create", "update", "delete", "iterate", "download"])
41+
def test_methods_present(chat_attachments_service, method: str) -> None:
42+
result = hasattr(chat_attachments_service, method)
43+
44+
assert result is True
45+
46+
47+
@pytest.mark.parametrize("method", ["get", "create", "update", "delete", "iterate", "download"])
48+
def test_async_methods_present(async_chat_attachments_service, method: str) -> None:
49+
result = hasattr(async_chat_attachments_service, method)
50+
51+
assert result is True

tests/unit/resources/helpdesk/test_chats.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import pytest
22

3+
from mpt_api_client.resources.helpdesk.chat_attachments import (
4+
AsyncChatAttachmentsService,
5+
ChatAttachmentsService,
6+
)
37
from mpt_api_client.resources.helpdesk.chat_links import (
48
AsyncChatLinksService,
59
ChatLinksService,
@@ -55,6 +59,13 @@ def test_property_services(chats_service, service_method, expected_service_class
5559
assert result.endpoint_params == {"chat_id": "CHT-0000-0000-0001"}
5660

5761

62+
def test_attachments_service(chats_service):
63+
result = chats_service.attachments("CHT-0000-0000-0001")
64+
65+
assert isinstance(result, ChatAttachmentsService)
66+
assert result.endpoint_params == {"chat_id": "CHT-0000-0000-0001"}
67+
68+
5869
@pytest.mark.parametrize(
5970
("service_method", "expected_service_class"),
6071
[
@@ -67,3 +78,10 @@ def test_async_property_services(async_chats_service, service_method, expected_s
6778

6879
assert isinstance(result, expected_service_class)
6980
assert result.endpoint_params == {"chat_id": "CHT-0000-0000-0001"}
81+
82+
83+
def test_async_attachments_service(async_chats_service):
84+
result = async_chats_service.attachments("CHT-0000-0000-0001")
85+
86+
assert isinstance(result, AsyncChatAttachmentsService)
87+
assert result.endpoint_params == {"chat_id": "CHT-0000-0000-0001"}

0 commit comments

Comments
 (0)