Skip to content

Commit ff156f0

Browse files
committed
MPT-14073 Add notifications categories
1 parent 7ecc4f0 commit ff156f0

File tree

9 files changed

+276
-18
lines changed

9 files changed

+276
-18
lines changed

mpt_api_client/mpt_client.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
AsyncBilling,
77
AsyncCatalog,
88
AsyncCommerce,
9+
AsyncNotifications,
910
Audit,
1011
Billing,
1112
Catalog,
1213
Commerce,
14+
Notifications,
1315
)
1416

1517

@@ -56,6 +58,11 @@ def billing(self) -> AsyncBilling:
5658
"""Billing MPT API Client."""
5759
return AsyncBilling(http_client=self.http_client)
5860

61+
@property
62+
def notifications(self) -> AsyncNotifications:
63+
"""Notifications MPT API Client."""
64+
return AsyncNotifications(http_client=self.http_client)
65+
5966

6067
class MPTClient:
6168
"""MPT API Client."""
@@ -104,3 +111,8 @@ def audit(self) -> Audit:
104111
def billing(self) -> Billing:
105112
"""Billing MPT API Client."""
106113
return Billing(http_client=self.http_client)
114+
115+
@property
116+
def notifications(self) -> Notifications:
117+
"""Notifications MPT API Client."""
118+
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
@@ -2,14 +2,17 @@
22
from mpt_api_client.resources.billing import AsyncBilling, Billing
33
from mpt_api_client.resources.catalog import AsyncCatalog, Catalog
44
from mpt_api_client.resources.commerce import AsyncCommerce, Commerce
5+
from mpt_api_client.resources.notifications import AsyncNotifications, Notifications
56

67
__all__ = [ # noqa: WPS410
78
"AsyncAudit",
89
"AsyncBilling",
910
"AsyncCatalog",
1011
"AsyncCommerce",
12+
"AsyncNotifications",
1113
"Audit",
1214
"Billing",
1315
"Catalog",
1416
"Commerce",
17+
"Notifications",
1518
]
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: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,24 @@ extend-ignore =
3232

3333

3434
per-file-ignores =
35+
mpt_api_client/mpt_client.py: WPS235
3536
mpt_api_client/resources/audit/*.py: WPS215
3637
mpt_api_client/resources/billing/*.py: WPS215 WPS202 WPS214 WPS204
3738
mpt_api_client/resources/catalog/*.py: WPS110 WPS215 WPS214
3839
mpt_api_client/resources/commerce/*.py: WPS215
3940
mpt_api_client/rql/query_builder.py: WPS110 WPS115 WPS210 WPS214
4041
mpt_api_client/resources/catalog/products.py: WPS204 WPS214 WPS215
42+
mpt_api_client/resources/notifications/categories.py: WPS215
4143
mpt_api_client/http/mixins.py: WPS202
4244
tests/http/test_async_service.py: WPS204 WPS202
4345
tests/http/test_service.py: WPS204 WPS202
4446
tests/http/test_mixins.py: WPS204 WPS202
4547
tests/resources/catalog/test_products.py: WPS202 WPS210
4648
tests/resources/*/test_mixins.py: WPS118 WPS202 WPS204
49+
mpt_api_client/resources/notifications/categories.py
50+
51+
52+
tests/test_mpt_client.py: WPS235
4753

4854
tests/*:
4955
# 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: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,32 @@
77
AsyncBilling,
88
AsyncCatalog,
99
AsyncCommerce,
10+
AsyncNotifications,
1011
Audit,
1112
Billing,
1213
Catalog,
1314
Commerce,
15+
Notifications,
1416
)
1517
from tests.conftest import API_TOKEN, API_URL
1618

1719

18-
def test_mpt_client() -> None:
20+
@pytest.mark.parametrize(
21+
("resource_name", "expected_type"),
22+
[
23+
("commerce", Commerce),
24+
("catalog", Catalog),
25+
("audit", Audit),
26+
("billing", Billing),
27+
("notifications", Notifications),
28+
],
29+
)
30+
def test_mpt_client(resource_name: str, expected_type: type) -> None:
1931
mpt = MPTClient.from_config(base_url=API_URL, api_token=API_TOKEN)
20-
commerce = mpt.commerce
21-
catalog = mpt.catalog
22-
audit = mpt.audit
23-
billing = mpt.billing
32+
resource = getattr(mpt, resource_name)
2433

2534
assert isinstance(mpt, MPTClient)
26-
assert isinstance(commerce, Commerce)
27-
assert isinstance(catalog, Catalog)
28-
assert isinstance(audit, Audit)
29-
assert isinstance(billing, Billing)
35+
assert isinstance(resource, expected_type)
3036

3137

3238
def test_mpt_client_env(monkeypatch: pytest.MonkeyPatch) -> None:
@@ -39,18 +45,22 @@ def test_mpt_client_env(monkeypatch: pytest.MonkeyPatch) -> None:
3945
assert isinstance(mpt.http_client, HTTPClient)
4046

4147

42-
def test_async_mpt_client() -> None:
48+
@pytest.mark.parametrize(
49+
("resource_name", "expected_type"),
50+
[
51+
("commerce", AsyncCommerce),
52+
("catalog", AsyncCatalog),
53+
("audit", AsyncAudit),
54+
("billing", AsyncBilling),
55+
("notifications", AsyncNotifications),
56+
],
57+
)
58+
def test_async_mpt_client(resource_name: str, expected_type: type) -> None:
4359
mpt = AsyncMPTClient.from_config(base_url=API_URL, api_token=API_TOKEN)
44-
commerce = mpt.commerce
45-
catalog = mpt.catalog
46-
audit = mpt.audit
47-
billing = mpt.billing
60+
resource = getattr(mpt, resource_name)
4861

4962
assert isinstance(mpt, AsyncMPTClient)
50-
assert isinstance(commerce, AsyncCommerce)
51-
assert isinstance(catalog, AsyncCatalog)
52-
assert isinstance(audit, AsyncAudit)
53-
assert isinstance(billing, AsyncBilling)
63+
assert isinstance(resource, expected_type)
5464

5565

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

0 commit comments

Comments
 (0)