Flesh out more of the subscription infra and services using CRUD types

This commit is contained in:
Campbell Alden 2026-08-24 00:12:47 +09:00
parent bdf74c2dbd
commit b601a8fbe8
2 changed files with 26 additions and 48 deletions

View file

@ -1,15 +1,17 @@
from dataclasses import asdict
import logging
from src.infra.publication import model_to_order
from typing import TYPE_CHECKING
from datetime import datetime
from src.infra.users import UserModel, model_to_user
from src.infra.users import UserModel
from sqlalchemy.orm import Mapped, mapped_column, relationship, Session, joinedload
from src.infra.db import Base
from sqlalchemy import Engine, ForeignKey, CheckConstraint, DateTime, func, UniqueConstraint, select
from src.infra.db import Base, CRUDRepo
from sqlalchemy import Engine, ForeignKey, CheckConstraint, DateTime, func, UniqueConstraint, select, delete
from src.services.subscription import (
SubscriptionRepo,
Subscription,
SubscriptionId,
UpdateSubscription,
SubscriptionCreateParams,
AvailableSubscriptionEntry,
)
@ -46,16 +48,24 @@ class SubscriptionModel(Base):
def model_to_subscription(db_sub: SubscriptionModel) -> Subscription:
return Subscription(
id=db_sub.id,
user=model_to_user(db_sub.user).to_profile(),
user_id=db_sub.user_id,
publication_order=model_to_order(db_sub.order).to_profile(),
sequence_seen=db_sub.sequence_seen,
start=db_sub.start,
)
class SubscriptionRepoImpl(SubscriptionRepo):
def mutate_subscription(db_sub: SubscriptionModel, sub: Subscription):
pass
class SubscriptionRepoImpl(SubscriptionRepo, CRUDRepo[SubscriptionModel, Subscription, SubscriptionCreateParams]):
def __init__(self, db: Engine):
self._db = db
super().__init__(
db, SubscriptionModel, model_to_subscription, mutate_subscription, lambda u: SubscriptionModel(**asdict(u))
)
self._logger = logging.getLogger('UserRepoImpl')
def get_subscriptions_for_user(self, user_id: int) -> list[Subscription]:
with Session(self._db) as session:
@ -65,7 +75,6 @@ class SubscriptionRepoImpl(SubscriptionRepo):
.where(SubscriptionModel.user_id == user_id)
.options(
joinedload(SubscriptionModel.order),
joinedload(SubscriptionModel.user),
)
)
.unique()
@ -78,21 +87,12 @@ class SubscriptionRepoImpl(SubscriptionRepo):
db_sub = session.get(
SubscriptionModel,
sub_id,
options=[joinedload(SubscriptionModel.order), joinedload(SubscriptionModel.user)],
options=[joinedload(SubscriptionModel.order)],
)
if db_sub:
return model_to_subscription(db_sub)
def create_subscription_for_user(self, user_id: int, order_id: int) -> Subscription:
pass
def delete_subscription(self, subscription_id: SubscriptionId):
pass
def update_subscription(self, update: UpdateSubscription) -> Subscription:
pass
def get_available_entries(self, user_id: int) -> dict[SubscriptionId, list[AvailableSubscriptionEntry]]:
return {}

View file

@ -1,3 +1,4 @@
from src.infra.db import CRUD
import abc
from datetime import datetime
from src.services.publications import (
@ -5,7 +6,6 @@ from src.services.publications import (
Sequence as PublicationSequence,
Publication,
)
from src.services.users.data import UserProfile
from dataclasses import dataclass
SubscriptionId = int
@ -20,7 +20,7 @@ class SubscriptionCreateParams:
@dataclass
class Subscription:
id: SubscriptionId
user: UserProfile
user_id: int
publication_order: PublicationOrderProfile
sequence_seen: int
start: datetime
@ -29,8 +29,6 @@ class Subscription:
@dataclass
class UpdateSubscription:
id: int
user_id: int
publication_order_id: int | None = None
sequence_seen: int | None = None
start: datetime | None = None
@ -43,27 +41,11 @@ class AvailableSubscriptionEntry:
sequence: PublicationSequence
class SubscriptionRepo(abc.ABC):
class SubscriptionRepo(CRUD[Subscription, SubscriptionCreateParams]):
@abc.abstractmethod
def get_subscriptions_for_user(self, user_id: int) -> list[Subscription]:
pass
@abc.abstractmethod
def get_subscription_by_id(self, sub_id: SubscriptionId) -> Subscription | None:
pass
@abc.abstractmethod
def create_subscription_for_user(self, user_id: int, order_id: int) -> Subscription:
pass
@abc.abstractmethod
def delete_subscription(self, subscription_id: SubscriptionId):
pass
@abc.abstractmethod
def update_subscription(self, subscription: UpdateSubscription) -> Subscription:
pass
@abc.abstractmethod
def get_available_entries(self, user_id: int) -> dict[SubscriptionId, list[AvailableSubscriptionEntry]]:
pass
@ -91,15 +73,11 @@ class SubscriptionService:
return self._repo.get_available_entries(user_id)
def create_suscription(self, params: SubscriptionCreateParams) -> Subscription:
return self._repo.create_subscription_for_user(params.user_id, params.order_id)
return self._repo.create(params, None)
def delete_subcription(self, user_id: int, subscription_id: SubscriptionId):
subscription = self._repo.get_subscription_by_id(subscription_id)
if subscription is None:
# TODO Domain error types
raise RuntimeError('not found')
if subscription.user.id != user_id:
# TODO: Domain error types
raise RuntimeError('Cannot delete a subscription for another user')
def verify(subscription: Subscription):
if subscription.user_id != user_id:
raise ValueError('Only the owning user can delete a subscription')
self._repo.delete_subscription(subscription_id)
self._repo.delete(subscription_id, verify)