Skip to content

Commit dbab938

Browse files
committed
MPT-13316 Add catalog products
- Added Catalog - Added ProductsService
1 parent 4981fb8 commit dbab938

12 files changed

Lines changed: 422 additions & 8 deletions

File tree

mpt_api_client/mpt_client.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from typing import Self
22

33
from mpt_api_client.http import AsyncHTTPClient, HTTPClient
4-
from mpt_api_client.resources import AsyncCommerce, Commerce
4+
from mpt_api_client.resources import AsyncCatalog, AsyncCommerce, Catalog, Commerce
55

66

77
class AsyncMPTClient:
@@ -28,7 +28,12 @@ def from_config(cls, api_token: str, base_url: str) -> Self:
2828
return cls(AsyncHTTPClient(base_url=base_url, api_token=api_token))
2929

3030
@property
31-
def commerce(self) -> "AsyncCommerce":
31+
def catalog(self) -> AsyncCatalog:
32+
"""Catalog MPT API Client."""
33+
return AsyncCatalog(http_client=self.http_client)
34+
35+
@property
36+
def commerce(self) -> AsyncCommerce:
3237
"""Commerce MPT API Client."""
3338
return AsyncCommerce(http_client=self.http_client)
3439

@@ -57,11 +62,16 @@ def from_config(cls, api_token: str, base_url: str) -> Self:
5762
return cls(HTTPClient(base_url=base_url, api_token=api_token))
5863

5964
@property
60-
def commerce(self) -> "Commerce":
65+
def commerce(self) -> Commerce:
6166
"""Commerce MPT API Client.
6267
6368
The Commerce API provides a comprehensive set of endpoints
6469
for managing agreements, requests, subscriptions, and orders
6570
within a vendor-client-ops ecosystem.
6671
"""
6772
return Commerce(http_client=self.http_client)
73+
74+
@property
75+
def catalog(self) -> Catalog:
76+
"""Catalog MPT API Client."""
77+
return Catalog(http_client=self.http_client)
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from mpt_api_client.resources.catalog import AsyncCatalog, Catalog
12
from mpt_api_client.resources.commerce import AsyncCommerce, Commerce
23

