diff --git a/mpt_api_client/resources/billing/mixins.py b/mpt_api_client/resources/billing/mixins.py deleted file mode 100644 index bda8a7c7..00000000 --- a/mpt_api_client/resources/billing/mixins.py +++ /dev/null @@ -1,426 +0,0 @@ -from mpt_api_client.http.mixins import ( - AsyncCreateFileMixin, - AsyncDeleteMixin, - AsyncDownloadFileMixin, - AsyncGetMixin, - AsyncUpdateMixin, - CreateFileMixin, - DeleteMixin, - DownloadFileMixin, - GetMixin, - UpdateMixin, -) -from mpt_api_client.models import ResourceData - - -# TODO: Consider moving Regeneratable mixins to http/mixins if publishable and activatable are moved -# TODO: Consider reorganizing functions in mixins to reduce duplication and differences amongst -# different domains -class RegeneratableMixin[Model]: - """Regeneratable mixin adds the ability to regenerate resources.""" - - def regenerate(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Regenerate resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "regenerate", json=resource_data - ) - - def submit(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Submit resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "submit", json=resource_data - ) - - def enquiry(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Enquiry resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "enquiry", json=resource_data - ) - - def accept(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Accept resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "accept", json=resource_data - ) - - -class AsyncRegeneratableMixin[Model]: - """Regeneratable mixin adds the ability to regenerate resources.""" - - async def regenerate( - self, resource_id: str, resource_data: ResourceData | None = None - ) -> Model: - """Regenerate resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return await self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "regenerate", json=resource_data - ) - - async def submit(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Submit resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return await self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "submit", json=resource_data - ) - - async def enquiry(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Enquiry resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return await self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "enquiry", json=resource_data - ) - - async def accept(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Accept resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return await self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "accept", json=resource_data - ) - - -class RecalculatableMixin[Model]: - """Recalculatable mixin adds the ability to recalculate resources.""" - - def recalculate(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Recalculate resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "recalculate", json=resource_data - ) - - def accept(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Accept resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "accept", json=resource_data - ) - - def queue(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Queue resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "queue", json=resource_data - ) - - -class AsyncRecalculatableMixin[Model]: - """Recalculatable mixin adds the ability to recalculate resources.""" - - async def recalculate( - self, resource_id: str, resource_data: ResourceData | None = None - ) -> Model: - """Recalculate resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return await self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "recalculate", json=resource_data - ) - - async def accept(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Accept resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return await self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "accept", json=resource_data - ) - - async def queue(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Queue resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return await self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "queue", json=resource_data - ) - - -class IssuableMixin[Model]: - """Issuable mixin adds the ability to issue resources.""" - - def issue(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Issue resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "issue", json=resource_data - ) - - def cancel(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Cancel resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "cancel", json=resource_data - ) - - def error(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Error resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "error", json=resource_data - ) - - def pending(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Pending resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "pending", json=resource_data - ) - - def queue(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Queue resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "queue", json=resource_data - ) - - def retry(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Retry resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "retry", json=resource_data - ) - - def recalculate(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Recalculate resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "recalculate", json=resource_data - ) - - -class AsyncIssuableMixin[Model]: - """Issuable mixin adds the ability to issue resources.""" - - async def issue(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Issue resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return await self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "issue", json=resource_data - ) - - async def cancel(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Cancel resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return await self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "cancel", json=resource_data - ) - - async def error(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Error resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return await self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "error", json=resource_data - ) - - async def pending(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Pending resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return await self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "pending", json=resource_data - ) - - async def queue(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Queue resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return await self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "queue", json=resource_data - ) - - async def retry(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Retry resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return await self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "retry", json=resource_data - ) - - async def recalculate( - self, resource_id: str, resource_data: ResourceData | None = None - ) -> Model: - """Recalculate resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return await self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "recalculate", json=resource_data - ) - - -class AcceptableMixin[Model]: - """Acceptable mixin adds the ability to accept resources.""" - - def accept(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Accept resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "accept", json=resource_data - ) - - def queue(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Queue resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "queue", json=resource_data - ) - - -class AsyncAcceptableMixin[Model]: - """Acceptable mixin adds the ability to accept resources.""" - - async def accept(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Accept resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return await self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "accept", json=resource_data - ) - - async def queue(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Queue resource. - - Args: - resource_id: Resource ID - resource_data: Resource data will be updated - """ - return await self._resource_action( # type: ignore[attr-defined, no-any-return] - resource_id, "POST", "queue", json=resource_data - ) - - -class AttachmentMixin[Model]( - CreateFileMixin[Model], - UpdateMixin[Model], - DeleteMixin, - DownloadFileMixin[Model], - GetMixin[Model], -): - """Attachment mixin.""" - - -class AsyncAttachmentMixin[Model]( - AsyncCreateFileMixin[Model], - AsyncUpdateMixin[Model], - AsyncDeleteMixin, - AsyncDownloadFileMixin[Model], - AsyncGetMixin[Model], -): - """Async Attachment mixin.""" diff --git a/mpt_api_client/resources/billing/mixins/__init__.py b/mpt_api_client/resources/billing/mixins/__init__.py new file mode 100644 index 00000000..c807f375 --- /dev/null +++ b/mpt_api_client/resources/billing/mixins/__init__.py @@ -0,0 +1,33 @@ +from mpt_api_client.resources.billing.mixins.acceptable_mixin import ( + AcceptableMixin, + AsyncAcceptableMixin, +) +from mpt_api_client.resources.billing.mixins.attachment_mixin import ( + AsyncAttachmentMixin, + AttachmentMixin, +) +from mpt_api_client.resources.billing.mixins.issuable_mixin import ( + AsyncIssuableMixin, + IssuableMixin, +) +from mpt_api_client.resources.billing.mixins.recalculatable_mixin import ( + AsyncRecalculatableMixin, + RecalculatableMixin, +) +from mpt_api_client.resources.billing.mixins.regeneratable_mixin import ( + AsyncRegeneratableMixin, + RegeneratableMixin, +) + +__all__ = [ # noqa: WPS410 + "AcceptableMixin", + "AsyncAcceptableMixin", + "AsyncAttachmentMixin", + "AsyncIssuableMixin", + "AsyncRecalculatableMixin", + "AsyncRegeneratableMixin", + "AttachmentMixin", + "IssuableMixin", + "RecalculatableMixin", + "RegeneratableMixin", +] diff --git a/mpt_api_client/resources/billing/mixins/acceptable_mixin.py b/mpt_api_client/resources/billing/mixins/acceptable_mixin.py new file mode 100644 index 00000000..83a0f511 --- /dev/null +++ b/mpt_api_client/resources/billing/mixins/acceptable_mixin.py @@ -0,0 +1,53 @@ +from mpt_api_client.models import ResourceData + + +class AcceptableMixin[Model]: + """Acceptable mixin adds the ability to accept resources.""" + + def accept(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Accept resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "accept", json=resource_data + ) + + def queue(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Queue resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "queue", json=resource_data + ) + + +class AsyncAcceptableMixin[Model]: + """Acceptable mixin adds the ability to accept resources.""" + + async def accept(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Accept resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return await self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "accept", json=resource_data + ) + + async def queue(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Queue resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return await self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "queue", json=resource_data + ) diff --git a/mpt_api_client/resources/billing/mixins/attachment_mixin.py b/mpt_api_client/resources/billing/mixins/attachment_mixin.py new file mode 100644 index 00000000..dbb8a03f --- /dev/null +++ b/mpt_api_client/resources/billing/mixins/attachment_mixin.py @@ -0,0 +1,32 @@ +from mpt_api_client.http.mixins import ( + AsyncCreateFileMixin, + AsyncDeleteMixin, + AsyncDownloadFileMixin, + AsyncGetMixin, + AsyncUpdateMixin, + CreateFileMixin, + DeleteMixin, + DownloadFileMixin, + GetMixin, + UpdateMixin, +) + + +class AttachmentMixin[Model]( + CreateFileMixin[Model], + UpdateMixin[Model], + DeleteMixin, + DownloadFileMixin[Model], + GetMixin[Model], +): + """Attachment mixin.""" + + +class AsyncAttachmentMixin[Model]( + AsyncCreateFileMixin[Model], + AsyncUpdateMixin[Model], + AsyncDeleteMixin, + AsyncDownloadFileMixin[Model], + AsyncGetMixin[Model], +): + """Async Attachment mixin.""" diff --git a/mpt_api_client/resources/billing/mixins/issuable_mixin.py b/mpt_api_client/resources/billing/mixins/issuable_mixin.py new file mode 100644 index 00000000..723856b2 --- /dev/null +++ b/mpt_api_client/resources/billing/mixins/issuable_mixin.py @@ -0,0 +1,165 @@ +from mpt_api_client.models import ResourceData + + +class IssuableMixin[Model]: + """Issuable mixin adds the ability to issue resources.""" + + def issue(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Issue resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "issue", json=resource_data + ) + + def cancel(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Cancel resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "cancel", json=resource_data + ) + + def error(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Error resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "error", json=resource_data + ) + + def pending(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Pending resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "pending", json=resource_data + ) + + def queue(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Queue resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "queue", json=resource_data + ) + + def retry(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Retry resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "retry", json=resource_data + ) + + def recalculate(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Recalculate resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "recalculate", json=resource_data + ) + + +class AsyncIssuableMixin[Model]: + """Issuable mixin adds the ability to issue resources.""" + + async def issue(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Issue resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return await self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "issue", json=resource_data + ) + + async def cancel(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Cancel resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return await self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "cancel", json=resource_data + ) + + async def error(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Error resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return await self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "error", json=resource_data + ) + + async def pending(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Pending resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return await self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "pending", json=resource_data + ) + + async def queue(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Queue resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return await self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "queue", json=resource_data + ) + + async def retry(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Retry resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return await self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "retry", json=resource_data + ) + + async def recalculate( + self, resource_id: str, resource_data: ResourceData | None = None + ) -> Model: + """Recalculate resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return await self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "recalculate", json=resource_data + ) diff --git a/mpt_api_client/resources/billing/mixins/recalculatable_mixin.py b/mpt_api_client/resources/billing/mixins/recalculatable_mixin.py new file mode 100644 index 00000000..44163824 --- /dev/null +++ b/mpt_api_client/resources/billing/mixins/recalculatable_mixin.py @@ -0,0 +1,77 @@ +from mpt_api_client.models import ResourceData + + +class RecalculatableMixin[Model]: + """Recalculatable mixin adds the ability to recalculate resources.""" + + def recalculate(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Recalculate resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "recalculate", json=resource_data + ) + + def accept(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Accept resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "accept", json=resource_data + ) + + def queue(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Queue resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "queue", json=resource_data + ) + + +class AsyncRecalculatableMixin[Model]: + """Recalculatable mixin adds the ability to recalculate resources.""" + + async def recalculate( + self, resource_id: str, resource_data: ResourceData | None = None + ) -> Model: + """Recalculate resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return await self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "recalculate", json=resource_data + ) + + async def accept(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Accept resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return await self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "accept", json=resource_data + ) + + async def queue(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Queue resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return await self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "queue", json=resource_data + ) diff --git a/mpt_api_client/resources/billing/mixins/regeneratable_mixin.py b/mpt_api_client/resources/billing/mixins/regeneratable_mixin.py new file mode 100644 index 00000000..74061975 --- /dev/null +++ b/mpt_api_client/resources/billing/mixins/regeneratable_mixin.py @@ -0,0 +1,99 @@ +from mpt_api_client.models import ResourceData + + +class RegeneratableMixin[Model]: + """Regeneratable mixin adds the ability to regenerate resources.""" + + def regenerate(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Regenerate resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "regenerate", json=resource_data + ) + + def submit(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Submit resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "submit", json=resource_data + ) + + def enquiry(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Enquiry resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "enquiry", json=resource_data + ) + + def accept(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Accept resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "accept", json=resource_data + ) + + +class AsyncRegeneratableMixin[Model]: + """Regeneratable mixin adds the ability to regenerate resources.""" + + async def regenerate( + self, resource_id: str, resource_data: ResourceData | None = None + ) -> Model: + """Regenerate resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return await self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "regenerate", json=resource_data + ) + + async def submit(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Submit resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return await self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "submit", json=resource_data + ) + + async def enquiry(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Enquiry resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return await self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "enquiry", json=resource_data + ) + + async def accept(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Accept resource. + + Args: + resource_id: Resource ID + resource_data: Resource data will be updated + """ + return await self._resource_action( # type: ignore[attr-defined, no-any-return] + resource_id, "POST", "accept", json=resource_data + ) diff --git a/tests/unit/resources/billing/mixins/__init__.py b/tests/unit/resources/billing/mixins/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit/resources/billing/mixins/test_acceptable_mixin.py b/tests/unit/resources/billing/mixins/test_acceptable_mixin.py new file mode 100644 index 00000000..576ee060 --- /dev/null +++ b/tests/unit/resources/billing/mixins/test_acceptable_mixin.py @@ -0,0 +1,153 @@ +import httpx +import pytest +import respx + +from mpt_api_client.http.async_service import AsyncService +from mpt_api_client.http.service import Service +from mpt_api_client.resources.billing.mixins.acceptable_mixin import ( + AcceptableMixin, + AsyncAcceptableMixin, +) +from tests.unit.conftest import DummyModel + + +class DummyAcceptableService( + AcceptableMixin[DummyModel], + Service[DummyModel], +): + _endpoint = "/public/v1/dummy/acceptable/" + _model_class = DummyModel + _collection_key = "data" + + +class DummyAsyncAcceptableService( + AsyncAcceptableMixin[DummyModel], + AsyncService[DummyModel], +): + _endpoint = "/public/v1/dummy/acceptable/" + _model_class = DummyModel + _collection_key = "data" + + +@pytest.fixture +def acceptable_service(http_client): + return DummyAcceptableService(http_client=http_client) + + +@pytest.fixture +def async_acceptable_service(async_http_client): + return DummyAsyncAcceptableService(http_client=async_http_client) + + +@pytest.mark.parametrize( + ("action", "input_status"), + [ + ("accept", {"id": "OBJ-0000-0001", "status": "update"}), + ("queue", {"id": "OBJ-0000-0001", "status": "update"}), + ], +) +def test_acceptable_resource_actions(acceptable_service, action, input_status): + request_expected_content = b'{"id":"OBJ-0000-0001","status":"update"}' + response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} + with respx.mock: + mock_route = respx.post( + f"https://api.example.com/public/v1/dummy/acceptable/OBJ-0000-0001/{action}" + ).mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + headers={"content-type": "application/json"}, + json=response_expected_data, + ) + ) + + result = getattr(acceptable_service, action)("OBJ-0000-0001", input_status) + + assert mock_route.call_count == 1 + request = mock_route.calls[0].request + assert request.content == request_expected_content + assert result.to_dict() == response_expected_data + assert isinstance(result, DummyModel) + + +@pytest.mark.parametrize( + ("action", "input_status"), + [("accept", None), ("queue", None)], +) +def test_actions_no_data(acceptable_service, action, input_status): + request_expected_content = b"" + response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} + with respx.mock: + mock_route = respx.post( + f"https://api.example.com/public/v1/dummy/acceptable/OBJ-0000-0001/{action}" + ).mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + headers={"content-type": "application/json"}, + json=response_expected_data, + ) + ) + + result = getattr(acceptable_service, action)("OBJ-0000-0001", input_status) + + assert mock_route.call_count == 1 + request = mock_route.calls[0].request + assert request.content == request_expected_content + assert result.to_dict() == response_expected_data + assert isinstance(result, DummyModel) + + +@pytest.mark.parametrize( + ("action", "input_status"), + [ + ("accept", {"id": "OBJ-0000-0001", "status": "update"}), + ("queue", {"id": "OBJ-0000-0001", "status": "update"}), + ], +) +async def test_async_acceptable_resource_actions(async_acceptable_service, action, input_status): + request_expected_content = b'{"id":"OBJ-0000-0001","status":"update"}' + response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} + with respx.mock: + mock_route = respx.post( + f"https://api.example.com/public/v1/dummy/acceptable/OBJ-0000-0001/{action}" + ).mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + headers={"content-type": "application/json"}, + json=response_expected_data, + ) + ) + + result = await getattr(async_acceptable_service, action)("OBJ-0000-0001", input_status) + + assert mock_route.call_count == 1 + request = mock_route.calls[0].request + assert request.content == request_expected_content + assert result.to_dict() == response_expected_data + assert isinstance(result, DummyModel) + + +@pytest.mark.parametrize( + ("action", "input_status"), + [("accept", None), ("queue", None)], +) +async def test_async_actions_no_data(async_acceptable_service, action, input_status): + request_expected_content = b"" + response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} + with respx.mock: + mock_route = respx.post( + f"https://api.example.com/public/v1/dummy/acceptable/OBJ-0000-0001/{action}" + ).mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + headers={"content-type": "application/json"}, + json=response_expected_data, + ) + ) + + result = await getattr(async_acceptable_service, action)("OBJ-0000-0001", input_status) + + assert mock_route.call_count == 1 + request = mock_route.calls[0].request + assert request.content == request_expected_content + assert result.to_dict() == response_expected_data + assert isinstance(result, DummyModel) diff --git a/tests/unit/resources/billing/mixins/test_issuable_mixin.py b/tests/unit/resources/billing/mixins/test_issuable_mixin.py new file mode 100644 index 00000000..3eab1e87 --- /dev/null +++ b/tests/unit/resources/billing/mixins/test_issuable_mixin.py @@ -0,0 +1,181 @@ +import httpx +import pytest +import respx + +from mpt_api_client.http.async_service import AsyncService +from mpt_api_client.http.service import Service +from mpt_api_client.resources.billing.mixins.issuable_mixin import ( + AsyncIssuableMixin, + IssuableMixin, +) +from tests.unit.conftest import DummyModel + + +class DummyIssuableService( + IssuableMixin[DummyModel], + Service[DummyModel], +): + _endpoint = "/public/v1/dummy/issuable/" + _model_class = DummyModel + _collection_key = "data" + + +class DummyAsyncIssuableService( + AsyncIssuableMixin[DummyModel], + AsyncService[DummyModel], +): + _endpoint = "/public/v1/dummy/issuable/" + _model_class = DummyModel + _collection_key = "data" + + +@pytest.fixture +def issuable_service(http_client): + return DummyIssuableService(http_client=http_client) + + +@pytest.fixture +def async_issuable_service(async_http_client): + return DummyAsyncIssuableService(http_client=async_http_client) + + +@pytest.mark.parametrize( + ("action", "input_status"), + [ + ("issue", {"id": "OBJ-0000-0001", "status": "update"}), + ("cancel", {"id": "OBJ-0000-0001", "status": "update"}), + ("error", {"id": "OBJ-0000-0001", "status": "update"}), + ("pending", {"id": "OBJ-0000-0001", "status": "update"}), + ("queue", {"id": "OBJ-0000-0001", "status": "update"}), + ("retry", {"id": "OBJ-0000-0001", "status": "update"}), + ("recalculate", {"id": "OBJ-0000-0001", "status": "update"}), + ], +) +def test_issuable_resource_actions(issuable_service, action, input_status): + request_expected_content = b'{"id":"OBJ-0000-0001","status":"update"}' + response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} + with respx.mock: + mock_route = respx.post( + f"https://api.example.com/public/v1/dummy/issuable/OBJ-0000-0001/{action}" + ).mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + headers={"content-type": "application/json"}, + json=response_expected_data, + ) + ) + + result = getattr(issuable_service, action)("OBJ-0000-0001", input_status) + + assert mock_route.call_count == 1 + request = mock_route.calls[0].request + assert request.content == request_expected_content + assert result.to_dict() == response_expected_data + assert isinstance(result, DummyModel) + + +@pytest.mark.parametrize( + ("action", "input_status"), + [ + ("issue", None), + ("cancel", None), + ("error", None), + ("pending", None), + ("queue", None), + ("retry", None), + ("recalculate", None), + ], +) +def test_issuable_resource_actions_no_data(issuable_service, action, input_status): + request_expected_content = b"" + response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} + with respx.mock: + mock_route = respx.post( + f"https://api.example.com/public/v1/dummy/issuable/OBJ-0000-0001/{action}" + ).mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + headers={"content-type": "application/json"}, + json=response_expected_data, + ) + ) + + result = getattr(issuable_service, action)("OBJ-0000-0001", input_status) + + assert mock_route.call_count == 1 + request = mock_route.calls[0].request + assert request.content == request_expected_content + assert result.to_dict() == response_expected_data + assert isinstance(result, DummyModel) + + +@pytest.mark.parametrize( + ("action", "input_status"), + [ + ("issue", {"id": "OBJ-0000-0001", "status": "update"}), + ("cancel", {"id": "OBJ-0000-0001", "status": "update"}), + ("error", {"id": "OBJ-0000-0001", "status": "update"}), + ("pending", {"id": "OBJ-0000-0001", "status": "update"}), + ("queue", {"id": "OBJ-0000-0001", "status": "update"}), + ("retry", {"id": "OBJ-0000-0001", "status": "update"}), + ("recalculate", {"id": "OBJ-0000-0001", "status": "update"}), + ], +) +async def test_async_issuable_resource_actions(async_issuable_service, action, input_status): + request_expected_content = b'{"id":"OBJ-0000-0001","status":"update"}' + response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} + with respx.mock: + mock_route = respx.post( + f"https://api.example.com/public/v1/dummy/issuable/OBJ-0000-0001/{action}" + ).mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + headers={"content-type": "application/json"}, + json=response_expected_data, + ) + ) + + result = await getattr(async_issuable_service, action)("OBJ-0000-0001", input_status) + + assert mock_route.call_count == 1 + request = mock_route.calls[0].request + assert request.content == request_expected_content + assert result.to_dict() == response_expected_data + assert isinstance(result, DummyModel) + + +@pytest.mark.parametrize( + ("action", "input_status"), + [ + ("issue", None), + ("cancel", None), + ("error", None), + ("pending", None), + ("queue", None), + ("retry", None), + ("recalculate", None), + ], +) +async def test_async_issuable_resource_actions_no_data( + async_issuable_service, action, input_status +): + request_expected_content = b"" + response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} + with respx.mock: + mock_route = respx.post( + f"https://api.example.com/public/v1/dummy/issuable/OBJ-0000-0001/{action}" + ).mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + headers={"content-type": "application/json"}, + json=response_expected_data, + ) + ) + + result = await getattr(async_issuable_service, action)("OBJ-0000-0001", input_status) + + assert mock_route.call_count == 1 + request = mock_route.calls[0].request + assert request.content == request_expected_content + assert result.to_dict() == response_expected_data + assert isinstance(result, DummyModel) diff --git a/tests/unit/resources/billing/mixins/test_recalculatable_mixin.py b/tests/unit/resources/billing/mixins/test_recalculatable_mixin.py new file mode 100644 index 00000000..e24eb3b2 --- /dev/null +++ b/tests/unit/resources/billing/mixins/test_recalculatable_mixin.py @@ -0,0 +1,157 @@ +import httpx +import pytest +import respx + +from mpt_api_client.http.async_service import AsyncService +from mpt_api_client.http.service import Service +from mpt_api_client.resources.billing.mixins.recalculatable_mixin import ( + AsyncRecalculatableMixin, + RecalculatableMixin, +) +from tests.unit.conftest import DummyModel + + +class DummyRecalculatableService( + RecalculatableMixin[DummyModel], + Service[DummyModel], +): + _endpoint = "/public/v1/dummy/recalculatable/" + _model_class = DummyModel + _collection_key = "data" + + +class DummyAsyncRecalculatableService( + AsyncRecalculatableMixin[DummyModel], + AsyncService[DummyModel], +): + _endpoint = "/public/v1/dummy/recalculatable/" + _model_class = DummyModel + _collection_key = "data" + + +@pytest.fixture +def recalculatable_service(http_client): + return DummyRecalculatableService(http_client=http_client) + + +@pytest.fixture +def async_recalculatable_service(async_http_client): + return DummyAsyncRecalculatableService(http_client=async_http_client) + + +@pytest.mark.parametrize( + ("action", "input_status"), + [ + ("recalculate", {"id": "OBJ-0000-0001", "status": "update"}), + ("accept", {"id": "OBJ-0000-0001", "status": "update"}), + ("queue", {"id": "OBJ-0000-0001", "status": "update"}), + ], +) +def test_recalculate_resource_actions(recalculatable_service, action, input_status): + request_expected_content = b'{"id":"OBJ-0000-0001","status":"update"}' + response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} + with respx.mock: + mock_route = respx.post( + f"https://api.example.com/public/v1/dummy/recalculatable/OBJ-0000-0001/{action}" + ).mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + headers={"content-type": "application/json"}, + json=response_expected_data, + ) + ) + + result = getattr(recalculatable_service, action)("OBJ-0000-0001", input_status) + + assert mock_route.call_count == 1 + request = mock_route.calls[0].request + assert request.content == request_expected_content + assert result.to_dict() == response_expected_data + assert isinstance(result, DummyModel) + + +@pytest.mark.parametrize( + ("action", "input_status"), + [("recalculate", None), ("accept", None), ("queue", None)], +) +def test_actions_no_data(recalculatable_service, action, input_status): + request_expected_content = b"" + response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} + with respx.mock: + mock_route = respx.post( + f"https://api.example.com/public/v1/dummy/recalculatable/OBJ-0000-0001/{action}" + ).mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + headers={"content-type": "application/json"}, + json=response_expected_data, + ) + ) + + result = getattr(recalculatable_service, action)("OBJ-0000-0001", input_status) + + assert mock_route.call_count == 1 + request = mock_route.calls[0].request + assert request.content == request_expected_content + assert result.to_dict() == response_expected_data + assert isinstance(result, DummyModel) + + +@pytest.mark.parametrize( + ("action", "input_status"), + [ + ("recalculate", {"id": "OBJ-0000-0001", "status": "update"}), + ("accept", {"id": "OBJ-0000-0001", "status": "update"}), + ("queue", {"id": "OBJ-0000-0001", "status": "update"}), + ], +) +async def test_async_recalculate_resource_actions( + async_recalculatable_service, action, input_status +): + request_expected_content = b'{"id":"OBJ-0000-0001","status":"update"}' + response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} + with respx.mock: + mock_route = respx.post( + f"https://api.example.com/public/v1/dummy/recalculatable/OBJ-0000-0001/{action}" + ).mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + headers={"content-type": "application/json"}, + json=response_expected_data, + ) + ) + + result = await getattr(async_recalculatable_service, action)("OBJ-0000-0001", input_status) + + assert mock_route.call_count == 1 + request = mock_route.calls[0].request + assert request.content == request_expected_content + assert result.to_dict() == response_expected_data + assert isinstance(result, DummyModel) + + +@pytest.mark.parametrize( + ("action", "input_status"), + [("recalculate", None), ("accept", None), ("queue", None)], +) +async def test_async_actions_no_data(async_recalculatable_service, action, input_status): + request_expected_content = b"" + response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} + with respx.mock: + mock_route = respx.post( + f"https://api.example.com/public/v1/dummy/recalculatable/OBJ-0000-0001/{action}" + ).mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + headers={"content-type": "application/json"}, + json=response_expected_data, + ) + ) + + result = await getattr(async_recalculatable_service, action)("OBJ-0000-0001", input_status) + + assert mock_route.call_count == 1 + request = mock_route.calls[0].request + assert request.content == request_expected_content + assert result.to_dict() == response_expected_data + assert isinstance(result, DummyModel) diff --git a/tests/unit/resources/billing/mixins/test_regeneratable_mixin.py b/tests/unit/resources/billing/mixins/test_regeneratable_mixin.py new file mode 100644 index 00000000..0335e440 --- /dev/null +++ b/tests/unit/resources/billing/mixins/test_regeneratable_mixin.py @@ -0,0 +1,169 @@ +import httpx +import pytest +import respx + +from mpt_api_client.http.async_service import AsyncService +from mpt_api_client.http.service import Service +from mpt_api_client.resources.billing.mixins.regeneratable_mixin import ( + AsyncRegeneratableMixin, + RegeneratableMixin, +) +from tests.unit.conftest import DummyModel + + +class DummyRegeneratableService( + RegeneratableMixin[DummyModel], + Service[DummyModel], +): + _endpoint = "/public/v1/dummy/regeneratable/" + _model_class = DummyModel + _collection_key = "data" + + +class DummyAsyncRegeneratableService( + AsyncRegeneratableMixin[DummyModel], + AsyncService[DummyModel], +): + _endpoint = "/public/v1/dummy/regeneratable/" + _model_class = DummyModel + _collection_key = "data" + + +@pytest.fixture +def regeneratable_service(http_client): + return DummyRegeneratableService(http_client=http_client) + + +@pytest.fixture +def async_regeneratable_service(async_http_client): + return DummyAsyncRegeneratableService(http_client=async_http_client) + + +@pytest.mark.parametrize( + ("action", "input_status"), + [ + ("regenerate", {"id": "OBJ-0000-0001", "status": "update"}), + ("submit", {"id": "OBJ-0000-0001", "status": "update"}), + ("enquiry", {"id": "OBJ-0000-0001", "status": "update"}), + ("accept", {"id": "OBJ-0000-0001", "status": "update"}), + ], +) +def test_custom_resource_actions(regeneratable_service, action, input_status): + request_expected_content = b'{"id":"OBJ-0000-0001","status":"update"}' + response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} + with respx.mock: + mock_route = respx.post( + f"https://api.example.com/public/v1/dummy/regeneratable/OBJ-0000-0001/{action}" + ).mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + headers={"content-type": "application/json"}, + json=response_expected_data, + ) + ) + + result = getattr(regeneratable_service, action)("OBJ-0000-0001", input_status) + + assert mock_route.call_count == 1 + request = mock_route.calls[0].request + assert request.content == request_expected_content + assert result.to_dict() == response_expected_data + assert isinstance(result, DummyModel) + + +@pytest.mark.parametrize( + ("action", "input_status"), + [ + ("regenerate", None), + ("submit", None), + ("enquiry", None), + ("accept", None), + ], +) +def test_custom_resource_actions_no_data(regeneratable_service, action, input_status): + request_expected_content = b"" + response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} + with respx.mock: + mock_route = respx.post( + f"https://api.example.com/public/v1/dummy/regeneratable/OBJ-0000-0001/{action}" + ).mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + headers={"content-type": "application/json"}, + json=response_expected_data, + ) + ) + + result = getattr(regeneratable_service, action)("OBJ-0000-0001", input_status) + + assert mock_route.call_count == 1 + request = mock_route.calls[0].request + assert request.content == request_expected_content + assert result.to_dict() == response_expected_data + assert isinstance(result, DummyModel) + + +@pytest.mark.parametrize( + ("action", "input_status"), + [ + ("regenerate", {"id": "OBJ-0000-0001", "status": "update"}), + ("submit", {"id": "OBJ-0000-0001", "status": "update"}), + ("enquiry", {"id": "OBJ-0000-0001", "status": "update"}), + ("accept", {"id": "OBJ-0000-0001", "status": "update"}), + ], +) +async def test_async_custom_resource_actions(async_regeneratable_service, action, input_status): + request_expected_content = b'{"id":"OBJ-0000-0001","status":"update"}' + response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} + with respx.mock: + mock_route = respx.post( + f"https://api.example.com/public/v1/dummy/regeneratable/OBJ-0000-0001/{action}" + ).mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + headers={"content-type": "application/json"}, + json=response_expected_data, + ) + ) + + result = await getattr(async_regeneratable_service, action)("OBJ-0000-0001", input_status) + + assert mock_route.call_count == 1 + request = mock_route.calls[0].request + assert request.content == request_expected_content + assert result.to_dict() == response_expected_data + assert isinstance(result, DummyModel) + + +@pytest.mark.parametrize( + ("action", "input_status"), + [ + ("regenerate", None), + ("submit", None), + ("enquiry", None), + ("accept", None), + ], +) +async def test_async_custom_resource_actions_no_data( + async_regeneratable_service, action, input_status +): + request_expected_content = b"" + response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} + with respx.mock: + mock_route = respx.post( + f"https://api.example.com/public/v1/dummy/regeneratable/OBJ-0000-0001/{action}" + ).mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + headers={"content-type": "application/json"}, + json=response_expected_data, + ) + ) + + result = await getattr(async_regeneratable_service, action)("OBJ-0000-0001", input_status) + + assert mock_route.call_count == 1 + request = mock_route.calls[0].request + assert request.content == request_expected_content + assert result.to_dict() == response_expected_data + assert isinstance(result, DummyModel) diff --git a/tests/unit/resources/billing/test_mixins.py b/tests/unit/resources/billing/test_mixins.py deleted file mode 100644 index 4a709130..00000000 --- a/tests/unit/resources/billing/test_mixins.py +++ /dev/null @@ -1,637 +0,0 @@ -import httpx -import pytest -import respx - -from mpt_api_client.http.async_service import AsyncService -from mpt_api_client.http.service import Service -from mpt_api_client.resources.billing.mixins import ( - AcceptableMixin, - AsyncAcceptableMixin, - AsyncIssuableMixin, - AsyncRecalculatableMixin, - AsyncRegeneratableMixin, - IssuableMixin, - RecalculatableMixin, - RegeneratableMixin, -) -from tests.unit.conftest import DummyModel - - -class DummyRegeneratableService( - RegeneratableMixin[DummyModel], - Service[DummyModel], -): - _endpoint = "/public/v1/dummy/regeneratable/" - _model_class = DummyModel - _collection_key = "data" - - -class DummyAsyncRegeneratableService( - AsyncRegeneratableMixin[DummyModel], - AsyncService[DummyModel], -): - _endpoint = "/public/v1/dummy/regeneratable/" - _model_class = DummyModel - _collection_key = "data" - - -class DummyRecalculatableService( - RecalculatableMixin[DummyModel], - Service[DummyModel], -): - _endpoint = "/public/v1/dummy/recalculatable/" - _model_class = DummyModel - _collection_key = "data" - - -class DummyAsyncRecalculatableService( - AsyncRecalculatableMixin[DummyModel], - AsyncService[DummyModel], -): - _endpoint = "/public/v1/dummy/recalculatable/" - _model_class = DummyModel - _collection_key = "data" - - -class DummyIssuableService( - IssuableMixin[DummyModel], - Service[DummyModel], -): - _endpoint = "/public/v1/dummy/issuable/" - _model_class = DummyModel - _collection_key = "data" - - -class DummyAsyncIssuableService( - AsyncIssuableMixin[DummyModel], - AsyncService[DummyModel], -): - _endpoint = "/public/v1/dummy/issuable/" - _model_class = DummyModel - _collection_key = "data" - - -class DummyAcceptableService( - AcceptableMixin[DummyModel], - Service[DummyModel], -): - _endpoint = "/public/v1/dummy/acceptable/" - _model_class = DummyModel - _collection_key = "data" - - -class DummyAsyncAcceptableService( - AsyncAcceptableMixin[DummyModel], - AsyncService[DummyModel], -): - _endpoint = "/public/v1/dummy/acceptable/" - _model_class = DummyModel - _collection_key = "data" - - -@pytest.fixture -def regeneratable_service(http_client): - return DummyRegeneratableService(http_client=http_client) - - -@pytest.fixture -def async_regeneratable_service(async_http_client): - return DummyAsyncRegeneratableService(http_client=async_http_client) - - -@pytest.fixture -def recalculatable_service(http_client): - return DummyRecalculatableService(http_client=http_client) - - -@pytest.fixture -def async_recalculatable_service(async_http_client): - return DummyAsyncRecalculatableService(http_client=async_http_client) - - -@pytest.fixture -def issuable_service(http_client): - return DummyIssuableService(http_client=http_client) - - -@pytest.fixture -def async_issuable_service(async_http_client): - return DummyAsyncIssuableService(http_client=async_http_client) - - -@pytest.fixture -def acceptable_service(http_client): - return DummyAcceptableService(http_client=http_client) - - -@pytest.fixture -def async_acceptable_service(async_http_client): - return DummyAsyncAcceptableService(http_client=async_http_client) - - -@pytest.mark.parametrize( - ("action", "input_status"), - [ - ("regenerate", {"id": "OBJ-0000-0001", "status": "update"}), - ("submit", {"id": "OBJ-0000-0001", "status": "update"}), - ("enquiry", {"id": "OBJ-0000-0001", "status": "update"}), - ("accept", {"id": "OBJ-0000-0001", "status": "update"}), - ], -) -def test_custom_resource_actions(regeneratable_service, action, input_status): - request_expected_content = b'{"id":"OBJ-0000-0001","status":"update"}' - response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} - with respx.mock: - mock_route = respx.post( - f"https://api.example.com/public/v1/dummy/regeneratable/OBJ-0000-0001/{action}" - ).mock( - return_value=httpx.Response( - status_code=httpx.codes.OK, - headers={"content-type": "application/json"}, - json=response_expected_data, - ) - ) - - result = getattr(regeneratable_service, action)("OBJ-0000-0001", input_status) - - assert mock_route.call_count == 1 - request = mock_route.calls[0].request - assert request.content == request_expected_content - assert result.to_dict() == response_expected_data - assert isinstance(result, DummyModel) - - -@pytest.mark.parametrize( - ("action", "input_status"), - [ - ("regenerate", None), - ("submit", None), - ("enquiry", None), - ("accept", None), - ], -) -def test_custom_resource_actions_no_data(regeneratable_service, action, input_status): - request_expected_content = b"" - response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} - with respx.mock: - mock_route = respx.post( - f"https://api.example.com/public/v1/dummy/regeneratable/OBJ-0000-0001/{action}" - ).mock( - return_value=httpx.Response( - status_code=httpx.codes.OK, - headers={"content-type": "application/json"}, - json=response_expected_data, - ) - ) - - result = getattr(regeneratable_service, action)("OBJ-0000-0001", input_status) - - assert mock_route.call_count == 1 - request = mock_route.calls[0].request - assert request.content == request_expected_content - assert result.to_dict() == response_expected_data - assert isinstance(result, DummyModel) - - -@pytest.mark.parametrize( - ("action", "input_status"), - [ - ("regenerate", {"id": "OBJ-0000-0001", "status": "update"}), - ("submit", {"id": "OBJ-0000-0001", "status": "update"}), - ("enquiry", {"id": "OBJ-0000-0001", "status": "update"}), - ("accept", {"id": "OBJ-0000-0001", "status": "update"}), - ], -) -async def test_async_custom_resource_actions(async_regeneratable_service, action, input_status): - request_expected_content = b'{"id":"OBJ-0000-0001","status":"update"}' - response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} - with respx.mock: - mock_route = respx.post( - f"https://api.example.com/public/v1/dummy/regeneratable/OBJ-0000-0001/{action}" - ).mock( - return_value=httpx.Response( - status_code=httpx.codes.OK, - headers={"content-type": "application/json"}, - json=response_expected_data, - ) - ) - - result = await getattr(async_regeneratable_service, action)("OBJ-0000-0001", input_status) - - assert mock_route.call_count == 1 - request = mock_route.calls[0].request - assert request.content == request_expected_content - assert result.to_dict() == response_expected_data - assert isinstance(result, DummyModel) - - -@pytest.mark.parametrize( - ("action", "input_status"), - [ - ("regenerate", None), - ("submit", None), - ("enquiry", None), - ("accept", None), - ], -) -async def test_async_custom_resource_actions_no_data( - async_regeneratable_service, action, input_status -): - request_expected_content = b"" - response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} - with respx.mock: - mock_route = respx.post( - f"https://api.example.com/public/v1/dummy/regeneratable/OBJ-0000-0001/{action}" - ).mock( - return_value=httpx.Response( - status_code=httpx.codes.OK, - headers={"content-type": "application/json"}, - json=response_expected_data, - ) - ) - - result = await getattr(async_regeneratable_service, action)("OBJ-0000-0001", input_status) - - assert mock_route.call_count == 1 - request = mock_route.calls[0].request - assert request.content == request_expected_content - assert result.to_dict() == response_expected_data - assert isinstance(result, DummyModel) - - -@pytest.mark.parametrize( - ("action", "input_status"), - [ - ("recalculate", {"id": "OBJ-0000-0001", "status": "update"}), - ("accept", {"id": "OBJ-0000-0001", "status": "update"}), - ("queue", {"id": "OBJ-0000-0001", "status": "update"}), - ], -) -def test_recalculate_resource_actions(recalculatable_service, action, input_status): - request_expected_content = b'{"id":"OBJ-0000-0001","status":"update"}' - response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} - with respx.mock: - mock_route = respx.post( - f"https://api.example.com/public/v1/dummy/recalculatable/OBJ-0000-0001/{action}" - ).mock( - return_value=httpx.Response( - status_code=httpx.codes.OK, - headers={"content-type": "application/json"}, - json=response_expected_data, - ) - ) - - result = getattr(recalculatable_service, action)("OBJ-0000-0001", input_status) - - assert mock_route.call_count == 1 - request = mock_route.calls[0].request - assert request.content == request_expected_content - assert result.to_dict() == response_expected_data - assert isinstance(result, DummyModel) - - -@pytest.mark.parametrize( - ("action", "input_status"), - [("recalculate", None), ("accept", None), ("queue", None)], -) -def test_recalculate_resource_actions_no_data(recalculatable_service, action, input_status): - request_expected_content = b"" - response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} - with respx.mock: - mock_route = respx.post( - f"https://api.example.com/public/v1/dummy/recalculatable/OBJ-0000-0001/{action}" - ).mock( - return_value=httpx.Response( - status_code=httpx.codes.OK, - headers={"content-type": "application/json"}, - json=response_expected_data, - ) - ) - - result = getattr(recalculatable_service, action)("OBJ-0000-0001", input_status) - - assert mock_route.call_count == 1 - request = mock_route.calls[0].request - assert request.content == request_expected_content - assert result.to_dict() == response_expected_data - assert isinstance(result, DummyModel) - - -@pytest.mark.parametrize( - ("action", "input_status"), - [ - ("recalculate", {"id": "OBJ-0000-0001", "status": "update"}), - ("accept", {"id": "OBJ-0000-0001", "status": "update"}), - ("queue", {"id": "OBJ-0000-0001", "status": "update"}), - ], -) -async def test_async_recalculate_resource_actions( - async_recalculatable_service, action, input_status -): - request_expected_content = b'{"id":"OBJ-0000-0001","status":"update"}' - response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} - with respx.mock: - mock_route = respx.post( - f"https://api.example.com/public/v1/dummy/recalculatable/OBJ-0000-0001/{action}" - ).mock( - return_value=httpx.Response( - status_code=httpx.codes.OK, - headers={"content-type": "application/json"}, - json=response_expected_data, - ) - ) - - result = await getattr(async_recalculatable_service, action)("OBJ-0000-0001", input_status) - - assert mock_route.call_count == 1 - request = mock_route.calls[0].request - assert request.content == request_expected_content - assert result.to_dict() == response_expected_data - assert isinstance(result, DummyModel) - - -@pytest.mark.parametrize( - ("action", "input_status"), - [("recalculate", None), ("accept", None), ("queue", None)], -) -async def test_async_recalculate_resource_actions_no_data( - async_recalculatable_service, action, input_status -): - request_expected_content = b"" - response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} - with respx.mock: - mock_route = respx.post( - f"https://api.example.com/public/v1/dummy/recalculatable/OBJ-0000-0001/{action}" - ).mock( - return_value=httpx.Response( - status_code=httpx.codes.OK, - headers={"content-type": "application/json"}, - json=response_expected_data, - ) - ) - - result = await getattr(async_recalculatable_service, action)("OBJ-0000-0001", input_status) - - assert mock_route.call_count == 1 - request = mock_route.calls[0].request - assert request.content == request_expected_content - assert result.to_dict() == response_expected_data - assert isinstance(result, DummyModel) - - -@pytest.mark.parametrize( - ("action", "input_status"), - [ - ("issue", {"id": "OBJ-0000-0001", "status": "update"}), - ("cancel", {"id": "OBJ-0000-0001", "status": "update"}), - ("error", {"id": "OBJ-0000-0001", "status": "update"}), - ("pending", {"id": "OBJ-0000-0001", "status": "update"}), - ("queue", {"id": "OBJ-0000-0001", "status": "update"}), - ("retry", {"id": "OBJ-0000-0001", "status": "update"}), - ("recalculate", {"id": "OBJ-0000-0001", "status": "update"}), - ], -) -def test_issuable_resource_actions(issuable_service, action, input_status): - request_expected_content = b'{"id":"OBJ-0000-0001","status":"update"}' - response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} - with respx.mock: - mock_route = respx.post( - f"https://api.example.com/public/v1/dummy/issuable/OBJ-0000-0001/{action}" - ).mock( - return_value=httpx.Response( - status_code=httpx.codes.OK, - headers={"content-type": "application/json"}, - json=response_expected_data, - ) - ) - - result = getattr(issuable_service, action)("OBJ-0000-0001", input_status) - - assert mock_route.call_count == 1 - request = mock_route.calls[0].request - assert request.content == request_expected_content - assert result.to_dict() == response_expected_data - assert isinstance(result, DummyModel) - - -@pytest.mark.parametrize( - ("action", "input_status"), - [ - ("issue", None), - ("cancel", None), - ("error", None), - ("pending", None), - ("queue", None), - ("retry", None), - ("recalculate", None), - ], -) -def test_issuable_resource_actions_no_data(issuable_service, action, input_status): - request_expected_content = b"" - response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} - with respx.mock: - mock_route = respx.post( - f"https://api.example.com/public/v1/dummy/issuable/OBJ-0000-0001/{action}" - ).mock( - return_value=httpx.Response( - status_code=httpx.codes.OK, - headers={"content-type": "application/json"}, - json=response_expected_data, - ) - ) - - result = getattr(issuable_service, action)("OBJ-0000-0001", input_status) - - assert mock_route.call_count == 1 - request = mock_route.calls[0].request - assert request.content == request_expected_content - assert result.to_dict() == response_expected_data - assert isinstance(result, DummyModel) - - -@pytest.mark.parametrize( - ("action", "input_status"), - [ - ("issue", {"id": "OBJ-0000-0001", "status": "update"}), - ("cancel", {"id": "OBJ-0000-0001", "status": "update"}), - ("error", {"id": "OBJ-0000-0001", "status": "update"}), - ("pending", {"id": "OBJ-0000-0001", "status": "update"}), - ("queue", {"id": "OBJ-0000-0001", "status": "update"}), - ("retry", {"id": "OBJ-0000-0001", "status": "update"}), - ("recalculate", {"id": "OBJ-0000-0001", "status": "update"}), - ], -) -async def test_async_issuable_resource_actions(async_issuable_service, action, input_status): - request_expected_content = b'{"id":"OBJ-0000-0001","status":"update"}' - response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} - with respx.mock: - mock_route = respx.post( - f"https://api.example.com/public/v1/dummy/issuable/OBJ-0000-0001/{action}" - ).mock( - return_value=httpx.Response( - status_code=httpx.codes.OK, - headers={"content-type": "application/json"}, - json=response_expected_data, - ) - ) - - result = await getattr(async_issuable_service, action)("OBJ-0000-0001", input_status) - - assert mock_route.call_count == 1 - request = mock_route.calls[0].request - assert request.content == request_expected_content - assert result.to_dict() == response_expected_data - assert isinstance(result, DummyModel) - - -@pytest.mark.parametrize( - ("action", "input_status"), - [ - ("issue", None), - ("cancel", None), - ("error", None), - ("pending", None), - ("queue", None), - ("retry", None), - ("recalculate", None), - ], -) -async def test_async_issuable_resource_actions_no_data( - async_issuable_service, action, input_status -): - request_expected_content = b"" - response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} - with respx.mock: - mock_route = respx.post( - f"https://api.example.com/public/v1/dummy/issuable/OBJ-0000-0001/{action}" - ).mock( - return_value=httpx.Response( - status_code=httpx.codes.OK, - headers={"content-type": "application/json"}, - json=response_expected_data, - ) - ) - - result = await getattr(async_issuable_service, action)("OBJ-0000-0001", input_status) - - assert mock_route.call_count == 1 - request = mock_route.calls[0].request - assert request.content == request_expected_content - assert result.to_dict() == response_expected_data - assert isinstance(result, DummyModel) - - -@pytest.mark.parametrize( - ("action", "input_status"), - [ - ("accept", {"id": "OBJ-0000-0001", "status": "update"}), - ("queue", {"id": "OBJ-0000-0001", "status": "update"}), - ], -) -def test_acceptable_resource_actions(acceptable_service, action, input_status): - request_expected_content = b'{"id":"OBJ-0000-0001","status":"update"}' - response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} - with respx.mock: - mock_route = respx.post( - f"https://api.example.com/public/v1/dummy/acceptable/OBJ-0000-0001/{action}" - ).mock( - return_value=httpx.Response( - status_code=httpx.codes.OK, - headers={"content-type": "application/json"}, - json=response_expected_data, - ) - ) - - result = getattr(acceptable_service, action)("OBJ-0000-0001", input_status) - - assert mock_route.call_count == 1 - request = mock_route.calls[0].request - assert request.content == request_expected_content - assert result.to_dict() == response_expected_data - assert isinstance(result, DummyModel) - - -@pytest.mark.parametrize( - ("action", "input_status"), - [("accept", None), ("queue", None)], -) -def test_acceptable_resource_actions_no_data(acceptable_service, action, input_status): - request_expected_content = b"" - response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} - with respx.mock: - mock_route = respx.post( - f"https://api.example.com/public/v1/dummy/acceptable/OBJ-0000-0001/{action}" - ).mock( - return_value=httpx.Response( - status_code=httpx.codes.OK, - headers={"content-type": "application/json"}, - json=response_expected_data, - ) - ) - - result = getattr(acceptable_service, action)("OBJ-0000-0001", input_status) - - assert mock_route.call_count == 1 - request = mock_route.calls[0].request - assert request.content == request_expected_content - assert result.to_dict() == response_expected_data - assert isinstance(result, DummyModel) - - -@pytest.mark.parametrize( - ("action", "input_status"), - [ - ("accept", {"id": "OBJ-0000-0001", "status": "update"}), - ("queue", {"id": "OBJ-0000-0001", "status": "update"}), - ], -) -async def test_async_acceptable_resource_actions(async_acceptable_service, action, input_status): - request_expected_content = b'{"id":"OBJ-0000-0001","status":"update"}' - response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} - with respx.mock: - mock_route = respx.post( - f"https://api.example.com/public/v1/dummy/acceptable/OBJ-0000-0001/{action}" - ).mock( - return_value=httpx.Response( - status_code=httpx.codes.OK, - headers={"content-type": "application/json"}, - json=response_expected_data, - ) - ) - - result = await getattr(async_acceptable_service, action)("OBJ-0000-0001", input_status) - - assert mock_route.call_count == 1 - request = mock_route.calls[0].request - assert request.content == request_expected_content - assert result.to_dict() == response_expected_data - assert isinstance(result, DummyModel) - - -@pytest.mark.parametrize( - ("action", "input_status"), - [("accept", None), ("queue", None)], -) -async def test_async_acceptable_resource_actions_no_data( - async_acceptable_service, action, input_status -): - request_expected_content = b"" - response_expected_data = {"id": "OBJ-0000-0001", "status": "new_status"} - with respx.mock: - mock_route = respx.post( - f"https://api.example.com/public/v1/dummy/acceptable/OBJ-0000-0001/{action}" - ).mock( - return_value=httpx.Response( - status_code=httpx.codes.OK, - headers={"content-type": "application/json"}, - json=response_expected_data, - ) - ) - - result = await getattr(async_acceptable_service, action)("OBJ-0000-0001", input_status) - - assert mock_route.call_count == 1 - request = mock_route.calls[0].request - assert request.content == request_expected_content - assert result.to_dict() == response_expected_data - assert isinstance(result, DummyModel)