Skip to content

Commit 6403e88

Browse files
committed
MPT-12840 Update service for agreements asset
1 parent 468c07b commit 6403e88

File tree

5 files changed

+128
-94
lines changed

5 files changed

+128
-94
lines changed

mpt_api_client/http/base_service.py

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from mpt_api_client.rql import RQLQuery
99

1010

11-
class ServiceBase[Client, Model: BaseModel]:
11+
class ServiceBase[Client, Model: BaseModel]: # noqa: WPS214
1212
"""Service base with agnostic HTTP client."""
1313

1414
_endpoint: str
@@ -22,11 +22,13 @@ def __init__(
2222
query_rql: RQLQuery | None = None,
2323
query_order_by: list[str] | None = None,
2424
query_select: list[str] | None = None,
25+
endpoint_params: dict[str, str] | None = None,
2526
) -> None:
2627
self.http_client = http_client
2728
self.query_rql: RQLQuery | None = query_rql
2829
self.query_order_by = query_order_by
2930
self.query_select = query_select
31+
self.endpoint_params = endpoint_params or {}
3032

3133
def clone(self) -> Self:
3234
"""Create a copy of collection client for immutable operations.
@@ -39,30 +41,40 @@ def clone(self) -> Self:
3941
query_rql=self.query_rql,
4042
query_order_by=copy.copy(self.query_order_by) if self.query_order_by else None,
4143
query_select=copy.copy(self.query_select) if self.query_select else None,
44+
endpoint_params=self.endpoint_params,
4245
)
4346

44-
def build_url(self, query_params: dict[str, Any] | None = None) -> str: # noqa: WPS210
47+
@property
48+
def endpoint(self) -> str:
49+
"""Service endpoint URL."""
50+
return self._endpoint.format(**self.endpoint_params)
51+
52+
def build_url(
53+
self,
54+
query_params: dict[str, Any] | None = None,
55+
) -> str: # noqa: WPS210
4556
"""Builds the endpoint URL with all the query parameters.
4657
4758
Returns:
4859
Partial URL with query parameters.
4960
"""
5061
query_params = query_params or {}
62+
if self.query_order_by:
63+
query_params.update({"order": ",".join(self.query_order_by)})
64+
if self.query_select:
65+
query_params.update({"select": ",".join(self.query_select)})
66+
5167
query_parts = [
5268
f"{param_key}={param_value}" for param_key, param_value in query_params.items()
5369
]
54-
if self.query_order_by:
55-
str_order_by = ",".join(self.query_order_by)
56-
query_parts.append(f"order={str_order_by}")
57-
if self.query_select:
58-
str_query_select = ",".join(self.query_select)
59-
query_parts.append(f"select={str_query_select}")
70+
6071
if self.query_rql:
6172
query_parts.append(str(self.query_rql))
73+
6274
if query_parts:
6375
query = "&".join(query_parts)
64-
return f"{self._endpoint}?{query}"
65-
return self._endpoint
76+
return f"{self.endpoint}?{query}"
77+
return self.endpoint
6678

6779
def order_by(self, *fields: str) -> Self:
6880
"""Returns new collection with ordering setup.
@@ -109,12 +121,13 @@ def select(self, *fields: str) -> Self:
109121
new_client.query_select = list(fields)
110122
return new_client
111123

112-
def _create_collection(self, response: httpx.Response) -> Collection[Model]:
124+
@classmethod
125+
def _create_collection(cls, response: httpx.Response) -> Collection[Model]:
113126
meta = Meta.from_response(response)
114127
return Collection(
115128
resources=[
116-
self._model_class.new(resource, meta)
117-
for resource in response.json().get(self._collection_key)
129+
cls._model_class.new(resource, meta)
130+
for resource in response.json().get(cls._collection_key)
118131
],
119132
meta=meta,
120133
)

mpt_api_client/http/service.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from mpt_api_client.models.collection import ResourceList
1212

1313

