Skip to content

Commit 1375b24

Browse files
committed
Refactor files handle
1 parent d1039a5 commit 1375b24

11 files changed

Lines changed: 182 additions & 101 deletions

File tree

mpt_api_client/http/async_client.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
HTTPStatusError,
99
)
1010

11+
from mpt_api_client.constants import APPLICATION_JSON
1112
from mpt_api_client.exceptions import MPTError, transform_http_status_exception
13+
from mpt_api_client.http.mixins import _json_to_file_payload
1214
from mpt_api_client.http.types import (
1315
HeaderTypes,
1416
QueryParam,
@@ -65,6 +67,8 @@ async def request( # noqa: WPS211
6567
json: Any | None = None,
6668
query_params: QueryParam | None = None,
6769
headers: HeaderTypes | None = None,
70+
json_file_key: str = "_attachment_data",
71+
force_multipart: bool = False,
6872
) -> Response:
6973
"""Perform an HTTP request.
7074
@@ -75,6 +79,8 @@ async def request( # noqa: WPS211
7579
json: Request JSON data.
7680
query_params: Query parameters.
7781
headers: Request headers.
82+
json_file_key: json file name for data when sending a multipart request.
83+
force_multipart: force multipart request even if file is not provided.
7884
7985
Returns:
8086
Response object.
@@ -84,6 +90,11 @@ async def request( # noqa: WPS211
8490
MPTApiError: If the response contains an error.
8591
MPTHttpError: If the response contains an HTTP error.
8692
"""
93+
if force_multipart or (files and json):
94+
if files is None:
95+
files = {}
96+
files[json_file_key] = (None, _json_to_file_payload(json), APPLICATION_JSON)
97+
json = None
8798
try:
8899
response = await self.httpx_client.request(
89100
method,

mpt_api_client/http/client.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88
HTTPTransport,
99
)
1010

11+
from mpt_api_client.constants import APPLICATION_JSON
1112
from mpt_api_client.exceptions import (
1213
MPTError,
1314
transform_http_status_exception,
1415
)
16+
from mpt_api_client.http.mixins import _json_to_file_payload
1517
from mpt_api_client.http.types import (
1618
HeaderTypes,
1719
QueryParam,
@@ -67,6 +69,8 @@ def request( # noqa: WPS211
6769
json: Any | None = None,
6870
query_params: QueryParam | None = None,
6971
headers: HeaderTypes | None = None,
72+
json_file_key: str = "_attachment_data",
73+
force_multipart: bool = False,
7074
) -> Response:
7175
"""Perform an HTTP request.
7276
@@ -77,6 +81,8 @@ def request( # noqa: WPS211
7781
json: Request JSON data.
7882
query_params: Query parameters.
7983
headers: Request headers.
84+
json_file_key: json file name for data when sending a multipart request.
85+
force_multipart: force multipart request even if file is not provided.
8086
8187
Returns:
8288
Response object.
@@ -86,6 +92,11 @@ def request( # noqa: WPS211
8692
MPTApiError: If the response contains an error.
8793
MPTHttpError: If the response contains an HTTP error.
8894
"""
95+
if force_multipart or (files and json):
96+
if files is None:
97+
files = {}
98+
files[json_file_key] = (None, _json_to_file_payload(json), APPLICATION_JSON)
99+
json = None
89100
try:
90101
response = self.httpx_client.request(
91102
method,

mpt_api_client/resources/catalog/mixins.py

Lines changed: 58 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
from mpt_api_client.constants import APPLICATION_JSON
21
from mpt_api_client.http.mixins import (
32
AsyncDownloadFileMixin,
43
DownloadFileMixin,
5-
_json_to_file_payload,
64
)
75
from mpt_api_client.http.types import FileTypes
86
from mpt_api_client.models import ResourceData
@@ -83,14 +81,14 @@ async def unpublish(self, resource_id: str, resource_data: ResourceData | None =
8381
)
8482

8583

86-
class AsyncDocumentMixin[Model](
87-
AsyncDownloadFileMixin[Model],
88-
AsyncPublishableMixin[Model],
89-
):
90-
"""Async document mixin."""
84+
class AsyncCreateFileMixin[Model]:
85+
"""Create file mixin."""
86+
87+
_upload_file_key = "file"
88+
_upload_data_key = "document"
9189

9290
async def create(self, resource_data: ResourceData, file: FileTypes | None = None) -> Model:
93-
"""Creates document resource.
91+
"""Create document.
9492
9593
Creates a document resource by specifying a `file` or an `url`.
9694
@@ -100,27 +98,34 @@ async def create(self, resource_data: ResourceData, file: FileTypes | None = Non
10098
10199
Returns:
102100
Created resource.
103-
104101
"""
105102
files = {}
106-
107-
if resource_data:
108-
files["document"] = (
109-
None,
110-
_json_to_file_payload(resource_data),
111-
APPLICATION_JSON,
112-
)
113103
if file:
114-
files["file"] = file # type: ignore[assignment]
115-
response = await self.http_client.request("post", self.path, files=files) # type: ignore[attr-defined]
104+
files[self._upload_file_key] = file # type: ignore[assignment]
105+
response = await self.http_client.request(
106+
"post",
107+
self.path,
108+
json=resource_data,
109+
files=files,
110+
json_file_key=self._upload_data_key,
111+
force_multipart=True,
112+
) # type: ignore[attr-defined]
116113
return self._model_class.from_response(response) # type: ignore[attr-defined, no-any-return]
117114

118115

119-
class DocumentMixin[Model](
120-
DownloadFileMixin[Model],
121-
PublishableMixin[Model],
116+
class AsyncDocumentMixin[Model](
117+
AsyncCreateFileMixin[Model],
118+
AsyncDownloadFileMixin[Model],
119+
AsyncPublishableMixin[Model],
122120
):
123-
"""Document mixin."""
121+
"""Async document mixin."""
122+
123+
124+
class CreateFileMixin[Model]:
125+
"""Create file mixin."""
126+
127+
_upload_file_key = "file"
128+
_upload_data_key = "document"
124129

125130
def create(self, resource_data: ResourceData, file: FileTypes | None = None) -> Model:
126131
"""Create document.
@@ -135,19 +140,41 @@ def create(self, resource_data: ResourceData, file: FileTypes | None = None) ->
135140
Created resource.
136141
"""
137142
files = {}
138-
139-
if resource_data:
140-
files["document"] = (
141-
None,
142-
_json_to_file_payload(resource_data),
143-
APPLICATION_JSON,
144-
)
145143
if file:
146-
files["file"] = file # type: ignore[assignment]
147-
response = self.http_client.request("post", self.path, files=files) # type: ignore[attr-defined]
144+
files[self._upload_file_key] = file # type: ignore[assignment]
145+
response = self.http_client.request(
146+
"post",
147+
self.path,
148+
json=resource_data,
149+
files=files,
150+
json_file_key=self._upload_data_key,
151+
force_multipart=True,
152+
) # type: ignore[attr-defined]
148153
return self._model_class.from_response(response) # type: ignore[attr-defined, no-any-return]
149154

150155

156+
class DocumentMixin[Model](
157+
CreateFileMixin[Model],
158+
DownloadFileMixin[Model],
159+
PublishableMixin[Model],
160+
):
161+
"""Document mixin."""
162+
163+
_upload_file_key = "file"
164+
_upload_data_key = "document"
165+
166+
167+
class MediaMixin[Model](
168+
CreateFileMixin[Model],
169+
DownloadFileMixin[Model],
170+
PublishableMixin[Model],
171+
):
172+
"""Document mixin."""
173+
174+
_upload_file_key = "file"
175+
_upload_data_key = "media"
176+
177+
151178
class ActivatableMixin[Model]:
152179
"""Activatable mixin adds the ability to activate and deactivate."""
153180

mpt_api_client/resources/catalog/products_media.py

Lines changed: 2 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,12 @@
88
AsyncFilesOperationsMixin,
99
AsyncModifiableResourceMixin,
1010
CollectionMixin,
11-
FilesOperationsMixin,
1211
ModifiableResourceMixin,
1312
)
1413
from mpt_api_client.models import Model, ResourceData
1514
from mpt_api_client.resources.catalog.mixins import (
1615
AsyncPublishableMixin,
17-
PublishableMixin,
16+
MediaMixin,
1817
)
1918

2019

@@ -31,57 +30,14 @@ class MediaServiceConfig:
3130

3231

3332
class MediaService(
34-
FilesOperationsMixin[Media],
35-
PublishableMixin[Media],
33+
MediaMixin[Media],
3634
ModifiableResourceMixin[Media],
3735
CollectionMixin[Media],
3836
Service[Media],
3937
MediaServiceConfig,
4038
):
4139
"""Media service."""
4240

43-
@override
44-
def create(
45-
self,
46-
resource_data: ResourceData | None = None,
47-
files: dict[str, FileTypes] | None = None,
48-
data_key: str = "_media_data",
49-
) -> Media:
50-
"""Create Media resource.
51-
52-
Currently are two types of media resources available image and video.
53-
54-
Video:
55-
resource_data:
56-
{
57-
"name": "SomeMediaFile",
58-
"description":"Some media description",
59-
"mediaType": "Video",
60-
"url": http://www.somemedia.com/somevideo.avi,
61-
"displayOrder": 1
62-
}
63-
files: Add an image with the video thumbnail
64-
65-
Image:
66-
resource_data:
67-
{
68-
"name": "SomeMediaFile",
69-
"description":"Some media description",
70-
"mediaType": "Video",
71-
"displayOrder": 1
72-
}
73-
files: The image itself
74-
75-
Args:
76-
resource_data: Resource data.
77-
files: Files data.
78-
data_key: Key to use for the JSON data in the multipart form.
79-
80-
Returns:
81-
Media resource.
82-
"""
83-
return super().create(resource_data=resource_data, files=files, data_key=data_key)
84-
8541

8642
class AsyncMediaService(
8743
AsyncFilesOperationsMixin[Media],

tests/e2e/accounts/conftest.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import datetime as dt
2-
import pathlib
32

43
import pytest
54

@@ -10,9 +9,8 @@ def timestamp():
109

1110

1211
@pytest.fixture
13-
def account_icon():
14-
icon_path = pathlib.Path(__file__).parents[1] / "logo.png"
15-
return pathlib.Path.open(icon_path, "rb")
12+
def account_icon(logo_fd):
13+
return logo_fd
1614

1715

1816
@pytest.fixture

tests/e2e/catalog/product/conftest.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
1-
import pathlib
2-
31
import pytest
42

53

64
@pytest.fixture
7-
def product_icon():
8-
icon_path = pathlib.Path(__file__).parents[2] / "logo.png"
9-
return pathlib.Path.open(icon_path, "rb")
5+
def product_icon(logo_fd):
6+
return logo_fd
107

118

129
@pytest.fixture

tests/e2e/catalog/product/documents/conftest.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import pathlib
2-
31
import pytest
42

53

@@ -17,12 +15,6 @@ def document_data():
1715
}
1816

1917

20-
@pytest.fixture
21-
def test_file():
22-
file_path = pathlib.Path(__file__).parents[3] / "logo.png"
23-
return pathlib.Path.open(file_path, "rb")
24-
25-
2618
@pytest.fixture
2719
def vendor_document_service(mpt_vendor, product_id):
2820
return mpt_vendor.catalog.products.documents(product_id)

tests/e2e/conftest.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,9 @@ def api_token_id(e2e_config):
165165
@pytest.fixture
166166
def invalid_api_token_id():
167167
return "TKN-0000-0000"
168+
169+
170+
@pytest.fixture
171+
def logo_fd():
172+
file_path = pathlib.Path(__file__).parent / "logo.png"
173+
return pathlib.Path.open(file_path, "rb")

0 commit comments

Comments
 (0)