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
8 changes: 4 additions & 4 deletions mpt_api_client/http/base_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from mpt_api_client.http.query_state import QueryState
from mpt_api_client.http.types import Response
from mpt_api_client.models import Collection, Meta
from mpt_api_client.models import Meta, ModelCollection
from mpt_api_client.models import Model as BaseModel


Expand Down Expand Up @@ -42,16 +42,16 @@ def build_path(
return f"{self.path}?{query}" if query else self.path

@classmethod
def make_collection(cls, response: Response) -> Collection[Model]:
def make_collection(cls, response: Response) -> ModelCollection[Model]:
"""Builds a collection from a response.

Args:
response: The response object.
"""
meta = Meta.from_response(response)
return Collection(
return ModelCollection(
resources=[
cls._model_class.new(resource, meta)
cls._model_class(resource, meta)
for resource in response.json().get(cls._collection_key)
],
meta=meta,
Expand Down
6 changes: 3 additions & 3 deletions mpt_api_client/http/mixins/collection_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

from mpt_api_client.http.mixins.queryable_mixin import QueryableMixin
from mpt_api_client.http.types import Response
from mpt_api_client.models import Collection
from mpt_api_client.models import Model as BaseModel
from mpt_api_client.models import ModelCollection


class CollectionMixin[Model: BaseModel](QueryableMixin):
"""Mixin providing collection functionality."""

def fetch_page(self, limit: int = 100, offset: int = 0) -> Collection[Model]:
def fetch_page(self, limit: int = 100, offset: int = 0) -> ModelCollection[Model]:
"""Fetch one page of resources.

Returns:
Expand Down Expand Up @@ -78,7 +78,7 @@ def _fetch_page_as_response(self, limit: int = 100, offset: int = 0) -> Response
class AsyncCollectionMixin[Model: BaseModel](QueryableMixin):
"""Async mixin providing collection functionality."""

async def fetch_page(self, limit: int = 100, offset: int = 0) -> Collection[Model]:
async def fetch_page(self, limit: int = 100, offset: int = 0) -> ModelCollection[Model]:
"""Fetch one page of resources.

Returns:
Expand Down
18 changes: 9 additions & 9 deletions mpt_api_client/http/resource_accessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
from mpt_api_client.http.query_options import QueryOptions
from mpt_api_client.http.types import QueryParam, Response
from mpt_api_client.http.url_utils import join_url_path
from mpt_api_client.models.collection import ResourceList
from mpt_api_client.models.model import Model, ResourceData # NOSONAR
from mpt_api_client.models.model_collection import ModelCollection, ResourceList

_JsonPayload = ResourceData | ResourceList | None

Expand Down Expand Up @@ -65,7 +65,7 @@ def get(
options: QueryOptions | None = None,
) -> ResourceModel:
"""``GET`` the resource (optionally with a sub-action)."""
return self._action("GET", action, query_params=query_params, options=options)
return self._action("GET", action, query_params=query_params, options=options) # type: ignore[return-value]

def post(
self,
Expand All @@ -75,7 +75,7 @@ def post(
query_params: QueryParam | None = None,
) -> ResourceModel:
"""``POST`` to the resource (optionally with a sub-action)."""
return self._action("POST", action, json=json, query_params=query_params)
return self._action("POST", action, json=json, query_params=query_params) # type: ignore[return-value]

def put(
self,
Expand All @@ -85,7 +85,7 @@ def put(
query_params: QueryParam | None = None,
) -> ResourceModel:
"""``PUT`` to the resource (optionally with a sub-action)."""
return self._action("PUT", action, json=json, query_params=query_params)
return self._action("PUT", action, json=json, query_params=query_params) # type: ignore[return-value]

def delete(self) -> None:
"""``DELETE`` the resource."""
Expand All @@ -99,7 +99,7 @@ def _action(
json: _JsonPayload = None,
query_params: QueryParam | None = None,
options: QueryOptions | None = None,
) -> ResourceModel:
) -> ResourceModel | ModelCollection[ResourceModel]:
response = self.do_request(
method,
action,
Expand Down Expand Up @@ -164,7 +164,7 @@ async def get(
options: QueryOptions | None = None,
) -> ResourceModel:
"""``GET`` the resource (optionally with a sub-action)."""
return await self._action("GET", action, query_params=query_params, options=options)
return await self._action("GET", action, query_params=query_params, options=options) # type: ignore[return-value]

async def post(
self,
Expand All @@ -174,7 +174,7 @@ async def post(
query_params: QueryParam | None = None,
) -> ResourceModel:
"""``POST`` to the resource (optionally with a sub-action)."""
return await self._action("POST", action, json=json, query_params=query_params)
return await self._action("POST", action, json=json, query_params=query_params) # type: ignore[return-value]

async def put(
self,
Expand All @@ -184,7 +184,7 @@ async def put(
query_params: QueryParam | None = None,
) -> ResourceModel:
"""``PUT`` to the resource (optionally with a sub-action)."""
return await self._action("PUT", action, json=json, query_params=query_params)
return await self._action("PUT", action, json=json, query_params=query_params) # type: ignore[return-value]

async def delete(self) -> None:
"""``DELETE`` the resource."""
Expand All @@ -198,7 +198,7 @@ async def _action(
json: _JsonPayload = None,
query_params: QueryParam | None = None,
options: QueryOptions | None = None,
) -> ResourceModel:
) -> ResourceModel | ModelCollection[ResourceModel]:
response = await self.do_request(
method,
action,
Expand Down
4 changes: 2 additions & 2 deletions mpt_api_client/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from mpt_api_client.models.collection import Collection
from mpt_api_client.models.file_model import FileModel
from mpt_api_client.models.meta import Meta, Pagination
from mpt_api_client.models.model import Model, ResourceData
from mpt_api_client.models.model_collection import ModelCollection

__all__ = ["Collection", "FileModel", "Meta", "Model", "Pagination", "ResourceData"] # noqa: WPS410
__all__ = ["FileModel", "Meta", "Model", "ModelCollection", "Pagination", "ResourceData"] # noqa: WPS410
Comment thread
jentyk marked this conversation as resolved.
19 changes: 9 additions & 10 deletions mpt_api_client/models/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from mpt_api_client.http.types import Response
from mpt_api_client.models.meta import Meta
from mpt_api_client.models.model_collection import ModelCollection

ResourceData = dict[str, Any]

Expand Down Expand Up @@ -231,21 +232,19 @@ def __repr__(self) -> str:
return f"<{class_name} {self.id}>"

@classmethod
def new(cls, resource_data: ResourceData | None = None, meta: Meta | None = None) -> Self:
"""Creates a new resource from ResourceData and Meta."""
return cls(resource_data, meta=meta)

@classmethod
def from_response(cls, response: Response) -> Self:
def from_response(cls, response: Response) -> Self | ModelCollection[Self]:
"""Creates a Model from a response.

Args:
response: The httpx response object.
"""
response_data = response.json()

if isinstance(response_data, dict):
meta = Meta.from_response(response)
response_data.pop("$meta", None)
if not isinstance(response_data, dict):
raise TypeError("Response data must be a dict.")
meta = Meta.from_response(response)
return cls.new(response_data, meta)
return cls(response_data, meta)
if isinstance(response_data, list):
return ModelCollection([cls(data_item) for data_item in response_data])

raise TypeError(f"Incompatible response data type '{type(response_data).__name__}'.")
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
from collections.abc import Iterator
from typing import TYPE_CHECKING

from mpt_api_client.models.meta import Meta
from mpt_api_client.models.model import Model, ResourceData

ResourceList = list[ResourceData]
if TYPE_CHECKING:
from mpt_api_client.models.model import Model, ResourceData

ResourceList = list["ResourceData"]

class Collection[ItemType: Model]:

class ModelCollection[ItemType: "Model"]:
"""Provides a collection to interact with api collection data using fluent interfaces."""

def __init__(self, resources: list[ItemType] | None = None, meta: Meta | None = None) -> None:
Expand Down
12 changes: 9 additions & 3 deletions mpt_api_client/resources/accounts/accounts_user_groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
)
from mpt_api_client.models import Model
from mpt_api_client.models.model import ResourceData
from mpt_api_client.models.model_collection import ModelCollection


class AccountsUserGroup(Model):
Expand All @@ -35,14 +36,17 @@ class AccountsUserGroupsService(
):
"""Account User Groups Service."""

def update(self, resource_data: ResourceData) -> AccountsUserGroup:
def update(
self, resource_data: ResourceData
) -> AccountsUserGroup | ModelCollection[AccountsUserGroup]:
"""Update Account User Group.

Args:
resource_data (ResourceData): Resource data to update.

Returns:
AccountsUserGroup: Updated Account User Group.
AccountsUserGroup | ModelCollection[AccountsUserGroup]: Updated Account User Group,
or a ModelCollection[AccountsUserGroup] when the service returns a list response.
"""
response = self.http_client.request("put", self.path, json=resource_data)

Expand All @@ -59,7 +63,9 @@ class AsyncAccountsUserGroupsService(
):
"""Asynchronous Account User Groups Service."""

async def update(self, resource_data: ResourceData) -> AccountsUserGroup:
async def update(
self, resource_data: ResourceData
) -> AccountsUserGroup | ModelCollection[AccountsUserGroup]:
"""Update Account User Group.

Args:
Expand Down
17 changes: 13 additions & 4 deletions mpt_api_client/resources/billing/custom_ledgers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from mpt_api_client.http.types import FileContent, FileTypes
from mpt_api_client.http.url_utils import join_url_path
from mpt_api_client.models import Model
from mpt_api_client.models.model_collection import ModelCollection
from mpt_api_client.resources.billing.custom_ledger_attachments import (
AsyncCustomLedgerAttachmentsService,
CustomLedgerAttachmentsService,
Expand Down Expand Up @@ -46,15 +47,19 @@ class CustomLedgersService(
):
"""Custom Ledgers service."""

def upload(self, custom_ledger_id: str, file: FileTypes) -> CustomLedger:
def upload(
self, custom_ledger_id: str, file: FileTypes
) -> CustomLedger | ModelCollection[CustomLedger]:
"""Upload custom ledger file.

Args:
custom_ledger_id: Custom Ledger ID.
file: Custom Ledger file.

Returns:
CustomLedger: Created resource.
CustomLedger | ModelCollection[CustomLedger]: The uploaded resource as a single
CustomLedger instance, or a ModelCollection[CustomLedger] when the response contains
multiple records.
"""
files: dict[str, FileTypes] = {}

Expand Down Expand Up @@ -105,15 +110,19 @@ class AsyncCustomLedgersService(
):
"""Async Custom Ledgers service."""

async def upload(self, custom_ledger_id: str, file: FileTypes) -> CustomLedger:
async def upload(
self, custom_ledger_id: str, file: FileTypes
) -> CustomLedger | ModelCollection[CustomLedger]:
"""Upload custom ledger file.

Args:
custom_ledger_id: Custom Ledger ID.
file: Custom Ledger file.

Returns:
CustomLedger: Created resource.
CustomLedger | ModelCollection[CustomLedger]: The uploaded resource as a single
CustomLedger instance, or a ModelCollection[CustomLedger] when the response contains
multiple records.
"""
files: dict[str, FileTypes] = {}

Expand Down
15 changes: 11 additions & 4 deletions mpt_api_client/resources/billing/journals.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from mpt_api_client.http.types import FileTypes
from mpt_api_client.http.url_utils import join_url_path
from mpt_api_client.models import Model
from mpt_api_client.models.model_collection import ModelCollection
from mpt_api_client.resources.billing.journal_attachments import (
AsyncJournalAttachmentsService,
JournalAttachmentsService,
Expand Down Expand Up @@ -46,15 +47,18 @@ class JournalsService(
):
"""Journals service."""

def upload(self, journal_id: str, file: FileTypes | None = None) -> Journal: # noqa: WPS110
def upload(
self, journal_id: str, file: FileTypes | None = None
) -> Journal | ModelCollection[Journal]: # noqa: WPS110
"""Upload journal file.

Args:
journal_id: Journal ID.
file: journal file.

Returns:
Journal: Created resource.
Journal | ModelCollection[Journal]: The uploaded resource as a single Journal
instance, or a ModelCollection[Journal] when the response contains multiple records.
"""
files = {}

Expand Down Expand Up @@ -104,15 +108,18 @@ class AsyncJournalsService(
):
"""Async Journals service."""

async def upload(self, journal_id: str, file: FileTypes | None = None) -> Journal: # noqa: WPS110
async def upload(
self, journal_id: str, file: FileTypes | None = None
) -> Journal | ModelCollection[Journal]: # noqa: WPS110
"""Upload journal file.

Args:
journal_id: Journal ID.
file: journal file.

Returns:
Journal: Created resource.
Journal | ModelCollection[Journal]: The uploaded resource as a single Journal
instance, or a ModelCollection[Journal] when the response contains multiple records.
"""
files = {}

Expand Down
17 changes: 13 additions & 4 deletions mpt_api_client/resources/notifications/batches.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
GetMixin,
)
from mpt_api_client.models import FileModel, Model
from mpt_api_client.models.model_collection import ModelCollection


class Batch(Model):
Expand Down Expand Up @@ -37,15 +38,19 @@ class BatchesService(
):
"""Notifications Batches service."""

def get_attachment(self, batch_id: str, attachment_id: str) -> BatchAttachment:
def get_attachment(
self, batch_id: str, attachment_id: str
) -> BatchAttachment | ModelCollection[BatchAttachment]:
"""Get batch attachment.

Args:
batch_id: Batch ID.
attachment_id: Attachment ID.

Returns:
BatchAttachment containing the attachment data.
BatchAttachment | ModelCollection[BatchAttachment]: A single BatchAttachment when
the response contains one record, or a ModelCollection[BatchAttachment] when the
response contains multiple attachments.
"""
response = self.http_client.request(
"get",
Expand Down Expand Up @@ -81,15 +86,19 @@ class AsyncBatchesService(
):
"""Async Notifications Batches service."""

async def get_attachment(self, batch_id: str, attachment_id: str) -> BatchAttachment:
async def get_attachment(
self, batch_id: str, attachment_id: str
) -> BatchAttachment | ModelCollection[BatchAttachment]:
"""Get batch attachment.

Args:
batch_id: Batch ID.
attachment_id: Attachment ID.

Returns:
BatchAttachment containing the attachment data.
BatchAttachment | ModelCollection[BatchAttachment]: A single BatchAttachment when
the response contains one record, or a ModelCollection[BatchAttachment] when the
response contains multiple attachments.
"""
response = await self.http_client.request(
"get",
Expand Down
Loading