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
16 changes: 13 additions & 3 deletions mpt_api_client/mpt_client.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Self

from mpt_api_client.http import AsyncHTTPClient, HTTPClient
from mpt_api_client.resources import AsyncCommerce, Commerce
from mpt_api_client.resources import AsyncCatalog, AsyncCommerce, Catalog, Commerce


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

@property
def commerce(self) -> "AsyncCommerce":
def catalog(self) -> AsyncCatalog:
"""Catalog MPT API Client."""
return AsyncCatalog(http_client=self.http_client)

@property
def commerce(self) -> AsyncCommerce:
"""Commerce MPT API Client."""
return AsyncCommerce(http_client=self.http_client)

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

@property
def commerce(self) -> "Commerce":
def commerce(self) -> Commerce:
"""Commerce MPT API Client.

The Commerce API provides a comprehensive set of endpoints
for managing agreements, requests, subscriptions, and orders
within a vendor-client-ops ecosystem.
"""
return Commerce(http_client=self.http_client)

@property
def catalog(self) -> Catalog:
"""Catalog MPT API Client."""
return Catalog(http_client=self.http_client)
3 changes: 2 additions & 1 deletion mpt_api_client/resources/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from mpt_api_client.resources.catalog import AsyncCatalog, Catalog
from mpt_api_client.resources.commerce import AsyncCommerce, Commerce

__all__ = ["AsyncCommerce", "Commerce"] # noqa: WPS410
__all__ = ["AsyncCatalog", "AsyncCommerce", "Catalog", "Commerce"] # noqa: WPS410
3 changes: 3 additions & 0 deletions mpt_api_client/resources/catalog/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from mpt_api_client.resources.catalog.catalog import AsyncCatalog, Catalog

__all__ = ["AsyncCatalog", "Catalog"] # noqa: WPS410
26 changes: 26 additions & 0 deletions mpt_api_client/resources/catalog/catalog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from mpt_api_client.http import AsyncHTTPClient, HTTPClient
from mpt_api_client.resources.catalog.products import AsyncProductsService, ProductsService


class Catalog:
"""Catalog MPT API Module."""

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

@property
def products(self) -> ProductsService:
"""Products service."""
return ProductsService(http_client=self.http_client)


class AsyncCatalog:
"""Catalog MPT API Module."""

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

@property
def products(self) -> AsyncProductsService:
"""Products service."""
return AsyncProductsService(http_client=self.http_client)
75 changes: 75 additions & 0 deletions mpt_api_client/resources/catalog/mixins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
from mpt_api_client.models import ResourceData


class PublishableMixin[Model]:
"""Publishable mixin adds the ability to review, publish and unpublish."""

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

Args:
resource_id: Resource ID
resource_data: Resource data will be updated
"""
return self._resource_action( # type: ignore[attr-defined, no-any-return]
resource_id, "POST", "review", json=resource_data
)

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( # type: ignore[attr-defined, no-any-return]
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( # type: ignore[attr-defined, no-any-return]
resource_id, "POST", "unpublish", json=resource_data
)


class AsyncPublishableMixin[Model]:
"""Publishable mixin adds the ability to review, publish and unpublish."""

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

Args:
resource_id: Resource ID
resource_data: Resource data will be updated
"""
return await self._resource_action( # type: ignore[attr-defined, no-any-return]
resource_id, "POST", "review", json=resource_data
)

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( # type: ignore[attr-defined, no-any-return]
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( # type: ignore[attr-defined, no-any-return]
resource_id, "POST", "unpublish", json=resource_data
)
47 changes: 47 additions & 0 deletions mpt_api_client/resources/catalog/products.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from mpt_api_client.http import AsyncService, CreateMixin, Service
from mpt_api_client.http.mixins import (
AsyncCreateMixin,
AsyncDeleteMixin,
AsyncUpdateMixin,
DeleteMixin,
UpdateMixin,
)
from mpt_api_client.models import Model
from mpt_api_client.resources.catalog.mixins import (
AsyncPublishableMixin,
PublishableMixin,
)


class Product(Model):
"""Product resource."""


class ProductsServiceConfig:
"""Products service configuration."""

_endpoint = "/public/v1/catalog/products"
_model_class = Product
_collection_key = "data"


class ProductsService(
CreateMixin[Product],
DeleteMixin,
UpdateMixin[Product],
PublishableMixin[Product],
Service[Product],
ProductsServiceConfig,
):
"""Products service."""


class AsyncProductsService(
AsyncCreateMixin[Product],
AsyncDeleteMixin,
AsyncUpdateMixin[Product],
AsyncPublishableMixin[Product],
AsyncService[Product],
ProductsServiceConfig,
):
"""Products service."""
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ build-backend = "hatchling.build"
[tool.pytest.ini_options]
testpaths = "tests"
pythonpath = "."
addopts = "--cov=mpt_api_client --cov-report=term-missing --cov-report=html --cov-report=xml"
addopts = "--cov=mpt_api_client --cov-report=term-missing --cov-report=html --cov-report=xml --import-mode=importlib"
log_cli = false
asyncio_mode = "auto"
filterwarnings = [
Expand Down
2 changes: 2 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ extend-ignore =

per-file-ignores =
mpt_api_client/rql/query_builder.py: WPS110 WPS115 WPS210 WPS214
mpt_api_client/resources/catalog/products.py: WPS215
tests/http/test_async_service.py: WPS204 WPS202
tests/http/test_service.py: WPS204 WPS202

tests/*:
# Allow magic strings.
WPS432
Expand Down
58 changes: 58 additions & 0 deletions tests/resources/catalog/test_catalog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import pytest

from mpt_api_client.http import AsyncHTTPClient
from mpt_api_client.resources.catalog import AsyncCatalog, Catalog
from mpt_api_client.resources.catalog.products import AsyncProductsService, ProductsService


def test_catalog_init(http_client):
catalog = Catalog(http_client=http_client)

assert isinstance(catalog, Catalog)
assert catalog.http_client is http_client


def test_catalog_products_multiple_calls(http_client):
catalog = Catalog(http_client=http_client)

products_service = catalog.products
products_service_additional = catalog.products

assert products_service is not products_service_additional
assert isinstance(products_service, ProductsService)
assert isinstance(products_service_additional, ProductsService)


def test_async_catalog_init(async_http_client: AsyncHTTPClient):
catalog = AsyncCatalog(http_client=async_http_client)

assert isinstance(catalog, AsyncCatalog)
assert catalog.http_client is async_http_client


@pytest.mark.parametrize(
("attr_name", "expected"),
[
("products", ProductsService),
],
)
def test_catalog_properties(http_client, attr_name, expected):
catalog = Catalog(http_client=http_client)

service = getattr(catalog, attr_name)

assert isinstance(service, expected)


@pytest.mark.parametrize(
("attr_name", "expected"),
[
("products", AsyncProductsService),
],
)
def test_async_catalog_properties(http_client, attr_name, expected):
catalog = AsyncCatalog(http_client=http_client)

service = getattr(catalog, attr_name)

assert isinstance(service, expected)
Loading