3-
__all__ = ["AsyncCommerce", "Commerce"] # noqa: WPS410
4+
__all__ = ["AsyncCatalog", "AsyncCommerce", "Catalog", "Commerce"] # noqa: WPS410
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from mpt_api_client.resources.catalog.catalog import AsyncCatalog, Catalog
2+
3+
__all__ = ["AsyncCatalog", "Catalog"] # noqa: WPS410
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from mpt_api_client.http import AsyncHTTPClient, HTTPClient
2+
from mpt_api_client.resources.catalog.products import AsyncProductsService, ProductsService
3+
4+
5+
class Catalog:
6+
"""Catalog MPT API Module."""
7+
8+
def __init__(self, *, http_client: HTTPClient):
9+
self.http_client = http_client
10+
11+
@property
12+
def products(self) -> ProductsService:
13+
"""Products service."""
14+
return ProductsService(http_client=self.http_client)
15+
16+
17+
class AsyncCatalog:
18+
"""Catalog MPT API Module."""
19+
20+
def __init__(self, *, http_client: AsyncHTTPClient):
21+
self.http_client = http_client
22+
23+
@property
24+
def products(self) -> AsyncProductsService:
25+
"""Products service."""
26+
return AsyncProductsService(http_client=self.http_client)
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
from mpt_api_client.models import ResourceData
2+
3+
4+
class ReviewMixin[Model]:
5+
"""Review mixin adds the review action that changes the state to Pending."""
6+
7+
def review(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
8+
"""Update state to Pending.
9+
10+
Args:
11+
resource_id: Resource ID
12+
resource_data: Resource data will be updated
13+
"""
14+
return self._resource_action( # type: ignore[attr-defined, no-any-return]
15+
resource_id, "POST", "review", json=resource_data
16+
)
17+
18+
19+
class PublishMixin[Model]:
20+
"""Publish mixin adds the publish action that changes the state to Published."""
21+
22+
def publish(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
23+
"""Update state to Published.
24+
25+
Args:
26+
resource_id: Resource ID
27+
resource_data: Resource data will be updated
28+
"""
29+
return self._resource_action( # type: ignore[attr-defined, no-any-return]
30+
resource_id, "POST", "publish", json=resource_data
31+
)
32+
33+
34+
class UnpublishMixin[Model]:
35+
"""Unpublish mixin adds the unpublish action that changes the state to Unpublished."""
36+
37+
def unpublish(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
38+
"""Update state to Unpublished.
39+
40+
Args:
41+
resource_id: Resource ID
42+
resource_data: Resource data will be updated
43+
"""
44+
return self._resource_action( # type: ignore[attr-defined, no-any-return]
45+
resource_id, "POST", "unpublish", json=resource_data
46+
)
47+
48+
49+
class AsyncReviewMixin[Model]:
50+
"""Review mixin adds the review action that changes the state to reviewing."""
51+
52+
async def review(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
53+
"""Update state to reviewing.
54+
55+
Args:
56+
resource_id: Resource ID
57+
resource_data: Resource data will be updated
58+
"""
59+
return await self._resource_action( # type: ignore[attr-defined, no-any-return]
60+
resource_id, "POST", "review", json=resource_data
61+
)
62+
63+
64+
class AsyncPublishMixin[Model]:
65+
"""Publish mixin adds the publish action that changes the state to Published."""
66+
67+
async def publish(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
68+
"""Update state to Published.
69+
70+
Args:
71+
resource_id: Resource ID
72+
resource_data: Resource data will be updated
73+
"""
74+
return await self._resource_action( # type: ignore[attr-defined, no-any-return]
75+
resource_id, "POST", "publish", json=resource_data
76+
)
77+
78+
79+
class AsyncUnpublishMixin[Model]:
80+
"""Unpublish mixin adds the unpublish action that changes the state to Unpublished."""
81+
82+
async def unpublish(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
83+
"""Update state to Unpublished.
84+
85+
Args:
86+
resource_id: Resource ID
87+
resource_data: Resource data will be updated
88+
"""
89+
return await self._resource_action( # type: ignore[attr-defined, no-any-return]
90+
resource_id, "POST", "unpublish", json=resource_data
91+
)
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
from mpt_api_client.http import AsyncService, CreateMixin, Service
2+
from mpt_api_client.http.mixins import (
3+
AsyncCreateMixin,
4+
AsyncDeleteMixin,
5+
AsyncUpdateMixin,
6+
DeleteMixin,
7+
UpdateMixin,
8+
)
9+
from mpt_api_client.models import Model
10+
from mpt_api_client.resources.catalog.mixins import (
11+
AsyncPublishMixin,
12+
AsyncReviewMixin,
13+
AsyncUnpublishMixin,
14+
PublishMixin,
15+
ReviewMixin,
16+
UnpublishMixin,
17+
)
18+
19+
20+
class Product(Model):
21+
"""Product resource."""
22+
23+
24+
class ProductsServiceConfig:
25+
"""Products service configuration."""
26+
27+
_endpoint = "/public/v1/catalog/products"
28+
_model_class = Product
29+
_collection_key = "data"
30+
31+
32+
class ProductsService(
33+
CreateMixin[Product],
34+
DeleteMixin,
35+
UpdateMixin[Product],
36+
ReviewMixin[Product],
37+
PublishMixin[Product],
38+
UnpublishMixin[Product],
39+
Service[Product],
40+
ProductsServiceConfig,
41+
):
42+
"""Products service."""
43+
44+
45+
class AsyncProductsService(
46+
AsyncCreateMixin[Product],
47+
AsyncDeleteMixin,
48+
AsyncUpdateMixin[Product],
49+
AsyncReviewMixin[Product],
50+
AsyncPublishMixin[Product],
51+
AsyncUnpublishMixin[Product],
52+
AsyncService[Product],
53+
ProductsServiceConfig,
54+
):
55+
"""Products service."""

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ build-backend = "hatchling.build"
5858
[tool.pytest.ini_options]
5959
testpaths = "tests"
6060
pythonpath = "."
61-
addopts = "--cov=mpt_api_client --cov-report=term-missing --cov-report=html --cov-report=xml"
61+
addopts = "--cov=mpt_api_client --cov-report=term-missing --cov-report=html --cov-report=xml --import-mode=importlib"
6262
log_cli = false
6363
asyncio_mode = "auto"
6464
filterwarnings = [

setup.cfg

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@ extend-ignore =
3333

3434
per-file-ignores =
3535
mpt_api_client/rql/query_builder.py: WPS110 WPS115 WPS210 WPS214
36+
mpt_api_client/resources/catalog/products.py: WPS215
3637
tests/http/test_async_service.py: WPS204 WPS202
3738
tests/http/test_service.py: WPS204 WPS202
39+
3840
tests/*:
3941
# Allow magic strings.
4042
WPS432
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import pytest
2+
3+
from mpt_api_client.http import AsyncHTTPClient
4+
from mpt_api_client.resources.catalog import AsyncCatalog, Catalog
5+
from mpt_api_client.resources.catalog.products import AsyncProductsService, ProductsService
6+
7+
8+
def test_catalog_init(http_client):
9+
catalog = Catalog(http_client=http_client)
10+
11+
assert isinstance(catalog, Catalog)
12+
assert catalog.http_client is http_client
13+
14+
15+
def test_catalog_products_multiple_calls(http_client):
16+
catalog = Catalog(http_client=http_client)
17+
18+
products_service = catalog.products
19+
products_service_additional = catalog.products
20+
21+
assert products_service is not products_service_additional
22+
assert isinstance(products_service, ProductsService)
23+
assert isinstance(products_service_additional, ProductsService)
24+
25+
26+
def test_async_catalog_init(async_http_client: AsyncHTTPClient):
27+
catalog = AsyncCatalog(http_client=async_http_client)
28+
29+
assert isinstance(catalog, AsyncCatalog)
30+
assert catalog.http_client is async_http_client
31+
32+
33+
@pytest.mark.parametrize(
34+
("attr_name", "expected"),
35+
[
36+
("products", ProductsService),
37+
],
38+
)
39+
def test_catalog_properties(http_client, attr_name, expected):
40+
catalog = Catalog(http_client=http_client)
41+
42+
service = getattr(catalog, attr_name)
43+
44+
assert isinstance(service, expected)
45+
46+
47+
@pytest.mark.parametrize(
48+
("attr_name", "expected"),
49+
[
50+
("products", AsyncProductsService),
51+
],
52+
)
53+
def test_async_catalog_properties(http_client, attr_name, expected):
54+
catalog = AsyncCatalog(http_client=http_client)
55+
56+
service = getattr(catalog, attr_name)
57+
58+
assert isinstance(service, expected)

0 commit comments

Comments
 (0)