|
| 1 | +import json |
1 | 2 | from urllib.parse import urljoin |
2 | 3 |
|
3 | | -from mpt_api_client.models import ResourceData |
| 4 | +from httpx import Response |
| 5 | +from httpx._types import FileTypes |
| 6 | + |
| 7 | +from mpt_api_client.models import FileModel, ResourceData |
| 8 | + |
| 9 | + |
| 10 | +def _json_to_file_payload(resource_data: ResourceData) -> bytes: |
| 11 | + return json.dumps( |
| 12 | + resource_data, ensure_ascii=False, separators=(",", ":"), allow_nan=False |
| 13 | + ).encode("utf-8") |
4 | 14 |
|
5 | 15 |
|
6 | 16 | class CreateMixin[Model]: |
@@ -48,6 +58,53 @@ def update(self, resource_id: str, resource_data: ResourceData) -> Model: |
48 | 58 | return self._resource_action(resource_id, "PUT", json=resource_data) # type: ignore[attr-defined, no-any-return] |
49 | 59 |
|
50 | 60 |
|
| 61 | +class FileOperationsMixin[Model]: |
| 62 | + """Mixin that provides create and download methods for file-based resources.""" |
| 63 | + |
| 64 | + def create( |
| 65 | + self, |
| 66 | + resource_data: ResourceData | None = None, |
| 67 | + files: dict[str, FileTypes] | None = None, # noqa: WPS221 |
| 68 | + data_key: str = "_attachment_data", |
| 69 | + ) -> Model: |
| 70 | + """Create resource with file support. |
| 71 | +
|
| 72 | + Args: |
| 73 | + resource_data: Resource data. |
| 74 | + files: Files data. |
| 75 | + data_key: Key to use for the JSON data in the multipart form. |
| 76 | +
|
| 77 | + Returns: |
| 78 | + Created resource. |
| 79 | + """ |
| 80 | + files = files or {} |
| 81 | + |
| 82 | + if resource_data: |
| 83 | + files[data_key] = ( |
| 84 | + None, |
| 85 | + _json_to_file_payload(resource_data), |
| 86 | + "application/json", |
| 87 | + ) |
| 88 | + |
| 89 | + response = self.http_client.post(self.endpoint, files=files) # type: ignore[attr-defined] |
| 90 | + response.raise_for_status() |
| 91 | + return self._model_class.from_response(response) # type: ignore[attr-defined, no-any-return] |
| 92 | + |
| 93 | + def download(self, resource_id: str) -> FileModel: |
| 94 | + """Download the file for the given resource ID. |
| 95 | +
|
| 96 | + Args: |
| 97 | + resource_id: Resource ID. |
| 98 | +
|
| 99 | + Returns: |
| 100 | + File model containing the downloaded file. |
| 101 | + """ |
| 102 | + response: Response = self._resource_do_request( # type: ignore[attr-defined] |
| 103 | + resource_id, method="GET", headers={"Accept": "*"} |
| 104 | + ) |
| 105 | + return FileModel(response) |
| 106 | + |
| 107 | + |
51 | 108 | class AsyncCreateMixin[Model]: |
52 | 109 | """Create resource mixin.""" |
53 | 110 |
|
@@ -92,3 +149,50 @@ async def update(self, resource_id: str, resource_data: ResourceData) -> Model: |
92 | 149 |
|
93 | 150 | """ |
94 | 151 | return await self._resource_action(resource_id, "PUT", json=resource_data) # type: ignore[attr-defined, no-any-return] |
| 152 | + |
| 153 | + |
| 154 | +class AsyncFileOperationsMixin[Model]: |
| 155 | + """Async mixin that provides create and download methods for file-based resources.""" |
| 156 | + |
| 157 | + async def create( |
| 158 | + self, |
| 159 | + resource_data: ResourceData | None = None, |
| 160 | + files: dict[str, FileTypes] | None = None, # noqa: WPS221 |
| 161 | + data_key: str = "_attachment_data", |
| 162 | + ) -> Model: |
| 163 | + """Create resource with file support. |
| 164 | +
|
| 165 | + Args: |
| 166 | + resource_data: Resource data. |
| 167 | + files: Files data. |
| 168 | + data_key: Key to use for the JSON data in the multipart form. |
| 169 | +
|
| 170 | + Returns: |
| 171 | + Created resource. |
| 172 | + """ |
| 173 | + files = files or {} |
| 174 | + |
| 175 | + if resource_data: |
| 176 | + files[data_key] = ( |
| 177 | + None, |
| 178 | + _json_to_file_payload(resource_data), |
| 179 | + "application/json", |
| 180 | + ) |
| 181 | + |
| 182 | + response = await self.http_client.post(self.endpoint, files=files) # type: ignore[attr-defined] |
| 183 | + response.raise_for_status() |
| 184 | + return self._model_class.from_response(response) # type: ignore[attr-defined, no-any-return] |
| 185 | + |
| 186 | + async def download(self, resource_id: str) -> FileModel: |
| 187 | + """Download the file for the given resource ID. |
| 188 | +
|
| 189 | + Args: |
| 190 | + resource_id: Resource ID. |
| 191 | +
|
| 192 | + Returns: |
| 193 | + File model containing the downloaded file. |
| 194 | + """ |
| 195 | + response = await self._resource_do_request( # type: ignore[attr-defined] |
| 196 | + resource_id, method="GET", headers={"Accept": "*"} |
| 197 | + ) |
| 198 | + return FileModel(response) |
0 commit comments