Skip to content

Commit 518843b

Browse files
authored
MPT-13316 Add catalog products (#29)
- Added Catalog - Added ProductsService
2 parents 4981fb8 + 6828a85 commit 518843b

File tree

12 files changed

+402
-8
lines changed

12 files changed

+402
-8
lines changed

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: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
from mpt_api_client.models import ResourceData
2+
3+
4+
class PublishableMixin[Model]:
5+
"""Publishable mixin adds the ability to review, publish and unpublish."""
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+
def publish(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
19+
"""Update state to Published.
20+
21+
Args:
22+
resource_id: Resource ID
23+
resource_data: Resource data will be updated
24+
"""
25+
return self._resource_action( # type: ignore[attr-defined, no-any-return]
26+
resource_id, "POST", "publish", json=resource_data
27+
)
28+
29+
def unpublish(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
30+
"""Update state to Unpublished.
31+
32+
Args:
33+
resource_id: Resource ID
34+
resource_data: Resource data will be updated
35+
"""
36+
return self._resource_action( # type: ignore[attr-defined, no-any-return]
37+
resource_id, "POST", "unpublish", json=resource_data
38+
)
39+
40+
41+
class AsyncPublishableMixin[Model]:
42+
"""Publishable mixin adds the ability to review, publish and unpublish."""
43+
44+
async def review(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
45+
"""Update state to reviewing.
46+
47+
Args:
48+
resource_id: Resource ID
49+
resource_data: Resource data will be updated
50+
"""
51+
return await self._resource_action( # type: ignore[attr-defined, no-any-return]
52+
resource_id, "POST", "review", json=resource_data
53+
)
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( # type: ignore[attr-defined, no-any-return]
63+
resource_id, "POST", "publish", json=resource_data
64+
)
65+
66+
async def unpublish(self, resource_id: str, resource_data: ResourceData | None = None) -> Model:
67+
"""Update state to Unpublished.
68+
69+
Args:
70+
resource_id: Resource ID
71+
resource_data: Resource data will be updated
72+
"""
73+
return await self._resource_action( # type: ignore[attr-defined, no-any-return]
74+
resource_id, "POST", "unpublish", json=resource_data
75+
)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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+
AsyncPublishableMixin,
12+
PublishableMixin,
13+
)
14+
15+
16+
class Product(Model):
17+
"""Product resource."""
18+
19+
20+
class ProductsServiceConfig:
21+
"""Products service configuration."""
22+
23+
_endpoint = "/public/v1/catalog/products"
24+
_model_class = Product
25+
_collection_key = "data"
26+
27+
28+
class ProductsService(
29+
CreateMixin[Product],
30+
DeleteMixin,
31+
UpdateMixin[Product],
32+
PublishableMixin[Product],
33+
Service[Product],
34+
ProductsServiceConfig,
35+
):
36+
"""Products service."""
37+
38+
39+
class AsyncProductsService(
40+
AsyncCreateMixin[Product],
41+
AsyncDeleteMixin,
42+
AsyncUpdateMixin[Product],
43+
AsyncPublishableMixin[Product],
44+
AsyncService[Product],
45+
ProductsServiceConfig,
46+
):
47+
"""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)