Files
hotpocket/services/packages/soa/hotpocket_soa/services/associations.py
Tomek Wójcik 8b86145519 BTHLABS-61: Service layer refactoring
A journey to fix `ValidationError` in Pocket imports turned service
layer refactoring :D
2025-10-12 20:54:00 +02:00

175 lines
5.3 KiB
Python

# -*- coding: utf-8 -*-
from __future__ import annotations
import datetime
import http
import uuid
from hotpocket_backend.apps.saves.services import (
AssociationsService as BackendAssociationsService,
)
from hotpocket_soa.dto.associations import (
AssociationOut,
AssociationsQuery,
AssociationUpdateIn,
AssociationWithTargetOut,
)
from hotpocket_soa.dto.saves import SaveOut
from hotpocket_soa.exceptions.backend import NotFound
from .base import ProxyService, SOAError
class AssociationsService(ProxyService):
class AssociationsServiceError(SOAError):
pass
class NotFound(AssociationsServiceError):
pass
class AccessDenied(AssociationsServiceError):
pass
def __init__(self):
super().__init__()
self.backend_associations_service = BackendAssociationsService()
def get_error_class(self) -> type[SOAError]:
return self.AssociationsServiceError
def create(self,
*,
account_uuid: uuid.UUID,
target: SaveOut,
pk: uuid.UUID | None = None,
created_at: datetime.datetime | None = None,
) -> AssociationOut:
return AssociationOut.model_validate(
self.call(
self.backend_associations_service,
'create',
account_uuid=account_uuid,
save_uuid=target.pk,
pk=pk,
created_at=created_at,
),
from_attributes=True,
)
def get(self,
*,
account_uuid: uuid.UUID | None,
pk: uuid.UUID,
with_target: bool = False,
allow_archived: bool = False,
) -> AssociationOut | AssociationWithTargetOut:
try:
model: type[AssociationOut] = AssociationOut
if with_target is True:
model = AssociationWithTargetOut
result = model.model_validate(
self.call(
self.backend_associations_service,
'get',
pk=pk,
with_target=with_target,
),
from_attributes=True,
)
if allow_archived is False and result.archived_at is not None:
raise self.NotFound(
http.HTTPStatus.NOT_FOUND.value, f'pk=`{pk}`',
)
if account_uuid is not None and result.account_uuid != account_uuid:
raise self.AccessDenied(
http.HTTPStatus.FORBIDDEN.value,
f'account_uuid=`{account_uuid}` pk=`{pk}`',
)
return result
except NotFound as exception:
raise self.NotFound.from_backend_error(exception)
def search(self,
*,
query: AssociationsQuery,
limit: int,
) -> list[AssociationWithTargetOut]:
return [
AssociationWithTargetOut.model_validate(row, from_attributes=True)
for row
in self.call(
self.backend_associations_service,
'search',
query=query,
limit=limit,
)
]
def archive(self, *, association: AssociationOut) -> bool:
try:
return self.call(
self.backend_associations_service,
'archive',
pk=association.pk,
)
except NotFound as exception:
raise self.NotFound.from_backend_error(exception)
def star(self, *, association: AssociationOut) -> AssociationOut:
try:
return AssociationOut.model_validate(
self.call(
self.backend_associations_service,
'star',
pk=association.pk,
),
from_attributes=True,
)
except NotFound as exception:
raise self.NotFound.from_backend_error(exception)
def unstar(self, *, association: AssociationOut) -> AssociationOut:
try:
return AssociationOut.model_validate(
self.call(
self.backend_associations_service,
'unstar',
pk=association.pk,
),
from_attributes=True,
)
except NotFound as exception:
raise self.NotFound.from_backend_error(exception)
def update(self,
*,
association: AssociationOut,
update: AssociationUpdateIn,
) -> AssociationOut:
try:
return AssociationOut.model_validate(
self.call(
self.backend_associations_service,
'update',
pk=association.pk,
update=update,
),
from_attributes=True,
)
except NotFound as exception:
raise self.NotFound.from_backend_error(exception)
def delete(self, *, association: AssociationOut) -> bool:
try:
return self.call(
self.backend_associations_service,
'delete',
pk=association.pk,
)
except NotFound as exception:
raise self.NotFound.from_backend_error(exception)