Skip to content

Commit ad83080

Browse files
committed
MPT-13324 Add catalog products terms variants
- Add catalog product terms variants - Refactored create and download into FileOperationsMixin
1 parent 8da8b0b commit ad83080

File tree

8 files changed

+274
-207
lines changed

8 files changed

+274
-207
lines changed

mpt_api_client/resources/catalog/mixins.py

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,109 @@
1-
from mpt_api_client.models import ResourceData
1+
import json
2+
3+
from httpx import Response
4+
from httpx._types import FileTypes
5+
6+
from mpt_api_client.models import FileModel, ResourceData
7+
8+
9+
def _json_to_file_payload(resource_data: ResourceData) -> bytes:
10+
return json.dumps(
11+
resource_data, ensure_ascii=False, separators=(",", ":"), allow_nan=False
12+
).encode("utf-8")
13+
14+
15+
class FileOperationsMixin[Model]:
16+
"""Mixin that provides create and download methods for file-based resources."""
17+
18+
def create(
19+
self,
20+
resource_data: ResourceData | None = None,
21+
files: dict[str, FileTypes] | None = None, # noqa: WPS221
22+
data_key: str = "_attachment_data",
23+
) -> Model:
24+
"""Create resource with file support.
25+
26+
Args:
27+
resource_data: Resource data.
28+
files: Files data.
29+
data_key: Key to use for the JSON data in the multipart form.
30+
31+
Returns:
32+
Created resource.
33+
"""
34+
files = files or {}
35+
36+
if resource_data:
37+
files[data_key] = (
38+
None,
39+
_json_to_file_payload(resource_data),
40+
"application/json",
41+
)
42+
43+
response = self.http_client.post(self.endpoint, files=files) # type: ignore[attr-defined]
44+
response.raise_for_status()
45+
return self._model_class.from_response(response) # type: ignore[attr-defined, no-any-return]
46+
47+
def download(self, resource_id: str) -> FileModel:
48+
"""Download the file for the given resource ID.
49+
50+
Args:
51+
resource_id: Resource ID.
52+
53+
Returns:
54+
File model containing the downloaded file.
55+
"""
56+
response: Response = self._resource_do_request( # type: ignore[attr-defined]
57+
resource_id, method="GET", headers={"Accept": "*"}
58+
)
59+
return FileModel(response)
60+
61+
62+
class AsyncFileOperationsMixin[Model]:
63+
"""Async mixin that provides create and download methods for file-based resources."""
64+
65+
async def create(
66+
self,
67+
resource_data: ResourceData | None = None,
68+
files: dict[str, FileTypes] | None = None, # noqa: WPS221
69+
data_key: str = "_attachment_data",
70+
) -> Model:
71+
"""Create resource with file support.
72+
73+
Args:
74+
resource_data: Resource data.
75+
files: Files data.
76+
data_key: Key to use for the JSON data in the multipart form.
77+
78+
Returns:
79+
Created resource.
80+
"""
81+
files = files or {}
82+
83+
if resource_data:
84+
files[data_key] = (
85+
None,
86+
_json_to_file_payload(resource_data),
87+
"application/json",
88+
)
89+
90+
response = await self.http_client.post(self.endpoint, files=files) # type: ignore[attr-defined]
91+
response.raise_for_status()
92+
return self._model_class.from_response(response) # type: ignore[attr-defined, no-any-return]
93+
94+
async def download(self, resource_id: str) -> FileModel:
95+
"""Download the file for the given resource ID.
96+
97+
Args:
98+
resource_id: Resource ID.
99+
100+
Returns:
101+
File model containing the downloaded file.
102+
"""
103+
response = await self._resource_do_request( # type: ignore[attr-defined]
104+
resource_id, method="GET", headers={"Accept": "*"}
105+
)
106+
return FileModel(response)
2107

3108

4109
class PublishableMixin[Model]:
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, DeleteMixin, Service
2+
from mpt_api_client.http.mixins import (
3+
AsyncDeleteMixin,
4+
AsyncUpdateMixin,
5+
UpdateMixin,
6+
)
7+
from mpt_api_client.models import Model
8+
from mpt_api_client.resources.catalog.mixins import (
9+
AsyncFileOperationsMixin,
10+
AsyncPublishableMixin,
11+
FileOperationsMixin,
12+
PublishableMixin,
13+
)
14+
15+
16+
class TermVariant(Model):
17+
"""Term variant resource."""
18+
19+
20+
class TermVariantServiceConfig:
21+
"""Term variant service configuration."""
22+
23+
_endpoint = "/public/v1/catalog/products/terms/{term_id}/variants"
24+
_model_class = TermVariant
25+
_collection_key = "data"
26+
27+
28+
class TermVariantService(
29+
FileOperationsMixin[TermVariant],
30+
DeleteMixin,
31+
UpdateMixin[TermVariant],
32+
PublishableMixin[TermVariant],
33+
Service[TermVariant],
34+
TermVariantServiceConfig,
35+
):
36+
"""Term variant service."""
37+
38+
39+
class AsyncTermVariantService(
40+
AsyncFileOperationsMixin[TermVariant],
41+
AsyncDeleteMixin,
42+
AsyncUpdateMixin[TermVariant],
43+
AsyncPublishableMixin[TermVariant],
44+
AsyncService[TermVariant],
45+
TermVariantServiceConfig,
46+
):
47+
"""Async Term variant service."""

