Skip to content

Commit f053c54

Browse files
authored
MPT-12839 Prepare for Commerce agreements (#22)
MPT-12839 Prepare for Commerce agreements - Added select parameter to resource get method - Moved create and delete opreations into mixins for both Sync and Async Services
2 parents c29e3af + 830b3ac commit f053c54

File tree

9 files changed

+250
-117
lines changed

9 files changed

+250
-117
lines changed

mpt_api_client/http/__init__.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
from mpt_api_client.http.async_client import AsyncHTTPClient
22
from mpt_api_client.http.async_service import AsyncService
33
from mpt_api_client.http.client import HTTPClient
4+
from mpt_api_client.http.mixins import AsyncCreateMixin, AsyncDeleteMixin, CreateMixin, DeleteMixin
45
from mpt_api_client.http.service import Service
56

6-
__all__ = ["AsyncHTTPClient", "AsyncService", "HTTPClient", "Service"] # noqa: WPS410
7+
__all__ = [ # noqa: WPS410
8+
"AsyncCreateMixin",
9+
"AsyncDeleteMixin",
10+
"AsyncHTTPClient",
11+
"AsyncService",
12+
"CreateMixin",
13+
"DeleteMixin",
14+
"HTTPClient",
15+
"Service",
16+
]

mpt_api_client/http/async_service.py

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from mpt_api_client.http.async_client import AsyncHTTPClient
77
from mpt_api_client.http.base_service import ServiceBase
8+
from mpt_api_client.http.types import QueryParam
89
from mpt_api_client.models import Collection, ResourceData
910
from mpt_api_client.models import Model as BaseModel
1011
from mpt_api_client.models.collection import ResourceList
@@ -23,7 +24,11 @@ class AsyncService[Model: BaseModel](ServiceBase[AsyncHTTPClient, Model]): # no
2324
"""
2425

2526
async def fetch_page(self, limit: int = 100, offset: int = 0) -> Collection[Model]:
26-
"""Fetch one page of resources."""
27+
"""Fetch one page of resources.
28+
29+
Returns:
30+
Collection of resources.
31+
"""
2732
response = await self._fetch_page_as_response(limit=limit, offset=offset)
2833
return self._create_collection(response)
2934

@@ -72,30 +77,32 @@ async def iterate(self, batch_size: int = 100) -> AsyncIterator[Model]:
7277
break
7378
offset = items_collection.meta.pagination.next_offset()
7479

75-
async def create(self, resource_data: ResourceData) -> Model:
76-
"""Create a new resource using `POST /endpoint`.
80+
async def get(self, resource_id: str, select: list[str] | str | None = None) -> Model:
81+
"""Fetch a specific resource using `GET /endpoint/{resource_id}`.
82+
83+
Args:
84+
resource_id: Resource ID.
85+
select: List of fields to select.
7786
7887
Returns:
79-
New resource created.
88+
Resource object.
8089
"""
81-
response = await self.http_client.post(self._endpoint, json=resource_data)
82-
response.raise_for_status()
90+
if isinstance(select, list):
91+
select = ",".join(select) if select else None
92+
return await self._resource_action(resource_id=resource_id, query_params={"select": select})
8393

84-
return self._model_class.from_response(response)
94+
async def update(self, resource_id: str, resource_data: ResourceData) -> Model:
95+
"""Update a resource using `PUT /endpoint/{resource_id}`.
8596
86-
async def get(self, resource_id: str) -> Model:
87-
"""Fetch a specific resource using `GET /endpoint/{resource_id}`."""
88-
return await self._resource_action(resource_id=resource_id)
97+
Args:
98+
resource_id: Resource ID.
99+
resource_data: Resource data.
89100
90-
async def update(self, resource_id: str, resource_data: ResourceData) -> Model:
91-
"""Update a resource using `PUT /endpoint/{resource_id}`."""
92-
return await self._resource_action(resource_id, "PUT", json=resource_data)
101+
Returns:
102+
Resource object.
93103
94-
async def delete(self, resource_id: str) -> None:
95-
"""Delete resource using `DELETE /endpoint/{resource_id}`."""
96-
url = urljoin(f"{self._endpoint}/", resource_id)
97-
response = await self.http_client.delete(url)
98-
response.raise_for_status()
104+
"""
105+
return await self._resource_action(resource_id, "PUT", json=resource_data)
99106

100107
async def _fetch_page_as_response(self, limit: int = 100, offset: int = 0) -> httpx.Response:
101108
"""Fetch one page of resources.
@@ -118,6 +125,7 @@ async def _resource_do_request(
118125
method: str = "GET",
119126
action: str | None = None,
120127
json: ResourceData | ResourceList | None = None,
128+
query_params: QueryParam | None = None,
121129
) -> httpx.Response:
122130
"""Perform an action on a specific resource using.
123131
@@ -129,13 +137,14 @@ async def _resource_do_request(
129137
method: The HTTP method to use.
130138
action: The action name to use.
131139
json: The updated resource data.
140+
query_params: Additional query parameters.
132141
133142
Raises:
134143
HTTPError: If the action fails.
135144
"""
136145
resource_url = urljoin(f"{self._endpoint}/", resource_id)
137146
url = urljoin(f"{resource_url}/", action) if action else resource_url
138-
response = await self.http_client.request(method, url, json=json)
147+
response = await self.http_client.request(method, url, json=json, params=query_params)
139148
response.raise_for_status()
140149
return response
141150

@@ -145,7 +154,18 @@ async def _resource_action(
145154
method: str = "GET",
146155
action: str | None = None,
147156
json: ResourceData | ResourceList | None = None,
157+
query_params: QueryParam | None = None,
148158
) -> Model:
149-
"""Perform an action on a specific resource using `HTTP_METHOD /endpoint/{resource_id}`."""
150-
response = await self._resource_do_request(resource_id, method, action, json=json)
159+
"""Perform an action on a specific resource using `HTTP_METHOD /endpoint/{resource_id}`.
160+
161+
Args:
162+
resource_id: The resource ID to operate on.
163+
method: The HTTP method to use.
164+
action: The action name to use.
165+
json: The updated resource data.
166+
query_params: Additional query parameters.
167+
"""
168+
response = await self._resource_do_request(
169+
resource_id, method, action, json=json, query_params=query_params
170+
)
151171
return self._model_class.from_response(response)

mpt_api_client/http/mixins.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
from urllib.parse import urljoin
2+
3+
from mpt_api_client.models import ResourceData
4+
5+
6+
class CreateMixin[Model]:
7+
"""Create resource mixin."""
8+
9+
def create(self, resource_data: ResourceData) -> Model:
10+
"""Create a new resource using `POST /endpoint`.
11+
12+
Returns:
13+
New resource created.
14+
"""
15+
response = self.http_client.post(self._endpoint, json=resource_data) # type: ignore[attr-defined]
16+
response.raise_for_status()
17+
18+
return self._model_class.from_response(response) # type: ignore[attr-defined, no-any-return]
19+
20+
21+
class DeleteMixin:
22+
"""Delete resource mixin."""
23+
24+
def delete(self, resource_id: str) -> None:
25+
"""Delete resource using `DELETE /endpoint/{resource_id}`.
26+
27+
Args:
28+
resource_id: Resource ID.
29+
"""
30+
response = self._resource_do_request(resource_id, "DELETE") # type: ignore[attr-defined]
31+
response.raise_for_status()
32+
33+
34+
class AsyncCreateMixin[Model]:
35+
"""Create resource mixin."""
36+
37+
async def create(self, resource_data: ResourceData) -> Model:
38+
"""Create a new resource using `POST /endpoint`.
39+
40+
Returns:
41+
New resource created.
42+
"""
43+
response = await self.http_client.post(self._endpoint, json=resource_data) # type: ignore[attr-defined]
44+
response.raise_for_status()
45+
46+
return self._model_class.from_response(response) # type: ignore[attr-defined, no-any-return]
47+
48+
49+
class AsyncDeleteMixin:
50+
"""Delete resource mixin."""
51+
52+
async def delete(self, resource_id: str) -> None:
53+
"""Delete resource using `DELETE /endpoint/{resource_id}`.
54+
55+
Args:
56+
resource_id: Resource ID.
57+
"""
58+
url = urljoin(f"{self._endpoint}/", resource_id) # type: ignore[attr-defined]
59+
response = await self.http_client.delete(url) # type: ignore[attr-defined]
60+
response.raise_for_status()

mpt_api_client/http/service.py

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from mpt_api_client.http.base_service import ServiceBase
77
from mpt_api_client.http.client import HTTPClient
8+
from mpt_api_client.http.types import QueryParam
89
from mpt_api_client.models import Collection, ResourceData
910
from mpt_api_client.models import Model as BaseModel
1011
from mpt_api_client.models.collection import ResourceList
@@ -75,29 +76,33 @@ def iterate(self, batch_size: int = 100) -> Iterator[Model]:
7576
break
7677
offset = items_collection.meta.pagination.next_offset()
7778

78-
def create(self, resource_data: ResourceData) -> Model:
79-
"""Create a new resource using `POST /endpoint`.
79+
def get(self, resource_id: str, select: list[str] | str | None = None) -> Model:
80+
"""Fetch a specific resource using `GET /endpoint/{resource_id}`.
81+
82+
Args:
83+
resource_id: Resource ID.
84+
select: List of fields to select.
8085
8186
Returns:
82-
New resource created.
87+
Resource object.
8388
"""
84-
response = self.http_client.post(self._endpoint, json=resource_data)
85-
response.raise_for_status()
89+
if isinstance(select, list):
90+
select = ",".join(select) if select else None
8691

87-
return self._model_class.from_response(response)
88-
89-
def get(self, resource_id: str) -> Model:
90-
"""Fetch a specific resource using `GET /endpoint/{resource_id}`."""
91-
return self._resource_action(resource_id=resource_id)
92+
return self._resource_action(resource_id=resource_id, query_params={"select": select})
9293

9394
def update(self, resource_id: str, resource_data: ResourceData) -> Model:
94-
"""Update a resource using `PUT /endpoint/{resource_id}`."""
95-
return self._resource_action(resource_id, "PUT", json=resource_data)
95+
"""Update a resource using `PUT /endpoint/{resource_id}`.
9696
97-
def delete(self, resource_id: str) -> None:
98-
"""Delete the resoruce using `DELETE /endpoint/{resource_id}`."""
99-
response = self._resource_do_request(resource_id, "DELETE")
100-
response.raise_for_status()
97+
Args:
98+
resource_id: Resource ID.
99+
resource_data: Resource data.
100+
101+
Returns:
102+
Resource object.
103+
104+
"""
105+
return self._resource_action(resource_id, "PUT", json=resource_data)
101106

102107
def _fetch_page_as_response(self, limit: int = 100, offset: int = 0) -> httpx.Response:
103108
"""Fetch one page of resources.
@@ -120,6 +125,7 @@ def _resource_do_request(
120125
method: str = "GET",
121126
action: str | None = None,
122127
json: ResourceData | ResourceList | None = None,
128+
query_params: QueryParam | None = None,
123129
) -> httpx.Response:
124130
"""Perform an action on a specific resource using `HTTP_METHOD /endpoint/{resource_id}`.
125131
@@ -128,6 +134,7 @@ def _resource_do_request(
128134
method: The HTTP method to use.
129135
action: The action name to use.
130136
json: The updated resource data.
137+
query_params: Additional query parameters.
131138
132139
Returns:
133140
HTTP response object.
@@ -137,7 +144,7 @@ def _resource_do_request(
137144
"""
138145
resource_url = urljoin(f"{self._endpoint}/", resource_id)
139146
url = urljoin(f"{resource_url}/", action) if action else resource_url
140-
response = self.http_client.request(method, url, json=json)
147+
response = self.http_client.request(method, url, json=json, params=query_params)
141148
response.raise_for_status()
142149
return response
143150

@@ -147,7 +154,18 @@ def _resource_action(
147154
method: str = "GET",
148155
action: str | None = None,
149156
json: ResourceData | ResourceList | None = None,
157+
query_params: QueryParam | None = None,
150158
) -> Model:
151-
"""Perform an action on a specific resource using `HTTP_METHOD /endpoint/{resource_id}`."""
152-
response = self._resource_do_request(resource_id, method, action, json=json)
159+
"""Perform an action on a specific resource using `HTTP_METHOD /endpoint/{resource_id}`.
160+
161+
Args:
162+
resource_id: The resource ID to operate on.
163+
method: The HTTP method to use.
164+
action: The action name to use.
165+
json: The updated resource data.
166+
query_params: Additional query parameters.
167+
"""
168+
response = self._resource_do_request(
169+
resource_id, method, action, json=json, query_params=query_params
170+
)
153171
return self._model_class.from_response(response)

mpt_api_client/http/types.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
PrimitiveType = str | int | float | bool | None
2+
QueryParam = dict[str, PrimitiveType]

mpt_api_client/resources/commerce/orders.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
from mpt_api_client.http import AsyncService, Service
1+
from mpt_api_client.http import (
2+
AsyncCreateMixin,
3+
AsyncDeleteMixin,
4+
AsyncService,
5+
CreateMixin,
6+
DeleteMixin,
7+
Service,
8+
)
29
from mpt_api_client.models import Model, ResourceData
310

411

@@ -14,8 +21,13 @@ class OrdersServiceConfig:
1421
_collection_key = "data"
1522

1623

17-
class OrdersService(Service[Order], OrdersServiceConfig):
18-
"""Orders client."""
24+
class OrdersService( # noqa: WPS215
25+
CreateMixin[Order],
26+
DeleteMixin,
27+
Service[Order],
28+
OrdersServiceConfig,
29+
):
30+
"""Orders service."""
1931

2032
def validate(self, resource_id: str, resource_data: ResourceData | None = None) -> Order:
2133
"""Switch order to validate state.
@@ -84,8 +96,13 @@ def template(self, resource_id: str) -> str:
8496
return response.text
8597

8698

87-
class AsyncOrdersService(AsyncService[Order], OrdersServiceConfig):
88-
"""Async Orders client."""
99+
class AsyncOrdersService( # noqa: WPS215
100+
AsyncCreateMixin[Order],
101+
AsyncDeleteMixin,
102+
AsyncService[Order],
103+
OrdersServiceConfig,
104+
):
105+
"""Async Orders service."""
89106

90107
async def validate(self, resource_id: str, resource_data: ResourceData | None = None) -> Order:
91108
"""Switch order to validate state.

tests/http/conftest.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,23 @@
22
import pytest
33

44
from mpt_api_client import RQLQuery
5-
from mpt_api_client.http.async_service import AsyncService
6-
from mpt_api_client.http.service import Service
5+
from mpt_api_client.http import (
6+
AsyncCreateMixin,
7+
AsyncDeleteMixin,
8+
AsyncService,
9+
CreateMixin,
10+
DeleteMixin,
11+
Service,
12+
)
713
from tests.conftest import DummyModel
814

915

10-
class DummyService(Service[DummyModel]):
16+
class DummyService(CreateMixin[DummyModel], DeleteMixin, Service[DummyModel]):
1117
_endpoint = "/api/v1/test"
1218
_model_class = DummyModel
1319

1420

15-
class AsyncDummyService(AsyncService[DummyModel]):
21+
class AsyncDummyService(AsyncCreateMixin[DummyModel], AsyncDeleteMixin, AsyncService[DummyModel]):
1622
_endpoint = "/api/v1/test"
1723
_model_class = DummyModel
1824

0 commit comments

Comments
 (0)