Skip to content

Commit c2704a7

Browse files
authored
MPT-12329 Order collection client (#14)
2 parents 2157c1f + 6480edd commit c2704a7

File tree

12 files changed

+268
-6
lines changed

12 files changed

+268
-6
lines changed

mpt_api_client/http/collection.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ class CollectionBaseClient[ResourceType: Resource](ABC): # noqa: WPS214
2323
"""
2424

2525
_endpoint: str
26-
_resource_class: type[Resource]
27-
_collection_class: type[Collection[Resource]]
26+
_resource_class: type[ResourceType]
27+
_collection_class: type[Collection[ResourceType]]
2828

2929
def __init__(
3030
self,
@@ -181,7 +181,7 @@ def create(self, resource_data: dict[str, Any]) -> ResourceType:
181181
response = self.mpt_client.post(self._endpoint, json=resource_data)
182182
response.raise_for_status()
183183

184-
return self._resource_class.from_response(response) # type: ignore[return-value]
184+
return self._resource_class.from_response(response)
185185

186186
def _fetch_page_as_response(self, limit: int = 100, offset: int = 0) -> httpx.Response:
187187
"""Fetch one page of resources.

mpt_api_client/modules/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from mpt_api_client.modules.order import Order, OrderCollectionClient
2+
3+
__all__ = ["Order", "OrderCollectionClient"] # noqa: WPS410

mpt_api_client/modules/order.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from mpt_api_client.http.collection import CollectionBaseClient
2+
from mpt_api_client.models import Collection, Resource
3+
from mpt_api_client.registry import commerce
4+
5+
6+
class Order(Resource):
7+
"""Order resource."""
8+
9+
10+
@commerce("orders")
11+
class OrderCollectionClient(CollectionBaseClient[Order]):
12+
"""Orders client."""
13+
14+
_endpoint = "/public/v1/commerce/orders"
15+
_resource_class = Order
16+
_collection_class = Collection[Order]

mpt_api_client/mpt.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from mpt_api_client.http.client import MPTClient
2+
from mpt_api_client.modules import OrderCollectionClient
3+
from mpt_api_client.registry import Registry, commerce
4+
5+
6+
class MPT:
7+
"""MPT API Client."""
8+
9+
def __init__(
10+
self,
11+
base_url: str | None = None,
12+
api_key: str | None = None,
13+
registry: Registry | None = None,
14+
mpt_client: MPTClient | None = None,
15+
):
16+
17+
self.mpt_client = mpt_client or MPTClient(base_url=base_url, api_token=api_key)
18+
self.registry: Registry = registry or Registry()
19+
20+
def __getattr__(self, name): # type: ignore[no-untyped-def]
21+
return self.registry.get(name)(client=self.mpt_client)
22+
23+
@property
24+
def commerce(self) -> "CommerceMpt":
25+
"""Commerce MPT API Client.
26+
27+
The Commerce API provides a comprehensive set of endpoints
28+
for managing agreements, requests, subscriptions, and orders
29+
within a vendor-client-ops ecosystem.
30+
"""
31+
return CommerceMpt(mpt_client=self.mpt_client, registry=commerce)
32+
33+
34+
class CommerceMpt(MPT):
35+
"""Commerce MPT API Client."""
36+
37+
@property
38+
def orders(self) -> OrderCollectionClient:
39+
"""Orders MPT API collection.
40+
41+
The Orders API provides a comprehensive set of endpoints
42+
for creating, updating, and retrieving orders.
43+
44+
45+
46+
Returns: Order collection
47+
48+
Examples:
49+
active=RQLQuery("status=active")
50+
for order in mpt.orders.filter(active).iterate():
51+
[...]
52+
53+
"""
54+
return self.registry.get("orders")(client=self.mpt_client) # type: ignore[return-value]

mpt_api_client/registry.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
from collections.abc import Callable
2+
from typing import Any
3+
4+
from mpt_api_client.http.collection import CollectionBaseClient
5+
6+
ItemType = type[CollectionBaseClient[Any]]
7+
8+
9+
class Registry:
10+
"""Registry for MPT collection clients."""
11+
12+
def __init__(self) -> None:
13+
self.items: dict[str, ItemType] = {} # noqa: WPS110
14+
15+
def __call__(self, keyname: str) -> Callable[[ItemType], ItemType]:
16+
"""Decorator to register a CollectionBaseClient class.
17+
18+
Args:
19+
keyname: The key to register the class under
20+
21+
Returns:
22+
The decorator function
23+
24+
Examples:
25+
registry = Registry()
26+
@registry("orders")
27+
class OrderCollectionClient(CollectionBaseClient):
28+
_endpoint = "/api/v1/orders"
29+
_resource_class = Order
30+
31+
registry.get("orders") == OrderCollectionClient
32+
"""
33+
34+
def decorator(cls: ItemType) -> ItemType:
35+
self.register(keyname, cls)
36+
return cls
37+
38+
return decorator
39+
40+
def register(self, keyname: str, item: ItemType) -> None: # noqa: WPS110
41+
"""Register a collection client class with a keyname.
42+
43+
Args:
44+
keyname: The key to register the client under
45+
item: The collection client class to register
46+
"""
47+
self.items[keyname] = item
48+
49+
def get(self, keyname: str) -> ItemType:
50+
"""Get a registered collection client class by keyname.
51+
52+
Args:
53+
keyname: The key to look up
54+
55+
Returns:
56+
The registered collection client class
57+
58+
Raises:
59+
KeyError: If keyname is not registered
60+
"""
61+
if keyname not in self.items:
62+
raise KeyError(f"No collection client registered with keyname: {keyname}")
63+
return self.items[keyname]
64+
65+
def list_keys(self) -> list[str]:
66+
"""Get all registered keynames.
67+
68+
Returns:
69+
List of all registered keynames
70+
"""
71+
return list(self.items.keys())
72+
73+
74+
commerce = Registry()
File renamed without changes.

tests/http/collection/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from mpt_api_client.http.collection import CollectionBaseClient
44
from mpt_api_client.models import Collection
5-
from tests.http.conftest import DummyResource
5+
from tests.conftest import DummyResource
66

77

88
class DummyCollectionClient(CollectionBaseClient[DummyResource]):

tests/http/resource/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from mpt_api_client.http.client import MPTClient
44
from mpt_api_client.http.resource import ResourceBaseClient
5-
from tests.http.conftest import DummyResource
5+
from tests.conftest import DummyResource
66

77

88
class DummyResourceClient(ResourceBaseClient[DummyResource]):

tests/http/test_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from httpx import ConnectTimeout, Response, codes
44

55
from mpt_api_client.http.client import MPTClient
6-
from tests.http.conftest import API_TOKEN, API_URL
6+
from tests.conftest import API_TOKEN, API_URL
77

88

99
def test_mpt_client_initialization():

tests/modules/test_order.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
from mpt_api_client.modules.order import Order, OrderCollectionClient
3+
4+
5+
def test_order_collection_client(mpt_client):
6+
order_cc = OrderCollectionClient(client=mpt_client)
7+
assert order_cc.query_rql is None
8+
9+
10+
def test_order():
11+
order = Order()
12+
assert order is not None

0 commit comments

Comments
 (0)