mpt_api_client/resources/catalog/product_terms.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
)
88
from mpt_api_client.models import Model
99
from mpt_api_client.resources.catalog.mixins import AsyncPublishableMixin, PublishableMixin
10+
from mpt_api_client.resources.catalog.product_term_variants import (
11+
AsyncTermVariantService,
12+
TermVariantService,
13+
)
1014

1115

1216
class Term(Model):
@@ -31,6 +35,13 @@ class TermService(
3135
):
3236
"""Term service."""
3337

38+
def variants(self, term_id: str) -> TermVariantService:
39+
"""Access term variants service."""
40+
return TermVariantService(
41+
http_client=self.http_client,
42+
endpoint_params={"term_id": term_id},
43+
)
44+
3445

3546
class AsyncTermService(
3647
AsyncCreateMixin[Term],
@@ -41,3 +52,10 @@ class AsyncTermService(
4152
TermServiceConfig,
4253
):
4354
"""Async Term service."""
55+
56+
def variants(self, term_id: str) -> AsyncTermVariantService:
57+
"""Access async term variants service."""
58+
return AsyncTermVariantService(
59+
http_client=self.http_client,
60+
endpoint_params={"term_id": term_id},
61+
)
Lines changed: 10 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,16 @@
1-
import json
2-
from typing import override
3-
4-
from httpx import Response
5-
from httpx._types import FileTypes
6-
7-
from mpt_api_client.http import AsyncService, CreateMixin, DeleteMixin, Service
1+
from mpt_api_client.http import AsyncService, DeleteMixin, Service
82
from mpt_api_client.http.mixins import (
9-
AsyncCreateMixin,
103
AsyncDeleteMixin,
114
AsyncUpdateMixin,
125
UpdateMixin,
136
)
14-
from mpt_api_client.models import FileModel, Model, ResourceData
15-
from mpt_api_client.resources.catalog.mixins import AsyncPublishableMixin, PublishableMixin
16-
17-
18-
def _json_to_file_payload(resource_data: ResourceData) -> bytes:
19-
return json.dumps(
20-
resource_data, ensure_ascii=False, separators=(",", ":"), allow_nan=False
21-
).encode("utf-8")
7+
from mpt_api_client.models import Model
8+
from mpt_api_client.resources.catalog.mixins import (
9+
AsyncFileOperationsMixin,
10+
AsyncPublishableMixin,
11+
FileOperationsMixin,
12+
PublishableMixin,
13+
)
2214

2315

2416
class Document(Model):
@@ -34,7 +26,7 @@ class DocumentServiceConfig:
3426

3527

3628
class DocumentService(
37-
CreateMixin[Document],
29+
FileOperationsMixin[Document],
3830
DeleteMixin,
3931
UpdateMixin[Document],
4032
PublishableMixin[Document],
@@ -43,101 +35,13 @@ class DocumentService(
4335
):
4436
"""Document service."""
4537

46-
@override
47-
def create(
48-
self,
49-
resource_data: ResourceData | None = None,
50-
files: dict[str, FileTypes] | None = None, # noqa: WPS221
51-
) -> Document:
52-
"""Create Document resource.
53-
54-
Include the document as a file or add an url in resource_data to be uploaded.
55-
56-
Args:
57-
resource_data: Resource data.
58-
files: Files data.
59-
60-
Returns:
61-
Document resource.
62-
"""
63-
files = files or {}
64-
65-
if resource_data:
66-
files["_attachment_data"] = (
67-
None,
68-
_json_to_file_payload(resource_data),
69-
"application/json",
70-
)
71-
72-
response = self.http_client.post(self.endpoint, files=files)
73-
response.raise_for_status()
74-
return Document.from_response(response)
75-
76-
def download(self, document_id: str) -> FileModel:
77-
"""Download the document file for the given document ID.
78-
79-
Args:
80-
document_id: Document ID.
81-
82-
Returns:
83-
Document file.
84-
"""
85-
response: Response = self._resource_do_request(
86-
document_id, method="GET", headers={"Accept": "*"}
87-
)
88-
return FileModel(response)
89-
9038

9139
class AsyncDocumentService(
92-
AsyncCreateMixin[Document],
40+
AsyncFileOperationsMixin[Document],
9341
AsyncDeleteMixin,
9442
AsyncUpdateMixin[Document],
9543
AsyncPublishableMixin[Document],
9644
AsyncService[Document],
9745
DocumentServiceConfig,
9846
):
9947
"""Document service."""
100-
101-
@override
102-
async def create(
103-
self,
104-
resource_data: ResourceData | None = None,
105-
files: dict[str, FileTypes] | None = None, # noqa: WPS221
106-
) -> Document:
107-
"""Create Document resource.
108-
109-
Include the document as a file or add an url in resource_data to be uploaded.
110-
111-
Args:
112-
resource_data: Resource data.
113-
files: Files data.
114-
115-
Returns:
116-
Document resource.
117-
"""
118-
files = files or {}
119-
120-
if resource_data:
121-
files["_attachment_data"] = (
122-
None,
123-
_json_to_file_payload(resource_data),
124-
"application/json",
125-
)
126-
127-
response = await self.http_client.post(self.endpoint, files=files)
128-
response.raise_for_status()
129-
return Document.from_response(response)
130-
131-
async def download(self, document_id: str) -> FileModel:
132-
"""Download the document file for the given document ID.
133-
134-
Args:
135-
document_id: Document ID.
136-
137-
Returns:
138-
Document file.
139-
"""
140-
response = await self._resource_do_request(
141-
document_id, method="GET", headers={"Accept": "*"}
142-
)
143-
return FileModel(response)

0 commit comments

Comments
 (0)