Skip to content

Commit 8c05542

Browse files
authored
MPT-19030 Add helpdesk chat participants service and tests (#238)
### Summary - Add `ChatParticipantsService` and `AsyncChatParticipantsService` for `/public/v1/helpdesk/chats/{chat_id}/participants` - Wire `participants(chat_id)` into sync and async chat services - Add unit tests for participants service and update chat service nested-resource tests - Add e2e participants test fixtures and sync/async test cases following existing helpdesk patterns ### Testing - `uv run pytest tests/unit/resources/helpdesk/test_chat_participants.py tests/unit/resources/helpdesk/test_chats.py` - `make check-all` - e2e tests: Not run (not requested) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> Closes [MPT-19030](https://softwareone.atlassian.net/browse/MPT-19030) - Add ChatParticipantsService and AsyncChatParticipantsService for the /public/v1/helpdesk/chats/{chat_id}/participants endpoint, including ChatParticipant model and service config. - Wire participants(chat_id) accessors into ChatsService and AsyncChatsService for nested resource access. - Add unit tests verifying service path resolution and presence of create/update/delete/iterate methods for both sync and async participants services. - Add e2e fixtures and flaky sync/async e2e tests (list, create, update, delete, and not-found error cases); tests are marked skipped pending MPT-19124. - Update pyproject.toml formatting and add flake8 per-file-ignores for mpt_api_client/resources/helpdesk/*.py. <!-- end of auto-generated comment: release notes by coderabbit.ai --> [MPT-19030]: https://softwareone.atlassian.net/browse/MPT-19030?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2 parents f785691 + dfbb9b5 commit 8c05542

9 files changed

Lines changed: 267 additions & 0 deletions

File tree

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
from mpt_api_client.http import AsyncService, Service
2+
from mpt_api_client.http.mixins import (
3+
AsyncCollectionMixin,
4+
AsyncCreateMixin,
5+
AsyncDeleteMixin,
6+
AsyncUpdateMixin,
7+
CollectionMixin,
8+
CreateMixin,
9+
DeleteMixin,
10+
UpdateMixin,
11+
)
12+
from mpt_api_client.models import Model
13+
14+
15+
class ChatParticipant(Model):
16+
"""Helpdesk Chat Participant resource."""
17+
18+
19+
class ChatParticipantsServiceConfig:
20+
"""Helpdesk Chat Participants service configuration."""
21+
22+
_endpoint = "/public/v1/helpdesk/chats/{chat_id}/participants"
23+
_model_class = ChatParticipant
24+
_collection_key = "data"
25+
26+
27+
class ChatParticipantsService(
28+
CreateMixin[ChatParticipant],
29+
UpdateMixin[ChatParticipant],
30+
DeleteMixin,
31+
CollectionMixin[ChatParticipant],
32+
Service[ChatParticipant],
33+
ChatParticipantsServiceConfig,
34+
):
35+
"""Helpdesk Chat Participants service."""
36+
37+
38+
class AsyncChatParticipantsService(
39+
AsyncCreateMixin[ChatParticipant],
40+
AsyncUpdateMixin[ChatParticipant],
41+
AsyncDeleteMixin,
42+
AsyncCollectionMixin[ChatParticipant],
43+
AsyncService[ChatParticipant],
44+
ChatParticipantsServiceConfig,
45+
):
46+
"""Async Helpdesk Chat Participants service."""

mpt_api_client/resources/helpdesk/chats.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
AsyncChatMessagesService,
2323
ChatMessagesService,
2424
)
25+
from mpt_api_client.resources.helpdesk.chat_participants import (
26+
AsyncChatParticipantsService,
27+
ChatParticipantsService,
28+
)
2529

2630

2731
class Chat(Model):
@@ -62,6 +66,12 @@ def links(self, chat_id: str) -> ChatLinksService:
6266
"""Return chat links service."""
6367
return ChatLinksService(http_client=self.http_client, endpoint_params={"chat_id": chat_id})
6468

69+
def participants(self, chat_id: str) -> ChatParticipantsService:
70+
"""Return chat participants service."""
71+
return ChatParticipantsService(
72+
http_client=self.http_client, endpoint_params={"chat_id": chat_id}
73+
)
74+
6575

