You've already forked hotpocket
A journey to fix `ValidationError` in Pocket imports turned service layer refactoring :D
175 lines
4.9 KiB
Python
175 lines
4.9 KiB
Python
# -*- coding: utf-8 -*-
|
|
from __future__ import annotations
|
|
|
|
import datetime
|
|
import logging
|
|
import uuid
|
|
|
|
from django.core.exceptions import ValidationError
|
|
from django.db import models
|
|
from django.utils.timezone import now
|
|
|
|
from hotpocket_backend.apps.core.services import get_adapter
|
|
from hotpocket_backend.apps.saves.models import Association, Save
|
|
from hotpocket_backend.apps.saves.types import PAssociationAdapter
|
|
from hotpocket_soa.dto.associations import (
|
|
AssociationsQuery,
|
|
AssociationUpdateIn,
|
|
)
|
|
from hotpocket_soa.exceptions.backend import (
|
|
Invalid as InvalidError,
|
|
NotFound as NotFoundError,
|
|
)
|
|
|
|
from .saves import SavesService
|
|
|
|
LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
class AssociationsService:
|
|
class AssociationsServiceError(Exception):
|
|
pass
|
|
|
|
class Invalid(InvalidError, AssociationsServiceError):
|
|
pass
|
|
|
|
class NotFound(NotFoundError, AssociationsServiceError):
|
|
pass
|
|
|
|
@property
|
|
def adapter(self) -> PAssociationAdapter:
|
|
if hasattr(self, '_adapter') is False:
|
|
adapter_klass = get_adapter(
|
|
'SAVES_ASSOCIATION_ADAPTER',
|
|
'hotpocket_backend.apps.saves.adapters.basic:BasicAssociationAdapter',
|
|
)
|
|
self._adapter = adapter_klass()
|
|
|
|
return self._adapter
|
|
|
|
def create(self,
|
|
*,
|
|
account_uuid: uuid.UUID,
|
|
save_uuid: uuid.UUID,
|
|
pk: uuid.UUID | None = None,
|
|
created_at: datetime.datetime | None = None,
|
|
) -> Association:
|
|
try:
|
|
save = SavesService().get(pk=save_uuid)
|
|
|
|
defaults = dict(
|
|
account_uuid=account_uuid,
|
|
target=save,
|
|
)
|
|
|
|
if pk is not None:
|
|
defaults['id'] = pk
|
|
|
|
result, created = Association.objects.get_or_create(
|
|
account_uuid=account_uuid,
|
|
deleted_at__isnull=True,
|
|
target=save,
|
|
archived_at__isnull=True,
|
|
defaults=defaults,
|
|
)
|
|
|
|
if created is True:
|
|
if created_at is not None:
|
|
result.created_at = created_at
|
|
result.save()
|
|
|
|
return result
|
|
except ValidationError as exception:
|
|
raise self.Invalid.from_django_validation_error(exception)
|
|
|
|
def get(self,
|
|
*,
|
|
pk: uuid.UUID,
|
|
with_target: bool = False,
|
|
) -> Association:
|
|
try:
|
|
query_set = Association.active_objects.\
|
|
filter(
|
|
target__deleted_at__isnull=True,
|
|
)
|
|
|
|
if with_target is True:
|
|
query_set = query_set.select_related('target')
|
|
|
|
return query_set.get(pk=pk)
|
|
except Association.DoesNotExist as exception:
|
|
raise self.NotFound(
|
|
f'Association not found: pk=`{pk}`',
|
|
) from exception
|
|
|
|
def search(self,
|
|
*,
|
|
query: AssociationsQuery,
|
|
offset: int = 0,
|
|
limit: int = 10,
|
|
order_by: str = '-pk',
|
|
) -> models.QuerySet[Save]:
|
|
filters = self.adapter.get_search_filters(query=query)
|
|
|
|
result = Association.active_objects.\
|
|
select_related('target').\
|
|
filter(*filters).\
|
|
order_by(order_by)
|
|
|
|
return result[offset:offset + limit]
|
|
|
|
def update(self,
|
|
*,
|
|
pk: uuid.UUID,
|
|
update: AssociationUpdateIn,
|
|
) -> Association:
|
|
try:
|
|
association = self.get(pk=pk)
|
|
association.target_title = update.target_title
|
|
association.target_description = update.target_description
|
|
|
|
next_target_meta = {
|
|
**(association.target_meta or {}),
|
|
}
|
|
|
|
next_target_meta.pop('title', None)
|
|
next_target_meta.pop('description', None)
|
|
association.target_meta = next_target_meta
|
|
|
|
association.save()
|
|
|
|
return association
|
|
except ValidationError as exception:
|
|
raise self.Invalid.from_django_validation_error(exception)
|
|
|
|
def archive(self, *, pk: uuid.UUID) -> bool:
|
|
association = self.get(pk=pk)
|
|
association.archived_at = now()
|
|
association.save()
|
|
|
|
return True
|
|
|
|
def star(self, *, pk: uuid.UUID) -> Association:
|
|
association = self.get(pk=pk)
|
|
|
|
if association.starred_at is None:
|
|
association.starred_at = now()
|
|
association.save()
|
|
|
|
return association
|
|
|
|
def unstar(self, *, pk: uuid.UUID) -> Association:
|
|
association = self.get(pk=pk)
|
|
|
|
if association.starred_at is not None:
|
|
association.starred_at = None
|
|
association.save()
|
|
|
|
return association
|
|
|
|
def delete(self, *, pk: uuid.UUID) -> bool:
|
|
association = self.get(pk=pk)
|
|
association.soft_delete()
|
|
|
|
return True
|