diff --git a/elytra_client/__init__.py b/elytra_client/__init__.py index aa852f2..6dbfcfa 100644 --- a/elytra_client/__init__.py +++ b/elytra_client/__init__.py @@ -1,6 +1,6 @@ """Elytra PIM Client - A Pythonic client for the Elytra PIM API""" -__version__ = "0.6.0" +__version__ = "0.7.0" __author__ = "Your Name" from . import rest_api @@ -8,27 +8,47 @@ from .client import ElytraClient from .exceptions import ElytraAPIError, ElytraAuthenticationError from .models import ( ProductAttributeResponse, + SingleMediaResponse, + SingleNewMediaRequestBody, SingleNewProductGroupRequestBody, SingleNewProductRequestBody, + SingleNewTextRequestBody, + SingleNewTreeGroupRequestBody, SingleProductGroupResponse, SingleProductResponse, + SingleTextResponse, + SingleTreeGroupResponse, + SingleUpdateMediaRequestBody, SingleUpdateProductGroupRequestBody, SingleUpdateProductRequestBody, + SingleUpdateTextRequestBody, + SingleUpdateTreeGroupRequestBody, ) __all__ = [ "ElytraClient", "ElytraAPIError", "ElytraAuthenticationError", - # Response models + # Product models "SingleProductResponse", "SingleProductGroupResponse", "ProductAttributeResponse", - # Request models "SingleNewProductRequestBody", "SingleUpdateProductRequestBody", "SingleNewProductGroupRequestBody", "SingleUpdateProductGroupRequestBody", + # Media models + "SingleMediaResponse", + "SingleNewMediaRequestBody", + "SingleUpdateMediaRequestBody", + # Text models + "SingleTextResponse", + "SingleNewTextRequestBody", + "SingleUpdateTextRequestBody", + # Tree group models + "SingleTreeGroupResponse", + "SingleNewTreeGroupRequestBody", + "SingleUpdateTreeGroupRequestBody", # Legacy REST API subpackage "rest_api", ] diff --git a/elytra_client/client.py b/elytra_client/client.py index 6f2bd43..ba25ecc 100644 --- a/elytra_client/client.py +++ b/elytra_client/client.py @@ -646,51 +646,74 @@ class ElytraClient: def upload_media_file( self, + file_bytes: bytes, media_id: int, - file_path: str, - language_code: str = "en", + mam_system: str, + language_code: Optional[str] = None, + filename: str = "upload", ) -> Dict[str, Any]: """ - Upload a file for a media item. + Upload a file for a media item using multipart/form-data. Args: - media_id: The media ID - file_path: Path to the file to upload - language_code: Language code for the file + file_bytes: Raw file content as bytes + media_id: The Elytra media ID to attach the file to + mam_system: MAM system identifier (e.g. 'fs', 'sixomc', 'cumulus') + language_code: Optional language code for the file + filename: Filename hint sent with the upload Returns: - Upload response - - Note: - This method uses multipart/form-data encoding for file upload. - The actual file upload implementation may require custom handling - outside of the standard JSON request pattern. + Upload response dict """ - # Note: This is a placeholder for file upload functionality - # Actual implementation would need to handle multipart/form-data - raise NotImplementedError( - "File upload requires special multipart/form-data handling. " - "Please use direct requests session or a specialized file upload method." + url = urljoin(self.base_url, "/media/file") + + form_data: Dict[str, Any] = { + "mediaId": str(media_id), + "mamSystem": mam_system, + } + if language_code: + form_data["languageCode"] = language_code + + # Build request manually so requests can set the multipart Content-Type boundary + # without being overridden by the session-level Content-Type: application/json header. + req = requests.Request( + "POST", + url, + headers={"Authorization": f"Bearer {self.api_key}", "Accept": "application/json"}, + files={"file": (filename, file_bytes)}, + data=form_data, ) + prepared = req.prepare() + + try: + response = self.session.send(prepared, timeout=self.timeout) + response.raise_for_status() + except requests.exceptions.HTTPError as e: + self._handle_http_error(e) + raise + + return response.json() def download_media_file(self, media_file_id: int) -> bytes: """ - Download a media file. + Download a media file by its file ID. Args: media_file_id: The media file ID Returns: - File content as bytes - - Note: - This method returns raw binary data and should be handled differently - from typical JSON API responses. + File content as raw bytes """ - raise NotImplementedError( - "File download requires special binary data handling. " - "Please use direct requests session or a specialized file download method." - ) + url = urljoin(self.base_url, f"/media/file/{media_file_id}") + + try: + response = self.session.get(url, timeout=self.timeout) + response.raise_for_status() + except requests.exceptions.HTTPError as e: + self._handle_http_error(e) + raise + + return response.content def delete_media_file(self, media_file_id: int) -> Dict[str, Any]: """ diff --git a/pyproject.toml b/pyproject.toml index f7b9d60..7c3bc5c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "elytra-pim-client" -version = "0.6.0" +version = "0.7.0" description = "A Pythonic client for the Elytra PIM API" readme = "README.md" requires-python = ">=3.9"