Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions mpt_api_client/mpt_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
AsyncBilling,
AsyncCatalog,
AsyncCommerce,
AsyncNotifications,
Audit,
Billing,
Catalog,
Commerce,
Notifications,
)


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

@property
def notifications(self) -> AsyncNotifications:
"""Notifications MPT API Client."""
return AsyncNotifications(http_client=self.http_client)


class MPTClient:
"""MPT API Client."""
Expand Down Expand Up @@ -116,3 +123,8 @@ def billing(self) -> Billing:
def accounts(self) -> Accounts:
"""Accounts MPT API Client."""
return Accounts(http_client=self.http_client)

@property
def notifications(self) -> Notifications:
"""Notifications MPT API Client."""
return Notifications(http_client=self.http_client)
3 changes: 3 additions & 0 deletions mpt_api_client/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from mpt_api_client.resources.billing import AsyncBilling, Billing
from mpt_api_client.resources.catalog import AsyncCatalog, Catalog
from mpt_api_client.resources.commerce import AsyncCommerce, Commerce
from mpt_api_client.resources.notifications import AsyncNotifications, Notifications

__all__ = [ # noqa: WPS410
"Accounts",
Expand All @@ -11,8 +12,10 @@
"AsyncBilling",
"AsyncCatalog",
"AsyncCommerce",
"AsyncNotifications",
"Audit",
"Billing",
"Catalog",
"Commerce",
"Notifications",
]
3 changes: 3 additions & 0 deletions mpt_api_client/resources/notifications/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from mpt_api_client.resources.notifications.notifications import AsyncNotifications, Notifications

__all__ = ["AsyncNotifications", "Notifications"] # noqa: WPS410
71 changes: 71 additions & 0 deletions mpt_api_client/resources/notifications/categories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
from mpt_api_client.http import AsyncService, CreateMixin, Service
from mpt_api_client.http.mixins import AsyncDeleteMixin, AsyncUpdateMixin, DeleteMixin, UpdateMixin
from mpt_api_client.models import Model, ResourceData


class Category(Model):
"""Notifications Category resource."""


class CategoriesServiceConfig:
"""Notifications Categories service configuration."""

_endpoint = "/public/v1/notifications/categories"
_model_class = Category
_collection_key = "data"


class CategoriesService(
CreateMixin[Category],
UpdateMixin[Category],
DeleteMixin,
Service[Category],
CategoriesServiceConfig,
):
"""Notifications Categories service."""

