Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 105 additions & 1 deletion mpt_api_client/http/mixins.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import json
from urllib.parse import urljoin

from mpt_api_client.models import ResourceData
from httpx import Response
from httpx._types import FileTypes

from mpt_api_client.models import FileModel, ResourceData


def _json_to_file_payload(resource_data: ResourceData) -> bytes:
return json.dumps(
resource_data, ensure_ascii=False, separators=(",", ":"), allow_nan=False
).encode("utf-8")


class CreateMixin[Model]:
Expand Down Expand Up @@ -48,6 +58,53 @@ def update(self, resource_id: str, resource_data: ResourceData) -> Model:
return self._resource_action(resource_id, "PUT", json=resource_data) # type: ignore[attr-defined, no-any-return]


class FileOperationsMixin[Model]:
"""Mixin that provides create and download methods for file-based resources."""

def create(
self,
resource_data: ResourceData | None = None,
files: dict[str, FileTypes] | None = None, # noqa: WPS221
data_key: str = "_attachment_data",
) -> Model:
"""Create resource with file support.

Args:
resource_data: Resource data.
files: Files data.
data_key: Key to use for the JSON data in the multipart form.

Returns:
Created resource.
"""
files = files or {}

if resource_data:
files[data_key] = (
None,
_json_to_file_payload(resource_data),
"application/json",
)

response = self.http_client.post(self.endpoint, files=files) # type: ignore[attr-defined]
response.raise_for_status()
return self._model_class.from_response(response) # type: ignore[attr-defined, no-any-return]

def download(self, resource_id: str) -> FileModel:
"""Download the file for the given resource ID.

Args:
resource_id: Resource ID.

Returns:
File model containing the downloaded file.
"""
response: Response = self._resource_do_request( # type: ignore[attr-defined]
resource_id, method="GET", headers={"Accept": "*"}
)
return FileModel(response)


class AsyncCreateMixin[Model]:
"""Create resource mixin."""

Expand Down Expand Up @@ -92,3 +149,50 @@ async def update(self, resource_id: str, resource_data: ResourceData) -> Model:

"""
return await self._resource_action(resource_id, "PUT", json=resource_data) # type: ignore[attr-defined, no-any-return]


class AsyncFileOperationsMixin[Model]:
"""Async mixin that provides create and download methods for file-based resources."""

async def create(
self,
resource_data: ResourceData | None = None,
files: dict[str, FileTypes] | None = None, # noqa: WPS221
data_key: str = "_attachment_data",
) -> Model:
"""Create resource with file support.

Args:
resource_data: Resource data.
files: Files data.
data_key: Key to use for the JSON data in the multipart form.

Returns:
Created resource.
"""
files = files or {}

if resource_data:
files[data_key] = (
None,
_json_to_file_payload(resource_data),
"application/json",
)

response = await self.http_client.post(self.endpoint, files=files) # type: ignore[attr-defined]
response.raise_for_status()
return self._model_class.from_response(response) # type: ignore[attr-defined, no-any-return]

async def download(self, resource_id: str) -> FileModel:
"""Download the file for the given resource ID.

Args:
resource_id: Resource ID.