6676
class AsyncChatsService(
6777
AsyncCreateMixin[Chat],
@@ -90,3 +100,9 @@ def links(self, chat_id: str) -> AsyncChatLinksService:
90100
return AsyncChatLinksService(
91101
http_client=self.http_client, endpoint_params={"chat_id": chat_id}
92102
)
103+
104+
def participants(self, chat_id: str) -> AsyncChatParticipantsService:
105+
"""Return async chat participants service."""
106+
return AsyncChatParticipantsService(
107+
http_client=self.http_client, endpoint_params={"chat_id": chat_id}
108+
)

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ per-file-ignores = [
122122
"mpt_api_client/resources/catalog/*.py: WPS110 WPS214 WPS215 WPS235",
123123
"mpt_api_client/resources/catalog/products.py: WPS204 WPS214 WPS215 WPS235",
124124
"mpt_api_client/resources/commerce/*.py: WPS235 WPS215",
125+
"mpt_api_client/resources/helpdesk/*.py: WPS204 WPS215",
125126
"mpt_api_client/rql/query_builder.py: WPS110 WPS115 WPS210 WPS214",
126127
"tests/e2e/accounts/*.py: WPS430 WPS202",
127128
"tests/e2e/billing/*.py: WPS202 WPS421 WPS118",
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import pytest
2+
3+
from tests.e2e.helper import (
4+
async_create_fixture_resource_and_delete,
5+
create_fixture_resource_and_delete,
6+
)
7+
8+
9+
@pytest.fixture
10+
def chat_participants_service(mpt_ops, chat_id):
11+
return mpt_ops.helpdesk.chats.participants(chat_id)
12+
13+
14+
@pytest.fixture
15+
def async_chat_participants_service(async_mpt_ops, chat_id):
16+
return async_mpt_ops.helpdesk.chats.participants(chat_id)
17+
18+
19+
@pytest.fixture
20+
def chat_participant_data(account_id, user_id):
21+
return {
22+
"identity": {"id": user_id},
23+
"account": {"id": account_id},
24+
}
25+
26+
27+
@pytest.fixture
28+
def created_chat_participant(chat_participants_service, chat_participant_data):
29+
with create_fixture_resource_and_delete(
30+
chat_participants_service, chat_participant_data
31+
) as chat_participant:
32+
yield chat_participant
33+
34+
35+
@pytest.fixture
36+
async def async_created_chat_participant(async_chat_participants_service, chat_participant_data):
37+
async with async_create_fixture_resource_and_delete(
38+
async_chat_participants_service, chat_participant_data
39+
) as chat_participant:
40+
yield chat_participant
41+
42+
43+
@pytest.fixture
44+
def invalid_chat_participant_id():
45+
return "CHP-0000-0000-0000"
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import pytest
2+
3+
from mpt_api_client.exceptions import MPTAPIError
4+
5+
pytestmark = [pytest.mark.flaky]
6+
7+
8+
@pytest.mark.skip(reason="Unskip after MPT-19124 completed")
9+
async def test_list_chat_participants(async_chat_participants_service):
10+
result = await async_chat_participants_service.fetch_page(limit=1)
11+
12+
assert len(result) > 0
13+
14+
15+
@pytest.mark.skip(reason="Unskip after MPT-19124 completed") # noqa: AAA01
16+
def test_create_chat_participant(async_created_chat_participant):
17+
assert async_created_chat_participant.id is not None
18+
19+
20+
@pytest.mark.skip(reason="Unskip after MPT-19124 completed")
21+
async def test_update_chat_participant(
22+
async_chat_participants_service, async_created_chat_participant
23+
):
24+
result = await async_chat_participants_service.update(
25+
async_created_chat_participant.id,
26+
{"status": "Active"},
27+
)
28+
29+
assert result.id == async_created_chat_participant.id
30+
31+
32+
@pytest.mark.skip(reason="Unskip after MPT-19124 completed")
33+
async def test_delete_chat_participant(
34+
async_chat_participants_service, async_created_chat_participant
35+
):
36+
result = async_created_chat_participant
37+
38+
await async_chat_participants_service.delete(result.id)
39+
40+
41+
@pytest.mark.skip(reason="Unskip after MPT-19124 completed")
42+
async def test_update_chat_participant_not_found(
43+
async_chat_participants_service, invalid_chat_participant_id
44+
):
45+
with pytest.raises(MPTAPIError, match=r"404 Not Found"):
46+
await async_chat_participants_service.update(
47+
invalid_chat_participant_id,
48+
{"status": "Active"},
49+
)
50+
51+
52+
@pytest.mark.skip(reason="Unskip after MPT-19124 completed")
53+
async def test_delete_chat_participant_not_found(
54+
async_chat_participants_service, invalid_chat_participant_id
55+
):
56+
with pytest.raises(MPTAPIError, match=r"404 Not Found"):
57+
await async_chat_participants_service.delete(invalid_chat_participant_id)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import pytest
2+
3+
from mpt_api_client.exceptions import MPTAPIError
4+
5+
pytestmark = [pytest.mark.flaky]
6+
7+
8+
@pytest.mark.skip(reason="Unskip after MPT-19124 completed")
9+
def test_list_chat_participants(chat_participants_service):
10+
result = chat_participants_service.fetch_page(limit=1)
11+
12+
assert len(result) > 0
13+
14+
15+
@pytest.mark.skip(reason="Unskip after MPT-19124 completed") # noqa: AAA01
16+
def test_create_chat_participant(created_chat_participant):
17+
assert created_chat_participant.id is not None
18+
19+
20+
@pytest.mark.skip(reason="Unskip after MPT-19124 completed")
21+
def test_update_chat_participant(chat_participants_service, created_chat_participant):
22+
result = chat_participants_service.update(created_chat_participant.id, {"status": "Active"})
23+
24+
assert result.id == created_chat_participant.id
25+
26+
27+
@pytest.mark.skip(reason="Unskip after MPT-19124 completed")
28+
def test_delete_chat_participant(chat_participants_service, created_chat_participant):
29+
result = created_chat_participant
30+
31+
chat_participants_service.delete(result.id)
32+
33+
34+
@pytest.mark.skip(reason="Unskip after MPT-19124 completed")
35+
def test_update_chat_participant_not_found(chat_participants_service, invalid_chat_participant_id):
36+
with pytest.raises(MPTAPIError, match=r"404 Not Found"):
37+
chat_participants_service.update(invalid_chat_participant_id, {"status": "Active"})
38+
39+
40+
@pytest.mark.skip(reason="Unskip after MPT-19124 completed")
41+
def test_delete_chat_participant_not_found(chat_participants_service, invalid_chat_participant_id):
42+
with pytest.raises(MPTAPIError, match=r"404 Not Found"):
43+
chat_participants_service.delete(invalid_chat_participant_id)
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import pytest
2+
3+
from mpt_api_client.resources.helpdesk.chat_participants import (
4+
AsyncChatParticipantsService,
5+
ChatParticipantsService,
6+
)
7+
8+
9+
@pytest.fixture
10+
def chat_participants_service(http_client) -> ChatParticipantsService:
11+
return ChatParticipantsService(
12+
http_client=http_client, endpoint_params={"chat_id": "CHT-0000-0000-0001"}
13+
)
14+
15+
16+
@pytest.fixture
17+
def async_chat_participants_service(async_http_client) -> AsyncChatParticipantsService:
18+
return AsyncChatParticipantsService(
19+
http_client=async_http_client, endpoint_params={"chat_id": "CHT-0000-0000-0001"}
20+
)
21+
22+
23+
def test_endpoint(chat_participants_service) -> None:
24+
result = (
25+
chat_participants_service.path
26+
== "/public/v1/helpdesk/chats/CHT-0000-0000-0001/participants"
27+
)
28+
29+
assert result is True
30+
31+
32+
def test_async_endpoint(async_chat_participants_service) -> None:
33+
result = (
34+
async_chat_participants_service.path
35+
== "/public/v1/helpdesk/chats/CHT-0000-0000-0001/participants"
36+
)
37+
38+
assert result is True
39+
40+
41+
@pytest.mark.parametrize("method", ["create", "update", "delete", "iterate"])
42+
def test_methods_present(chat_participants_service, method: str) -> None:
43+
result = hasattr(chat_participants_service, method)
44+
45+
assert result is True
46+
47+
48+
@pytest.mark.parametrize("method", ["create", "update", "delete", "iterate"])
49+
def test_async_methods_present(async_chat_participants_service, method: str) -> None:
50+
result = hasattr(async_chat_participants_service, method)
51+
52+
assert result is True

tests/unit/resources/helpdesk/test_chats.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
AsyncChatMessagesService,
1313
ChatMessagesService,
1414
)
15+
from mpt_api_client.resources.helpdesk.chat_participants import (
16+
AsyncChatParticipantsService,
17+
ChatParticipantsService,
18+
)
1519
from mpt_api_client.resources.helpdesk.chats import AsyncChatsService, ChatsService
1620

1721

@@ -50,6 +54,7 @@ def test_async_mixins_present(async_chats_service, method):
5054
[
5155
("messages", ChatMessagesService),
5256
("links", ChatLinksService),
57+
("participants", ChatParticipantsService),
5358
],
5459
)
5560
def test_property_services(chats_service, service_method, expected_service_class):
@@ -71,6 +76,7 @@ def test_attachments_service(chats_service):
7176
[
7277
("messages", AsyncChatMessagesService),
7378
("links", AsyncChatLinksService),
79+
("participants", AsyncChatParticipantsService),
7480
],
7581
)
7682
def test_async_property_services(async_chats_service, service_method, expected_service_class):

0 commit comments

Comments
 (0)