Skip to content

Commit e4c2953

Browse files
committed
MPT-14073 Add notifications categories
1 parent 605a911 commit e4c2953

File tree

9 files changed

+272
-23
lines changed

9 files changed

+272
-23
lines changed

mpt_api_client/mpt_client.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88
AsyncBilling,
99
AsyncCatalog,
1010
AsyncCommerce,
11+
AsyncNotifications,
1112
Audit,
1213
Billing,
1314
Catalog,
1415
Commerce,
16+
Notifications,
1517
)
1618

1719

@@ -63,6 +65,11 @@ def accounts(self) -> AsyncAccounts:
6365
"""Accounts MPT API Client."""
6466
return AsyncAccounts(http_client=self.http_client)
6567

68+
@property
69+
def notifications(self) -> AsyncNotifications:
70+
"""Notifications MPT API Client."""
71+
return AsyncNotifications(http_client=self.http_client)
72+
6673

6774
class MPTClient:
6875
"""MPT API Client."""
@@ -116,3 +123,8 @@ def billing(self) -> Billing:
116123
def accounts(self) -> Accounts:
117124
"""Accounts MPT API Client."""
118125
return Accounts(http_client=self.http_client)
126+
127+
@property
128+
def notifications(self) -> Notifications:
129+
"""Notifications MPT API Client."""
130+
return Notifications(http_client=self.http_client)

mpt_api_client/resources/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from mpt_api_client.resources.billing import AsyncBilling, Billing
44
from mpt_api_client.resources.catalog import AsyncCatalog, Catalog
55
from mpt_api_client.resources.commerce import AsyncCommerce, Commerce
6+
from mpt_api_client.resources.notifications import AsyncNotifications, Notifications
67

