hotpocket/services/backend/hotpocket_backend/apps/saves/services/saves.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

116 lines
3.5 KiB
Python

# -*- coding: utf-8 -*-
from __future__ import annotations
import hashlib
import typing
import uuid
from django.core.exceptions import ValidationError
from django.db import models
from hotpocket_backend.apps.core.services import get_adapter
from hotpocket_backend.apps.saves.models import Save
from hotpocket_backend.apps.saves.types import PSaveAdapter
from hotpocket_soa.dto.saves import ImportedSaveIn, SaveIn, SavesQuery
from hotpocket_soa.exceptions.backend import (
Invalid as InvalidError,
NotFound as NotFoundError,
)
class SavesService:
class SavesServiceError(Exception):
pass
class Invalid(InvalidError, SavesServiceError):
pass
class NotFound(NotFoundError, SavesServiceError):
pass
@property
def adapter(self) -> PSaveAdapter:
if hasattr(self, '_adapter') is False:
adapter_klass = get_adapter(
'SAVES_SAVE_ADAPTER',
'hotpocket_backend.apps.saves.adapters.basic:BasicSaveAdapter',
)
self._adapter = adapter_klass()
return self._adapter
def create(self,
*,
account_uuid: uuid.UUID,
save: SaveIn | ImportedSaveIn,
) -> Save:
try:
key = hashlib.sha256(save.url.encode('utf-8')).hexdigest()
defaults = dict(
account_uuid=account_uuid,
key=key,
url=save.url,
)
save_object, created = Save.objects.get_or_create(
key=key,
deleted_at__isnull=True,
defaults=defaults,
)
if created is True:
save_object.is_netloc_banned = save.is_netloc_banned
if isinstance(save, ImportedSaveIn) is True:
save_object.title = save.title # type: ignore[union-attr]
save_object.save()
return save_object
except ValidationError as exception:
raise self.Invalid.from_django_validation_error(exception)
def get(self, *, pk: uuid.UUID) -> Save:
try:
return Save.active_objects.get(pk=pk)
except Save.DoesNotExist as exception:
raise self.NotFound(
f'Save not found: pk=`{pk}`',
) from exception
def search(self,
*,
filters: typing.Any,
offset: int = 0,
limit: int = 10,
order: str = '-created_at',
) -> models.QuerySet[Save]:
raise NotImplementedError('TODO')
def update(self, *, pk: uuid.UUID, save: SaveIn) -> Save:
raise NotImplementedError('TODO')
def delete(self, *, pk: uuid.UUID) -> bool:
raise NotImplementedError('TODO')
def search_associated_to_account(self,
*,
account_uuid: uuid.UUID,
query: SavesQuery,
offset: int = 0,
limit: int = 10,
order_by: str = '-associations__created_at',
) -> models.QuerySet[Save]:
result = Save.active_objects.\
filter(
associations__account_uuid=account_uuid,
associations__archived_at__isnull=True,
).\
order_by(order_by)
return result
def get_for_processing(self, *, pk: uuid.UUID) -> Save:
return self.adapter.get_for_processing(pk=pk)