beerds/server/modules/rate/repository/repository.py

82 lines
3.1 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Repository for working with the :class:`~server.modules.rate.repository.models.Vote` model.
Only the “writeonly” operation adding a vote is required in the current
application logic. The repository keeps the database interaction
encapsulated and provides an asynchronous API that can be used by the
service layer (or any other consumer).
The implementation uses the same pattern that is already present in the
``CharactersRepository`` an async session manager from
``server.infra.db.AsyncDB`` and an interface that makes unittesting
straightforward.
"""
from abc import ABCMeta, abstractmethod
from server.infra.db import AsyncDB
from server.modules.rate.repository import models
# --------------------------------------------------------------------------- #
# 1⃣ Base interface
# --------------------------------------------------------------------------- #
class AVoteRepository(metaclass=ABCMeta):
"""
Abstract repository that declares the contract for working with votes.
At the moment only one operation is required inserting a new vote.
"""
@abstractmethod
async def add_vote(self, vote: models.Vote) -> None:
"""Persist ``vote`` into the database."""
raise NotImplementedError
# --------------------------------------------------------------------------- #
# 2⃣ Concrete PostgreSQL implementation
# --------------------------------------------------------------------------- #
class PGVoteRepository(AVoteRepository):
"""
PostgreSQL implementation of :class:`AVoteRepository`.
The repository is intentionally *minimal* only an ``add_vote`` method
is exposed. The rest of the CRUD operations are expected to be added
later if/when the business logic grows.
"""
_db: AsyncDB
def __init__(self, db: AsyncDB):
self._db = db
# --------------------------------------------------------------------- #
# 2.1 Add a vote
# --------------------------------------------------------------------- #
async def add_vote(self, vote: models.Vote) -> None:
"""
Insert ``vote`` into the ``votes`` table.
Parameters
----------
vote:
Instance of :class:`~server.modules.rate.repository.models.Vote`
containing all necessary fields (``id``, ``attachment_id``,
``beerd_id``, ``rate`` and ``created_at``). The instance
is a *mapped* dataclass, therefore SQLAlchemy can persist it
directly.
Notes
-----
* No explicit caching the operation mutates the database and
should never be served from a cache.
* The method is deliberately ``async`` because it uses
``AsyncDB.async_session``.
"""
async with self._db.async_session() as session:
# We use the *instance* directly SQLAlchemy will handle the
# mapping for us. ``insert`` could also be used, but the
# instancebased approach keeps typechecking and IDE
# autocompletion in sync with the dataclass.
session.add(vote)
await session.commit()