def publish(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
"""Update state to Published.

Args:
resource_id: Resource ID
resource_data: Resource data will be updated
"""
return self._resource_action(resource_id, "POST", "publish", json=resource_data)

def unpublish(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
"""Update state to Unpublished.

Args:
resource_id: Resource ID
resource_data: Resource data will be updated
"""
return self._resource_action(resource_id, "POST", "unpublish", json=resource_data)


class AsyncCategoriesService(
CreateMixin[Category],
AsyncUpdateMixin[Category],
AsyncDeleteMixin,
AsyncService[Category],
CategoriesServiceConfig,
):
"""Async Notifications Categories service."""

async def publish(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
"""Update state to Published.

Args:
resource_id: Resource ID
resource_data: Resource data will be updated
"""
return await self._resource_action(resource_id, "POST", "publish", json=resource_data)

async def unpublish(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
"""Update state to Unpublished.

Args:
resource_id: Resource ID
resource_data: Resource data will be updated
"""
return await self._resource_action(resource_id, "POST", "unpublish", json=resource_data)
29 changes: 29 additions & 0 deletions mpt_api_client/resources/notifications/notifications.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from mpt_api_client.http import AsyncHTTPClient, HTTPClient
from mpt_api_client.resources.notifications.categories import (
AsyncCategoriesService,
CategoriesService,
)


class Notifications:
"""Notifications MPT API Module."""

def __init__(self, http_client: HTTPClient):
self.http_client = http_client

@property
def categories(self) -> CategoriesService:
"""Categories service."""
return CategoriesService(http_client=self.http_client)


class AsyncNotifications:
"""Notifications MPT API Module."""

def __init__(self, http_client: AsyncHTTPClient):
self.http_client = http_client

@property
def categories(self) -> AsyncCategoriesService:
"""Categories service."""
return AsyncCategoriesService(http_client=self.http_client)
4 changes: 3 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,22 @@ extend-ignore =

per-file-ignores =
mpt_api_client/resources/accounts/*.py: WPS215
mpt_api_client/mpt_client.py: WPS214 WPS235
mpt_api_client/resources/audit/*.py: WPS215
mpt_api_client/resources/billing/*.py: WPS215 WPS202 WPS214 WPS204
mpt_api_client/resources/catalog/*.py: WPS110 WPS215 WPS214
mpt_api_client/resources/commerce/*.py: WPS215
mpt_api_client/rql/query_builder.py: WPS110 WPS115 WPS210 WPS214
mpt_api_client/resources/catalog/products.py: WPS204 WPS214 WPS215
mpt_api_client/resources/notifications/categories.py: WPS215
mpt_api_client/http/mixins.py: WPS202
mpt_api_client/mpt_client.py: WPS235
tests/http/test_async_service.py: WPS204 WPS202
tests/http/test_service.py: WPS204 WPS202
tests/http/test_mixins.py: WPS204 WPS202
tests/resources/catalog/test_products.py: WPS202 WPS210
tests/resources/*/test_mixins.py: WPS118 WPS202 WPS204
tests/test_mpt.py: WPS210 WPS218 WPS235
tests/test_mpt_client.py: WPS235

tests/*:
# Allow magic strings.
Expand Down
75 changes: 75 additions & 0 deletions tests/resources/notifications/test_categories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import httpx
import pytest
import respx

from mpt_api_client.resources.notifications.categories import (
AsyncCategoriesService,
CategoriesService,
)


@pytest.fixture
def categories_service(http_client):
return CategoriesService(http_client=http_client)


@pytest.fixture
def async_categories_service(async_http_client):
return AsyncCategoriesService(http_client=async_http_client)


@pytest.mark.parametrize(
("action", "input_status"),
[
("publish", {"id": "CAT-123", "status": "to_publish"}),
("unpublish", {"id": "CAT-123", "status": "to_unpublish"}),
],
)
def test_custom_category_actions(categories_service, action, input_status):
request_expected_content = b'{"id":"CAT-123","status":"%s"}' % input_status["status"].encode()
response_expected_data = {"id": "CAT-123", "status": "new_status"}
with respx.mock:
mock_route = respx.post(
f"https://api.example.com/public/v1/notifications/categories/CAT-123/{action}"
).mock(
return_value=httpx.Response(
status_code=200,
headers={"content-type": "application/json"},
json=response_expected_data,
)
)
category = getattr(categories_service, action)("CAT-123", input_status)

assert mock_route.call_count == 1
assert category.to_dict() == response_expected_data
request = mock_route.calls[0].request
assert request.content == request_expected_content


@pytest.mark.parametrize(
("action", "input_status"),
[
("publish", {"id": "CAT-123", "status": "to_publish"}),
("unpublish", {"id": "CAT-123", "status": "to_unpublish"}),
],
)
@pytest.mark.asyncio
async def test_async_custom_category_actions(async_categories_service, action, input_status):
request_expected_content = b'{"id":"CAT-123","status":"%s"}' % input_status["status"].encode()
response_expected_data = {"id": "CAT-123", "status": "new_status"}
with respx.mock:
mock_route = respx.post(
f"https://api.example.com/public/v1/notifications/categories/CAT-123/{action}"
).mock(
return_value=httpx.Response(
status_code=200,
headers={"content-type": "application/json"},
json=response_expected_data,
)
)
category = await getattr(async_categories_service, action)("CAT-123", input_status)

assert category.to_dict() == response_expected_data
assert mock_route.call_count == 1
request = mock_route.calls[0].request
assert request.content == request_expected_content
49 changes: 49 additions & 0 deletions tests/resources/notifications/test_notifications.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import pytest

from mpt_api_client.resources import AsyncNotifications, Notifications
from mpt_api_client.resources.notifications.categories import (
AsyncCategoriesService,
CategoriesService,
)


def test_notifications_init(http_client):
commerce = Notifications(http_client=http_client)

assert isinstance(commerce, Notifications)
assert commerce.http_client is http_client


def test_async_notifications_init(async_http_client):
notifications = AsyncNotifications(http_client=async_http_client)

assert isinstance(notifications, AsyncNotifications)
assert notifications.http_client is async_http_client


@pytest.mark.parametrize(
("attr_name", "expected"),
[
("categories", CategoriesService),
],
)
def test_notifications_properties(http_client, attr_name, expected):
commerce = Notifications(http_client=http_client)

service = getattr(commerce, attr_name)

assert isinstance(service, expected)


@pytest.mark.parametrize(
("attr_name", "expected"),
[
("categories", AsyncCategoriesService),
],
)
def test_async_notifications_properties(http_client, attr_name, expected):
commerce = AsyncNotifications(http_client=http_client)

service = getattr(commerce, attr_name)

assert isinstance(service, expected)
49 changes: 27 additions & 22 deletions tests/test_mpt.py → tests/test_mpt_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
AsyncBilling,
AsyncCatalog,
AsyncCommerce,
AsyncNotifications,
Audit,
Billing,
Catalog,
Commerce,
Notifications,
)
from tests.conftest import API_TOKEN, API_URL

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


def get_async_mpt_client():
return AsyncMPTClient.from_config(base_url=API_URL, api_token=API_TOKEN)


@pytest.mark.parametrize(
("domain_module", "domain_type"),
("resource_name", "expected_type"),
[
(get_mpt_client(), MPTClient),
(get_mpt_client().commerce, Commerce),
(get_mpt_client().catalog, Catalog),
(get_mpt_client().audit, Audit),
(get_mpt_client().billing, Billing),
(get_mpt_client().accounts, Accounts),
("commerce", Commerce),
("catalog", Catalog),
("audit", Audit),
("billing", Billing),
("accounts", Accounts),
("notifications", Notifications),
],
)
def test_mpt_client(domain_module, domain_type) -> None:
assert isinstance(domain_module, domain_type)
def test_mpt_client(resource_name: str, expected_type: type) -> None:
mpt = MPTClient.from_config(base_url=API_URL, api_token=API_TOKEN)
resource = getattr(mpt, resource_name)
assert isinstance(mpt, MPTClient)
assert isinstance(resource, expected_type)


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


@pytest.mark.parametrize(
("domain_module", "domain_type"),
("resource_name", "expected_type"),
[
(get_async_mpt_client(), AsyncMPTClient),
(get_async_mpt_client().commerce, AsyncCommerce),
(get_async_mpt_client().catalog, AsyncCatalog),
(get_async_mpt_client().audit, AsyncAudit),
(get_async_mpt_client().billing, AsyncBilling),
(get_async_mpt_client().accounts, AsyncAccounts),
("commerce", AsyncCommerce),
("catalog", AsyncCatalog),
("audit", AsyncAudit),
("billing", AsyncBilling),
("accounts", AsyncAccounts),
("notifications", AsyncNotifications),
],
)
def test_async_mpt_client(domain_module, domain_type) -> None:
assert isinstance(domain_module, domain_type)
def test_async_mpt_client(resource_name: str, expected_type: type) -> None:
mpt = AsyncMPTClient.from_config(base_url=API_URL, api_token=API_TOKEN)
resource = getattr(mpt, resource_name)

assert isinstance(mpt, AsyncMPTClient)
assert isinstance(resource, expected_type)


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