78
__all__ = [ # noqa: WPS410
89
"Accounts",
@@ -11,8 +12,10 @@
1112
"AsyncBilling",
1213
"AsyncCatalog",
1314
"AsyncCommerce",
15+
"AsyncNotifications",
1416
"Audit",
1517
"Billing",
1618
"Catalog",
1719
"Commerce",
20+
"Notifications",
1821
]
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from mpt_api_client.resources.notifications.notifications import AsyncNotifications, Notifications
2+
3+
__all__ = ["AsyncNotifications", "Notifications"] # noqa: WPS410
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
from mpt_api_client.http import AsyncService, CreateMixin, Service
2+
from mpt_api_client.http.mixins import AsyncDeleteMixin, AsyncUpdateMixin, DeleteMixin, UpdateMixin
3+
from mpt_api_client.models import Model, ResourceData
4+
5+
6+
class Category(Model):
7+
"""Notifications Category resource."""
8+
9+
10+
class CategoriesServiceConfig:
11+
"""Notifications Categories service configuration."""
12+
13+
_endpoint = "/public/v1/notifications/categories"
14+
_model_class = Category
15+
_collection_key = "data"
16+
17+
18+
class CategoriesService(
19+
CreateMixin[Category],
20+
UpdateMixin[Category],
21+
DeleteMixin,
22+
Service[Category],
23+
CategoriesServiceConfig,
24+
):
25+
"""Notifications Categories service."""
26+
27+
def publish(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
28+
"""Update state to Published.
29+
30+
Args:
31+
resource_id: Resource ID
32+
resource_data: Resource data will be updated
33+
"""
34+
return self._resource_action(resource_id, "POST", "publish", json=resource_data)
35+
36+
def unpublish(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
37+
"""Update state to Unpublished.
38+
39+
Args:
40+
resource_id: Resource ID
41+
resource_data: Resource data will be updated
42+
"""
43+
return self._resource_action(resource_id, "POST", "unpublish", json=resource_data)
44+
45+
46+
class AsyncCategoriesService(
47+
CreateMixin[Category],
48+
AsyncUpdateMixin[Category],
49+
AsyncDeleteMixin,
50+
AsyncService[Category],
51+
CategoriesServiceConfig,
52+
):
53+
"""Async Notifications Categories service."""
54+
55+
async def publish(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
56+
"""Update state to Published.
57+
58+
Args:
59+
resource_id: Resource ID
60+
resource_data: Resource data will be updated
61+
"""
62+
return await self._resource_action(resource_id, "POST", "publish", json=resource_data)
63+
64+
async def unpublish(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
65+
"""Update state to Unpublished.
66+
67+
Args:
68+
resource_id: Resource ID
69+
resource_data: Resource data will be updated
70+
"""
71+
return await self._resource_action(resource_id, "POST", "unpublish", json=resource_data)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from mpt_api_client.http import AsyncHTTPClient, HTTPClient
2+
from mpt_api_client.resources.notifications.categories import (
3+
AsyncCategoriesService,
4+
CategoriesService,
5+
)
6+
7+
8+
class Notifications:
9+
"""Notifications MPT API Module."""
10+
11+
def __init__(self, http_client: HTTPClient):
12+
self.http_client = http_client
13+
14+
@property
15+
def categories(self) -> CategoriesService:
16+
"""Categories service."""
17+
return CategoriesService(http_client=self.http_client)
18+
19+
20+
class AsyncNotifications:
21+
"""Notifications MPT API Module."""
22+
23+
def __init__(self, http_client: AsyncHTTPClient):
24+
self.http_client = http_client
25+
26+
@property
27+
def categories(self) -> AsyncCategoriesService:
28+
"""Categories service."""
29+
return AsyncCategoriesService(http_client=self.http_client)

setup.cfg

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,22 @@ extend-ignore =
3333

3434
per-file-ignores =
3535
mpt_api_client/resources/accounts/*.py: WPS215
36+
mpt_api_client/mpt_client.py: WPS214 WPS235
3637
mpt_api_client/resources/audit/*.py: WPS215
3738
mpt_api_client/resources/billing/*.py: WPS215 WPS202 WPS214 WPS204
3839
mpt_api_client/resources/catalog/*.py: WPS110 WPS215 WPS214
3940
mpt_api_client/resources/commerce/*.py: WPS215
4041
mpt_api_client/rql/query_builder.py: WPS110 WPS115 WPS210 WPS214
4142
mpt_api_client/resources/catalog/products.py: WPS204 WPS214 WPS215
43+
mpt_api_client/resources/notifications/categories.py: WPS215
4244
mpt_api_client/http/mixins.py: WPS202
4345
mpt_api_client/mpt_client.py: WPS235
4446
tests/http/test_async_service.py: WPS204 WPS202
4547
tests/http/test_service.py: WPS204 WPS202
4648
tests/http/test_mixins.py: WPS204 WPS202
4749
tests/resources/catalog/test_products.py: WPS202 WPS210
4850
tests/resources/*/test_mixins.py: WPS118 WPS202 WPS204
49-
tests/test_mpt.py: WPS210 WPS218 WPS235
51+
tests/test_mpt_client.py: WPS235
5052

5153
tests/*:
5254
# Allow magic strings.
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import httpx
2+
import pytest
3+
import respx
4+
5+
from mpt_api_client.resources.notifications.categories import (
6+
AsyncCategoriesService,
7+
CategoriesService,
8+
)
9+
10+
11+
@pytest.fixture
12+
def categories_service(http_client):
13+
return CategoriesService(http_client=http_client)
14+
15+
16+
@pytest.fixture
17+
def async_categories_service(async_http_client):
18+
return AsyncCategoriesService(http_client=async_http_client)
19+
20+
21+
@pytest.mark.parametrize(
22+
("action", "input_status"),
23+
[
24+
("publish", {"id": "CAT-123", "status": "to_publish"}),
25+
("unpublish", {"id": "CAT-123", "status": "to_unpublish"}),
26+
],
27+
)
28+
def test_custom_category_actions(categories_service, action, input_status):
29+
request_expected_content = b'{"id":"CAT-123","status":"%s"}' % input_status["status"].encode()
30+
response_expected_data = {"id": "CAT-123", "status": "new_status"}
31+
with respx.mock:
32+
mock_route = respx.post(
33+
f"https://api.example.com/public/v1/notifications/categories/CAT-123/{action}"
34+
).mock(
35+
return_value=httpx.Response(
36+
status_code=200,
37+
headers={"content-type": "application/json"},
38+
json=response_expected_data,
39+
)
40+
)
41+
category = getattr(categories_service, action)("CAT-123", input_status)
42+
43+
assert mock_route.call_count == 1
44+
assert category.to_dict() == response_expected_data
45+
request = mock_route.calls[0].request
46+
assert request.content == request_expected_content
47+
48+
49+
@pytest.mark.parametrize(
50+
("action", "input_status"),
51+
[
52+
("publish", {"id": "CAT-123", "status": "to_publish"}),
53+
("unpublish", {"id": "CAT-123", "status": "to_unpublish"}),
54+
],
55+
)
56+
@pytest.mark.asyncio
57+
async def test_async_custom_category_actions(async_categories_service, action, input_status):
58+
request_expected_content = b'{"id":"CAT-123","status":"%s"}' % input_status["status"].encode()
59+
response_expected_data = {"id": "CAT-123", "status": "new_status"}
60+
with respx.mock:
61+
mock_route = respx.post(
62+
f"https://api.example.com/public/v1/notifications/categories/CAT-123/{action}"
63+
).mock(
64+
return_value=httpx.Response(
65+
status_code=200,
66+
headers={"content-type": "application/json"},
67+
json=response_expected_data,
68+
)
69+
)
70+
category = await getattr(async_categories_service, action)("CAT-123", input_status)
71+
72+
assert category.to_dict() == response_expected_data
73+
assert mock_route.call_count == 1
74+
request = mock_route.calls[0].request
75+
assert request.content == request_expected_content
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import pytest
2+
3+
from mpt_api_client.resources import AsyncNotifications, Notifications
4+
from mpt_api_client.resources.notifications.categories import (
5+
AsyncCategoriesService,
6+
CategoriesService,
7+
)
8+
9+
10+
def test_notifications_init(http_client):
11+
commerce = Notifications(http_client=http_client)
12+
13+
assert isinstance(commerce, Notifications)
14+
assert commerce.http_client is http_client
15+
16+
17+
def test_async_notifications_init(async_http_client):
18+
notifications = AsyncNotifications(http_client=async_http_client)
19+
20+
assert isinstance(notifications, AsyncNotifications)
21+
assert notifications.http_client is async_http_client
22+
23+
24+
@pytest.mark.parametrize(
25+
("attr_name", "expected"),
26+
[
27+
("categories", CategoriesService),
28+
],
29+
)
30+
def test_notifications_properties(http_client, attr_name, expected):
31+
commerce = Notifications(http_client=http_client)
32+
33+
service = getattr(commerce, attr_name)
34+
35+
assert isinstance(service, expected)
36+
37+
38+
@pytest.mark.parametrize(
39+
("attr_name", "expected"),
40+
[
41+
("categories", AsyncCategoriesService),
42+
],
43+
)
44+
def test_async_notifications_properties(http_client, attr_name, expected):
45+
commerce = AsyncNotifications(http_client=http_client)
46+
47+
service = getattr(commerce, attr_name)
48+
49+
assert isinstance(service, expected)

tests/test_mpt.py renamed to tests/test_mpt_client.py

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
AsyncBilling,
1010
AsyncCatalog,
1111
AsyncCommerce,
12+
AsyncNotifications,
1213
Audit,
1314
Billing,
1415
Catalog,
1516
Commerce,
17+
Notifications,
1618
)
1719
from tests.conftest import API_TOKEN, API_URL
1820

@@ -21,23 +23,22 @@ def get_mpt_client():
2123
return MPTClient.from_config(base_url=API_URL, api_token=API_TOKEN)
2224

2325

24-
def get_async_mpt_client():
25-
return AsyncMPTClient.from_config(base_url=API_URL, api_token=API_TOKEN)
26-
27-
2826
@pytest.mark.parametrize(
29-
("domain_module", "domain_type"),
27+
("resource_name", "expected_type"),
3028
[
31-
(get_mpt_client(), MPTClient),
32-
(get_mpt_client().commerce, Commerce),
33-
(get_mpt_client().catalog, Catalog),
34-
(get_mpt_client().audit, Audit),
35-
(get_mpt_client().billing, Billing),
36-
(get_mpt_client().accounts, Accounts),
29+
("commerce", Commerce),
30+
("catalog", Catalog),
31+
("audit", Audit),
32+
("billing", Billing),
33+
("accounts", Accounts),
34+
("notifications", Notifications),
3735
],
3836
)
39-
def test_mpt_client(domain_module, domain_type) -> None:
40-
assert isinstance(domain_module, domain_type)
37+
def test_mpt_client(resource_name: str, expected_type: type) -> None:
38+
mpt = MPTClient.from_config(base_url=API_URL, api_token=API_TOKEN)
39+
resource = getattr(mpt, resource_name)
40+
assert isinstance(mpt, MPTClient)
41+
assert isinstance(resource, expected_type)
4142

4243

4344
def test_mpt_client_env(monkeypatch: pytest.MonkeyPatch) -> None:
@@ -51,18 +52,22 @@ def test_mpt_client_env(monkeypatch: pytest.MonkeyPatch) -> None:
5152

5253

5354
@pytest.mark.parametrize(
54-
("domain_module", "domain_type"),
55+
("resource_name", "expected_type"),
5556
[
56-
(get_async_mpt_client(), AsyncMPTClient),
57-
(get_async_mpt_client().commerce, AsyncCommerce),
58-
(get_async_mpt_client().catalog, AsyncCatalog),
59-
(get_async_mpt_client().audit, AsyncAudit),
60-
(get_async_mpt_client().billing, AsyncBilling),
61-
(get_async_mpt_client().accounts, AsyncAccounts),
57+
("commerce", AsyncCommerce),
58+
("catalog", AsyncCatalog),
59+
("audit", AsyncAudit),
60+
("billing", AsyncBilling),
61+
("accounts", AsyncAccounts),
62+
("notifications", AsyncNotifications),
6263
],
6364
)
64-
def test_async_mpt_client(domain_module, domain_type) -> None:
65-
assert isinstance(domain_module, domain_type)
65+
def test_async_mpt_client(resource_name: str, expected_type: type) -> None:
66+
mpt = AsyncMPTClient.from_config(base_url=API_URL, api_token=API_TOKEN)
67+
resource = getattr(mpt, resource_name)
68+
69+
assert isinstance(mpt, AsyncMPTClient)
70+
assert isinstance(resource, expected_type)
6671

6772

6873
def test_async_mpt_client_env(monkeypatch: pytest.MonkeyPatch) -> None:

0 commit comments

Comments
 (0)