Returns:
File model containing the downloaded file.
"""
response = await self._resource_do_request( # type: ignore[attr-defined]
resource_id, method="GET", headers={"Accept": "*"}
)
return FileModel(response)
47 changes: 47 additions & 0 deletions mpt_api_client/resources/catalog/product_term_variants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from mpt_api_client.http import AsyncService, DeleteMixin, Service
from mpt_api_client.http.mixins import (
AsyncDeleteMixin,
AsyncFileOperationsMixin,
AsyncUpdateMixin,
FileOperationsMixin,
UpdateMixin,
)
from mpt_api_client.models import Model
from mpt_api_client.resources.catalog.mixins import (
AsyncPublishableMixin,
PublishableMixin,
)


class TermVariant(Model):
"""Term variant resource."""


class TermVariantServiceConfig:
"""Term variant service configuration."""

_endpoint = "/public/v1/catalog/products/terms/{term_id}/variants"
_model_class = TermVariant
_collection_key = "data"


class TermVariantService(
FileOperationsMixin[TermVariant],
DeleteMixin,
UpdateMixin[TermVariant],
PublishableMixin[TermVariant],
Service[TermVariant],
TermVariantServiceConfig,
):
"""Term variant service."""


class AsyncTermVariantService(
AsyncFileOperationsMixin[TermVariant],
AsyncDeleteMixin,
AsyncUpdateMixin[TermVariant],
AsyncPublishableMixin[TermVariant],
AsyncService[TermVariant],
TermVariantServiceConfig,
):
"""Async Term variant service."""
61 changes: 61 additions & 0 deletions mpt_api_client/resources/catalog/product_terms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from mpt_api_client.http import AsyncService, CreateMixin, DeleteMixin, Service
from mpt_api_client.http.mixins import (
AsyncCreateMixin,
AsyncDeleteMixin,
AsyncUpdateMixin,
UpdateMixin,
)
from mpt_api_client.models import Model
from mpt_api_client.resources.catalog.mixins import AsyncPublishableMixin, PublishableMixin
from mpt_api_client.resources.catalog.product_term_variants import (
AsyncTermVariantService,
TermVariantService,
)


class Term(Model):
"""Term resource."""


class TermServiceConfig:
"""Term service configuration."""

_endpoint = "/public/v1/catalog/products/{product_id}/terms"
_model_class = Term
_collection_key = "data"


class TermService(
CreateMixin[Term],
DeleteMixin,
UpdateMixin[Term],
PublishableMixin[Term],
Service[Term],
TermServiceConfig,
):
"""Term service."""

def variants(self, term_id: str) -> TermVariantService:
"""Access term variants service."""
return TermVariantService(
http_client=self.http_client,
endpoint_params={"term_id": term_id},
)


class AsyncTermService(
AsyncCreateMixin[Term],
AsyncDeleteMixin,
AsyncUpdateMixin[Term],
AsyncPublishableMixin[Term],
AsyncService[Term],
TermServiceConfig,
):
"""Async Term service."""

def variants(self, term_id: str) -> AsyncTermVariantService:
"""Access async term variants service."""
return AsyncTermVariantService(
http_client=self.http_client,
endpoint_params={"term_id": term_id},
)
46 changes: 46 additions & 0 deletions mpt_api_client/resources/catalog/products.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@
AsyncPublishableMixin,
PublishableMixin,
)
from mpt_api_client.resources.catalog.product_terms import (
AsyncTermService,
TermService,
)
from mpt_api_client.resources.catalog.products_documents import (
AsyncDocumentService,
DocumentService,
)
from mpt_api_client.resources.catalog.products_item_groups import (
AsyncItemGroupsService,
ItemGroupsService,
Expand All @@ -27,6 +35,10 @@
AsyncParametersService,
ParametersService,
)
from mpt_api_client.resources.catalog.products_templates import (
AsyncTemplatesService,
TemplatesService,
)


class Product(Model):
Expand Down Expand Up @@ -69,12 +81,28 @@ def media(self, product_id: str) -> MediaService:
http_client=self.http_client, endpoint_params={"product_id": product_id}
)

def documents(self, product_id: str) -> DocumentService:
"""Return documents service."""
return DocumentService(
http_client=self.http_client, endpoint_params={"product_id": product_id}
)

def product_parameters(self, product_id: str) -> ParametersService:
"""Return product_parameters service."""
return ParametersService(
http_client=self.http_client, endpoint_params={"product_id": product_id}
)

def templates(self, product_id: str) -> TemplatesService:
"""Return templates service."""
return TemplatesService(
http_client=self.http_client, endpoint_params={"product_id": product_id}
)

def terms(self, product_id: str) -> TermService:
"""Return terms service."""
return TermService(http_client=self.http_client, endpoint_params={"product_id": product_id})


class AsyncProductsService(
AsyncCreateMixin[Product],
Expand Down Expand Up @@ -104,8 +132,26 @@ def media(self, product_id: str) -> AsyncMediaService:
http_client=self.http_client, endpoint_params={"product_id": product_id}
)

def documents(self, product_id: str) -> AsyncDocumentService:
"""Return documents service."""
return AsyncDocumentService(
http_client=self.http_client, endpoint_params={"product_id": product_id}
)

def product_parameters(self, product_id: str) -> AsyncParametersService:
"""Return product_parameters service."""
return AsyncParametersService(
http_client=self.http_client, endpoint_params={"product_id": product_id}
)

def templates(self, product_id: str) -> AsyncTemplatesService:
"""Return templates service."""
return AsyncTemplatesService(
http_client=self.http_client, endpoint_params={"product_id": product_id}
)

def terms(self, product_id: str) -> AsyncTermService:
"""Return terms service."""
return AsyncTermService(
http_client=self.http_client, endpoint_params={"product_id": product_id}
)
44 changes: 44 additions & 0 deletions mpt_api_client/resources/catalog/products_documents.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from mpt_api_client.http import AsyncService, DeleteMixin, Service
from mpt_api_client.http.mixins import (
AsyncDeleteMixin,
AsyncFileOperationsMixin,
AsyncUpdateMixin,
FileOperationsMixin,
UpdateMixin,
)
from mpt_api_client.models import Model
from mpt_api_client.resources.catalog.mixins import AsyncPublishableMixin, PublishableMixin


class Document(Model):
"""Document resource."""


class DocumentServiceConfig:
"""Document service configuration."""

_endpoint = "/public/v1/catalog/products/{product_id}/documents"
_model_class = Document
_collection_key = "data"


class DocumentService(
FileOperationsMixin[Document],
DeleteMixin,
UpdateMixin[Document],
PublishableMixin[Document],
Service[Document],
DocumentServiceConfig,
):
"""Document service."""


class AsyncDocumentService(
AsyncFileOperationsMixin[Document],
AsyncDeleteMixin,
AsyncUpdateMixin[Document],
AsyncPublishableMixin[Document],
AsyncService[Document],
DocumentServiceConfig,
):
"""Document service."""
Loading