14-
class Service[Model: BaseModel](ServiceBase[HTTPClient, Model]):
14+
class Service[Model: BaseModel](ServiceBase[HTTPClient, Model]): # noqa: WPS214
1515
"""Immutable service for RESTful resource collections.
1616
1717
Examples:

setup.cfg

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ extend-ignore =
3333

3434
per-file-ignores =
3535
mpt_api_client/rql/query_builder.py: WPS110 WPS115 WPS210 WPS214
36-
mpt_api_client/http/service.py: WPS214
3736
tests/http/test_async_service.py: WPS204 WPS202
3837
tests/http/test_service.py: WPS204 WPS202
3938
tests/*:

tests/http/test_base_service.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import pytest
2+
3+
from mpt_api_client import RQLQuery
4+
from mpt_api_client.http import Service
5+
from tests.conftest import DummyModel
6+
7+
8+
class EndpointDummyService(Service[DummyModel]):
9+
_endpoint = "/api/{version}/test"
10+
_model_class = DummyModel
11+
12+
13+
@pytest.fixture
14+
def base_dummy_service(http_client):
15+
return EndpointDummyService(http_client=http_client, endpoint_params={"version": "v1"})
16+
17+
18+
def test_endpoint(http_client):
19+
service = EndpointDummyService(http_client=http_client, endpoint_params={"version": "vLatest"})
20+
21+
assert service.endpoint_params == {"version": "vLatest"}
22+
assert service.endpoint == "/api/vLatest/test"
23+
24+
25+
def test_filter(base_dummy_service, filter_status_active):
26+
new_collection = base_dummy_service.filter(filter_status_active)
27+
28+
assert base_dummy_service.query_rql is None
29+
assert new_collection != base_dummy_service
30+
assert new_collection.query_rql == filter_status_active
31+
32+
33+
def test_multiple_filters(base_dummy_service) -> None:
34+
filter_query = RQLQuery(status="active")
35+
filter_query2 = RQLQuery(name="test")
36+
37+
new_collection = base_dummy_service.filter(filter_query).filter(filter_query2)
38+
39+
assert base_dummy_service.query_rql is None
40+
assert new_collection.query_rql == filter_query & filter_query2
41+
42+
43+
def test_select(base_dummy_service) -> None:
44+
new_collection = base_dummy_service.select("agreement", "-product")
45+
46+
assert base_dummy_service.query_select is None
47+
assert new_collection != base_dummy_service
48+
assert new_collection.query_select == ["agreement", "-product"]
49+
50+
51+
def test_select_exception(base_dummy_service) -> None:
52+
with pytest.raises(ValueError):
53+
base_dummy_service.select("agreement").select("product")
54+
55+
56+
def test_order_by(base_dummy_service):
57+
new_collection = base_dummy_service.order_by("created", "-name")
58+
59+
assert base_dummy_service.query_order_by is None
60+
assert new_collection != base_dummy_service
61+
assert new_collection.query_order_by == ["created", "-name"]
62+
63+
64+
def test_order_by_exception(base_dummy_service):
65+
with pytest.raises(
66+
ValueError, match=r"Ordering is already set. Cannot set ordering multiple times."
67+
):
68+
base_dummy_service.order_by("created").order_by("name")
69+
70+
71+
def test_url(base_dummy_service, filter_status_active) -> None:
72+
custom_collection = (
73+
base_dummy_service.filter(filter_status_active)
74+
.select("-audit", "product.agreements", "-product.agreements.product")
75+
.order_by("-created", "name")
76+
)
77+
78+
url = custom_collection.build_url()
79+
80+
assert custom_collection != base_dummy_service
81+
assert url == (
82+
"/api/v1/test?order=-created,name"
83+
"&select=-audit,product.agreements,-product.agreements.product"
84+
"&eq(status,active)"
85+
)
86+
87+
88+
def test_clone(base_dummy_service, filter_status_active) -> None:
89+
configured = (
90+
base_dummy_service.filter(filter_status_active)
91+
.order_by("created", "-name")
92+
.select("agreement", "-product")
93+
)
94+
95+
cloned = configured.clone()
96+
97+
assert cloned is not configured
98+
assert isinstance(cloned, configured.__class__)
99+
assert cloned.http_client is configured.http_client
100+
assert str(cloned.query_rql) == str(configured.query_rql)
101+
assert cloned.endpoint_params == configured.endpoint_params

tests/http/test_service.py

Lines changed: 0 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import pytest
55
import respx
66

7-
from mpt_api_client.rql import RQLQuery
87
from tests.conftest import DummyModel
98
from tests.http.conftest import DummyService
109

@@ -279,81 +278,3 @@ def test_sync_update_resource(dummy_service):
279278
request = mock_route.calls[0].request
280279
assert mock_route.call_count == 1
281280
assert json.loads(request.content.decode()) == resource_data
282-
283-
284-
def test_sync_filter(dummy_service, filter_status_active):
285-
new_collection = dummy_service.filter(filter_status_active)
286-
287-
assert dummy_service.query_rql is None
288-
assert new_collection != dummy_service
289-
assert new_collection.query_rql == filter_status_active
290-
291-
292-
def test_sync_multiple_filters(dummy_service) -> None:
293-
filter_query = RQLQuery(status="active")
294-
filter_query2 = RQLQuery(name="test")
295-
296-
new_collection = dummy_service.filter(filter_query).filter(filter_query2)
297-
298-
assert dummy_service.query_rql is None
299-
assert new_collection.query_rql == filter_query & filter_query2
300-
301-
302-
def test_sync_select(dummy_service) -> None:
303-
new_collection = dummy_service.select("agreement", "-product")
304-
305-
assert dummy_service.query_select is None
306-
assert new_collection != dummy_service
307-
assert new_collection.query_select == ["agreement", "-product"]
308-
309-
310-
def test_sync_select_exception(dummy_service) -> None:
311-
with pytest.raises(ValueError):
312-
dummy_service.select("agreement").select("product")
313-
314-
315-
def test_sync_order_by(dummy_service):
316-
new_collection = dummy_service.order_by("created", "-name")
317-
318-
assert dummy_service.query_order_by is None
319-
assert new_collection != dummy_service
320-
assert new_collection.query_order_by == ["created", "-name"]
321-
322-
323-
def test_sync_order_by_exception(dummy_service):
324-
with pytest.raises(
325-
ValueError, match=r"Ordering is already set. Cannot set ordering multiple times."
326-
):
327-
dummy_service.order_by("created").order_by("name")
328-
329-
330-
def test_sync_url(dummy_service, filter_status_active) -> None:
331-
custom_collection = (
332-
dummy_service.filter(filter_status_active)
333-
.select("-audit", "product.agreements", "-product.agreements.product")
334-
.order_by("-created", "name")
335-
)
336-
337-
url = custom_collection.build_url()
338-
339-
assert custom_collection != dummy_service
340-
assert url == (
341-
"/api/v1/test?order=-created,name"
342-
"&select=-audit,product.agreements,-product.agreements.product"
343-
"&eq(status,active)"
344-
)
345-
346-
347-
def test_sync_clone(dummy_service, filter_status_active) -> None:
348-
configured = (
349-
dummy_service.filter(filter_status_active)
350-
.order_by("created", "-name")
351-
.select("agreement", "-product")
352-
)
353-
354-
cloned = configured.clone()
355-
356-
assert cloned is not configured
357-
assert isinstance(cloned, configured.__class__)
358-
assert cloned.http_client is configured.http_client
359-
assert str(cloned.query_rql) == str(configured.query_rql)

0 commit comments

Comments
 (0)