From bdc416d8245a1894eb7b2c71190e6433edf7b7fb Mon Sep 17 00:00:00 2001 From: Shahriyar Rzayev Date: Sun, 23 Oct 2022 12:29:18 +0400 Subject: [PATCH] feat: add repositories to separate package --- src/jobboard/adapters/db/unit_of_work.py | 7 +-- .../adapters/entrypoints/api/v1/route_jobs.py | 14 +++--- .../entrypoints/api/v1/route_login.py | 6 +-- .../entrypoints/api/v1/route_users.py | 4 +- .../entrypoints/webapps/jobs/route_jobs.py | 12 ++--- .../entrypoints/webapps/users/route_users.py | 4 +- .../repository.py => repositories/jobs.py} | 23 +-------- src/jobboard/adapters/repositories/users.py | 22 +++++++++ src/jobboard/adapters/use_cases/jobs.py | 4 +- src/jobboard/adapters/use_cases/users.py | 4 +- src/jobboard/configurator/containers.py | 8 ++-- .../{repository.py => repositories/jobs.py} | 43 ----------------- .../domain/ports/repositories/users.py | 47 +++++++++++++++++++ src/jobboard/domain/ports/unit_of_work.py | 7 +-- src/jobboard/domain/ports/use_cases/jobs.py | 2 +- src/jobboard/domain/ports/use_cases/users.py | 2 +- tests/fake_container.py | 8 ++-- tests/utils/users.py | 4 +- 18 files changed, 115 insertions(+), 106 deletions(-) rename src/jobboard/adapters/{db/repository.py => repositories/jobs.py} (55%) create mode 100644 src/jobboard/adapters/repositories/users.py rename src/jobboard/domain/ports/{repository.py => repositories/jobs.py} (57%) create mode 100644 src/jobboard/domain/ports/repositories/users.py diff --git a/src/jobboard/adapters/db/unit_of_work.py b/src/jobboard/adapters/db/unit_of_work.py index 1b5ee39..d9bebe6 100644 --- a/src/jobboard/adapters/db/unit_of_work.py +++ b/src/jobboard/adapters/db/unit_of_work.py @@ -2,7 +2,8 @@ from sqlalchemy.orm.session import Session -from src.jobboard.adapters.db import repository +from src.jobboard.adapters.repositories.users import UserSqlAlchemyRepository +from src.jobboard.adapters.repositories.jobs import JobSqlAlchemyRepository from src.jobboard.domain.ports.unit_of_work import ( JobUnitOfWorkInterface, UserUnitOfWorkInterface, @@ -15,7 +16,7 @@ def __init__(self, session_factory: Callable[[], Any]): def __enter__(self): self.session = self.session_factory() # type: Session - self.users = repository.UserSqlAlchemyRepository(self.session) + self.users = UserSqlAlchemyRepository(self.session) return super().__enter__() def __exit__(self, *args): @@ -35,7 +36,7 @@ def __init__(self, session_factory: Callable[[], Any]): def __enter__(self): self.session = self.session_factory() # type: Session - self.jobs = repository.JobSqlAlchemyRepository(self.session) + self.jobs = JobSqlAlchemyRepository(self.session) return super().__enter__() def __exit__(self, *args): diff --git a/src/jobboard/adapters/entrypoints/api/v1/route_jobs.py b/src/jobboard/adapters/entrypoints/api/v1/route_jobs.py index 3e15d81..f83dd92 100644 --- a/src/jobboard/adapters/entrypoints/api/v1/route_jobs.py +++ b/src/jobboard/adapters/entrypoints/api/v1/route_jobs.py @@ -13,7 +13,7 @@ from src.jobboard.configurator.containers import Container from src.jobboard.domain.model.model import User from src.jobboard.domain.ports.common.responses import ResponseTypes -from src.jobboard.domain.ports.use_cases.jobs import JobsServiceInterface +from src.jobboard.domain.ports.use_cases.jobs import JobServiceInterface from src.jobboard.domain.schemas.jobs import JobCreateInputDto router = APIRouter() @@ -25,7 +25,7 @@ def create_job( job: JobCreateInputDto, current_user: User = Depends(get_current_user_from_token), - job_service: JobsServiceInterface = Depends(Provide[Container.job_service]), + job_service: JobServiceInterface = Depends(Provide[Container.job_service]), ): response = job_service.create(job=job, owner_id=current_user.id) data = jsonable_encoder(response.value) @@ -40,7 +40,7 @@ def create_job( @inject def read_job( id: int, - job_service: JobsServiceInterface = Depends(Provide[Container.job_service]), + job_service: JobServiceInterface = Depends(Provide[Container.job_service]), ): response = job_service.retrieve_job(id_=id) data = jsonable_encoder(response.value) @@ -54,7 +54,7 @@ def read_job( @router.get("/all") @inject def read_jobs( - job_service: JobsServiceInterface = Depends(Provide[Container.job_service]), + job_service: JobServiceInterface = Depends(Provide[Container.job_service]), ): response = job_service.list_jobs() data = jsonable_encoder(response.value) @@ -71,7 +71,7 @@ def update_job( id: int, job: JobCreateInputDto, current_user: User = Depends(get_current_user_from_token), - job_service: JobsServiceInterface = Depends(Provide[Container.job_service]), + job_service: JobServiceInterface = Depends(Provide[Container.job_service]), ): response = job_service.update_job_by_id(id_=id, job=job, owner_id=current_user.id) data = jsonable_encoder(response.value) @@ -87,7 +87,7 @@ def update_job( def delete_job( id: int, current_user: User = Depends(get_current_user_from_token), - job_service: JobsServiceInterface = Depends(Provide[Container.job_service]), + job_service: JobServiceInterface = Depends(Provide[Container.job_service]), ): response = job_service.retrieve_job(id_=id) if response.type != ResponseTypes.SUCCESS: @@ -112,7 +112,7 @@ def delete_job( @inject def autocomplete( term: Optional[str] = None, - job_service: JobsServiceInterface = Depends(Provide[Container.job_service]), + job_service: JobServiceInterface = Depends(Provide[Container.job_service]), ): jobs = job_service.search_job(term) return [job.title for job in jobs] diff --git a/src/jobboard/adapters/entrypoints/api/v1/route_login.py b/src/jobboard/adapters/entrypoints/api/v1/route_login.py index 7438415..4f06d3b 100644 --- a/src/jobboard/adapters/entrypoints/api/v1/route_login.py +++ b/src/jobboard/adapters/entrypoints/api/v1/route_login.py @@ -9,7 +9,7 @@ from src.jobboard.configurator.config import settings from src.jobboard.configurator.containers import Container from src.jobboard.configurator.security import create_access_token -from src.jobboard.domain.ports.use_cases.users import UsersServiceInterface +from src.jobboard.domain.ports.use_cases.users import UserServiceInterface from src.jobboard.domain.schemas.tokens import Token from src.jobboard.domain.schemas.users import UserLoginInputDto, UserOutputDto @@ -21,7 +21,7 @@ def login_for_access_token( response: Response, form_data: OAuth2PasswordRequestForm = Depends(), - user_service: UsersServiceInterface = Depends(Provide[Container.user_service]), + user_service: UserServiceInterface = Depends(Provide[Container.user_service]), ): user = UserLoginInputDto(email=form_data.username, password=form_data.password) user = user_service.authenticate_user(user) @@ -46,7 +46,7 @@ def login_for_access_token( @inject def get_current_user_from_token( token: str = Depends(oauth2_scheme), - user_service: UsersServiceInterface = Depends(Provide[Container.user_service]), + user_service: UserServiceInterface = Depends(Provide[Container.user_service]), ): credentials_exception = HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, diff --git a/src/jobboard/adapters/entrypoints/api/v1/route_users.py b/src/jobboard/adapters/entrypoints/api/v1/route_users.py index 676f237..c1cc230 100644 --- a/src/jobboard/adapters/entrypoints/api/v1/route_users.py +++ b/src/jobboard/adapters/entrypoints/api/v1/route_users.py @@ -6,7 +6,7 @@ from src.jobboard.adapters.entrypoints import STATUS_CODES from src.jobboard.configurator.containers import Container -from src.jobboard.domain.ports.use_cases.users import UsersServiceInterface +from src.jobboard.domain.ports.use_cases.users import UserServiceInterface from src.jobboard.domain.schemas.users import UserCreateInputDto router = APIRouter() @@ -16,7 +16,7 @@ @inject def create_user( user: UserCreateInputDto, - user_service: UsersServiceInterface = Depends(Provide[Container.user_service]), + user_service: UserServiceInterface = Depends(Provide[Container.user_service]), ): response = user_service.create(user=user) data = jsonable_encoder(response.value) diff --git a/src/jobboard/adapters/entrypoints/webapps/jobs/route_jobs.py b/src/jobboard/adapters/entrypoints/webapps/jobs/route_jobs.py index 4f6481d..0e4f72b 100644 --- a/src/jobboard/adapters/entrypoints/webapps/jobs/route_jobs.py +++ b/src/jobboard/adapters/entrypoints/webapps/jobs/route_jobs.py @@ -11,7 +11,7 @@ from src.jobboard.adapters.entrypoints.webapps.jobs.forms import JobCreateForm from src.jobboard.configurator.containers import Container from src.jobboard.domain.model.model import User -from src.jobboard.domain.ports.use_cases.jobs import JobsServiceInterface +from src.jobboard.domain.ports.use_cases.jobs import JobServiceInterface from src.jobboard.domain.schemas.jobs import JobCreateInputDto templates = Jinja2Templates(directory="src/jobboard/adapters/entrypoints/templates") @@ -22,7 +22,7 @@ @inject async def home( request: Request, - job_service: JobsServiceInterface = Depends(Provide[Container.job_service]), + job_service: JobServiceInterface = Depends(Provide[Container.job_service]), msg: Optional[str] = None, ): jobs = job_service.list_jobs() @@ -36,7 +36,7 @@ async def home( def job_detail( id: int, request: Request, - job_service: JobsServiceInterface = Depends(Provide[Container.job_service]), + job_service: JobServiceInterface = Depends(Provide[Container.job_service]), ): job = job_service.retrieve_job(id_=id) return templates.TemplateResponse( @@ -53,7 +53,7 @@ def create_job(request: Request): @inject async def create_job( request: Request, - job_service: JobsServiceInterface = Depends(Provide[Container.job_service]), + job_service: JobServiceInterface = Depends(Provide[Container.job_service]), ): form = JobCreateForm(request) await form.load_data() @@ -82,7 +82,7 @@ async def create_job( @inject def show_jobs_to_delete( request: Request, - job_service: JobsServiceInterface = Depends(Provide[Container.job_service]), + job_service: JobServiceInterface = Depends(Provide[Container.job_service]), ): jobs = job_service.list_jobs() return templates.TemplateResponse( @@ -94,7 +94,7 @@ def show_jobs_to_delete( @inject def search( request: Request, - job_service: JobsServiceInterface = Depends(Provide[Container.job_service]), + job_service: JobServiceInterface = Depends(Provide[Container.job_service]), query: Optional[str] = None, ): jobs = job_service.search_job(query) diff --git a/src/jobboard/adapters/entrypoints/webapps/users/route_users.py b/src/jobboard/adapters/entrypoints/webapps/users/route_users.py index 0c23671..f059ae3 100644 --- a/src/jobboard/adapters/entrypoints/webapps/users/route_users.py +++ b/src/jobboard/adapters/entrypoints/webapps/users/route_users.py @@ -5,7 +5,7 @@ from src.jobboard.adapters.entrypoints.webapps.users.forms import UserCreateForm from src.jobboard.configurator.containers import Container -from src.jobboard.domain.ports.use_cases.users import UsersServiceInterface +from src.jobboard.domain.ports.use_cases.users import UserServiceInterface from src.jobboard.domain.schemas.users import UserCreateInputDto templates = Jinja2Templates(directory="src/jobboard/adapters/entrypoints/templates") @@ -21,7 +21,7 @@ def register(request: Request): @inject async def register( request: Request, - user_service: UsersServiceInterface = Depends(Provide[Container.user_service]), + user_service: UserServiceInterface = Depends(Provide[Container.user_service]), ): form = UserCreateForm(request) await form.load_data() diff --git a/src/jobboard/adapters/db/repository.py b/src/jobboard/adapters/repositories/jobs.py similarity index 55% rename from src/jobboard/adapters/db/repository.py rename to src/jobboard/adapters/repositories/jobs.py index 798f51b..04e7fcb 100644 --- a/src/jobboard/adapters/db/repository.py +++ b/src/jobboard/adapters/repositories/jobs.py @@ -1,28 +1,9 @@ from src.jobboard.domain.model import model -from src.jobboard.domain.ports.repository import ( +from src.jobboard.domain.ports.repositories.jobs import ( JobRepositoryInterface, - UserRepositoryInterface, ) -class UserSqlAlchemyRepository(UserRepositoryInterface): - def __init__(self, session): - super().__init__() - self.session = session - - def _add(self, user): - self.session.add(user) - - def _get(self, user_name: str): - return self.session.query(model.User).filter_by(user_name=user_name).first() - - def _get_by_email(self, email: str) -> model.User: - return self.session.query(model.User).filter_by(email=email).first() - - def _get_by_id(self, id: int) -> model.User: - return self.session.query(model.User).filter_by(id=id).first() - - class JobSqlAlchemyRepository(JobRepositoryInterface): def __init__(self, session): super().__init__() @@ -44,4 +25,4 @@ def _get_all(self) -> list[model.Job]: return self.session.query(model.Job).all() def _search(self, query: str) -> list[model.Job]: - return self.session.query(model.Job).filter(model.Job.title.contains(query)) + return self.session.query(model.Job).filter(model.Job.title.contains(query)) \ No newline at end of file diff --git a/src/jobboard/adapters/repositories/users.py b/src/jobboard/adapters/repositories/users.py new file mode 100644 index 0000000..cb6ff0c --- /dev/null +++ b/src/jobboard/adapters/repositories/users.py @@ -0,0 +1,22 @@ +from src.jobboard.domain.model import model +from src.jobboard.domain.ports.repositories.users import ( + UserRepositoryInterface, +) + + +class UserSqlAlchemyRepository(UserRepositoryInterface): + def __init__(self, session): + super().__init__() + self.session = session + + def _add(self, user): + self.session.add(user) + + def _get(self, user_name: str): + return self.session.query(model.User).filter_by(user_name=user_name).first() + + def _get_by_email(self, email: str) -> model.User: + return self.session.query(model.User).filter_by(email=email).first() + + def _get_by_id(self, id: int) -> model.User: + return self.session.query(model.User).filter_by(id=id).first() \ No newline at end of file diff --git a/src/jobboard/adapters/use_cases/jobs.py b/src/jobboard/adapters/use_cases/jobs.py index fcfa9d9..1929e06 100644 --- a/src/jobboard/adapters/use_cases/jobs.py +++ b/src/jobboard/adapters/use_cases/jobs.py @@ -7,7 +7,7 @@ ResponseTypes, ) from src.jobboard.domain.ports.unit_of_work import JobUnitOfWorkInterface -from src.jobboard.domain.ports.use_cases.jobs import JobsServiceInterface +from src.jobboard.domain.ports.use_cases.jobs import JobServiceInterface from src.jobboard.domain.schemas.jobs import JobCreateInputDto, JobOutputDto @@ -27,7 +27,7 @@ def _handle_response_failure( ) -class JobsService(JobsServiceInterface): +class JobService(JobServiceInterface): def __init__(self, uow: JobUnitOfWorkInterface): self.uow = uow diff --git a/src/jobboard/adapters/use_cases/users.py b/src/jobboard/adapters/use_cases/users.py index 1419423..1b2a141 100644 --- a/src/jobboard/adapters/use_cases/users.py +++ b/src/jobboard/adapters/use_cases/users.py @@ -8,7 +8,7 @@ ResponseTypes, ) from src.jobboard.domain.ports.unit_of_work import UserUnitOfWorkInterface -from src.jobboard.domain.ports.use_cases.users import UsersServiceInterface +from src.jobboard.domain.ports.use_cases.users import UserServiceInterface from src.jobboard.domain.schemas.users import ( UserCreateInputDto, UserLoginInputDto, @@ -16,7 +16,7 @@ ) -class UsersService(UsersServiceInterface): +class UserService(UserServiceInterface): def __init__(self, uow: UserUnitOfWorkInterface): self.uow = uow diff --git a/src/jobboard/configurator/containers.py b/src/jobboard/configurator/containers.py index 801b278..4450810 100644 --- a/src/jobboard/configurator/containers.py +++ b/src/jobboard/configurator/containers.py @@ -6,8 +6,8 @@ JobSqlAlchemyUnitOfWork, UserSqlAlchemyUnitOfWork, ) -from src.jobboard.adapters.use_cases.jobs import JobsService -from src.jobboard.adapters.use_cases.users import UsersService +from src.jobboard.adapters.use_cases.jobs import JobService +from src.jobboard.adapters.use_cases.users import UserService from src.jobboard.configurator import config @@ -35,8 +35,8 @@ class Container(containers.DeclarativeContainer): ) user_service = providers.Factory( - UsersService, + UserService, uow=user_uow, ) - job_service = providers.Factory(JobsService, uow=job_uow) + job_service = providers.Factory(JobService, uow=job_uow) diff --git a/src/jobboard/domain/ports/repository.py b/src/jobboard/domain/ports/repositories/jobs.py similarity index 57% rename from src/jobboard/domain/ports/repository.py rename to src/jobboard/domain/ports/repositories/jobs.py index 45abe2b..4ee59df 100644 --- a/src/jobboard/domain/ports/repository.py +++ b/src/jobboard/domain/ports/repositories/jobs.py @@ -4,49 +4,6 @@ from src.jobboard.domain.model import model -class UserRepositoryInterface(abc.ABC): - def __init__(self): - self.seen = set() # type: Set[model.User] - - def add(self, user: model.User): - self._add(user) - self.seen.add(user) - - def get(self, user_name: str) -> model.User: - user = self._get(user_name) - if user: - self.seen.add(user) - return user - - def get_by_email(self, email: str) -> model.User: - user = self._get_by_email(email) - if user: - self.seen.add(user) - return user - - def get_by_id(self, id: int) -> model.User: - user = self._get_by_id(id) - if user: - self.seen.add(user) - return user - - @abc.abstractmethod - def _add(self, user: model.User): - raise NotImplementedError - - @abc.abstractmethod - def _get(self, user_name: str) -> model.User: - raise NotImplementedError - - @abc.abstractmethod - def _get_by_email(self, email: str) -> model.User: - raise NotImplementedError - - @abc.abstractmethod - def _get_by_id(self, id: int) -> model.User: - raise NotImplementedError - - class JobRepositoryInterface(abc.ABC): def __init__(self): self.seen = set() # type: Set[model.Job] diff --git a/src/jobboard/domain/ports/repositories/users.py b/src/jobboard/domain/ports/repositories/users.py new file mode 100644 index 0000000..21fc5c9 --- /dev/null +++ b/src/jobboard/domain/ports/repositories/users.py @@ -0,0 +1,47 @@ +import abc +from typing import Set + +from src.jobboard.domain.model import model + + +class UserRepositoryInterface(abc.ABC): + def __init__(self): + self.seen = set() # type: Set[model.User] + + def add(self, user: model.User): + self._add(user) + self.seen.add(user) + + def get(self, user_name: str) -> model.User: + user = self._get(user_name) + if user: + self.seen.add(user) + return user + + def get_by_email(self, email: str) -> model.User: + user = self._get_by_email(email) + if user: + self.seen.add(user) + return user + + def get_by_id(self, id: int) -> model.User: + user = self._get_by_id(id) + if user: + self.seen.add(user) + return user + + @abc.abstractmethod + def _add(self, user: model.User): + raise NotImplementedError + + @abc.abstractmethod + def _get(self, user_name: str) -> model.User: + raise NotImplementedError + + @abc.abstractmethod + def _get_by_email(self, email: str) -> model.User: + raise NotImplementedError + + @abc.abstractmethod + def _get_by_id(self, id: int) -> model.User: + raise NotImplementedError diff --git a/src/jobboard/domain/ports/unit_of_work.py b/src/jobboard/domain/ports/unit_of_work.py index fe43e97..e91f920 100644 --- a/src/jobboard/domain/ports/unit_of_work.py +++ b/src/jobboard/domain/ports/unit_of_work.py @@ -1,11 +1,12 @@ import abc -from src.jobboard.domain.ports import repository +from src.jobboard.domain.ports.repositories.users import UserRepositoryInterface +from src.jobboard.domain.ports.repositories.jobs import JobRepositoryInterface from src.jobboard.domain.ports.common import messagebus class UserUnitOfWorkInterface(abc.ABC): - users: repository.UserRepositoryInterface + users: UserRepositoryInterface def __enter__(self) -> "UserUnitOfWorkInterface": return self @@ -33,7 +34,7 @@ def rollback(self): class JobUnitOfWorkInterface(abc.ABC): - jobs: repository.JobRepositoryInterface + jobs: JobRepositoryInterface def __enter__(self) -> "JobUnitOfWorkInterface": return self diff --git a/src/jobboard/domain/ports/use_cases/jobs.py b/src/jobboard/domain/ports/use_cases/jobs.py index bfa9ab1..ce03b91 100644 --- a/src/jobboard/domain/ports/use_cases/jobs.py +++ b/src/jobboard/domain/ports/use_cases/jobs.py @@ -6,7 +6,7 @@ from src.jobboard.domain.schemas.jobs import JobCreateInputDto -class JobsServiceInterface(abc.ABC): +class JobServiceInterface(abc.ABC): @abc.abstractmethod def __init__(self, uow: JobUnitOfWorkInterface): self.uow = uow diff --git a/src/jobboard/domain/ports/use_cases/users.py b/src/jobboard/domain/ports/use_cases/users.py index 52699a3..5175acf 100644 --- a/src/jobboard/domain/ports/use_cases/users.py +++ b/src/jobboard/domain/ports/use_cases/users.py @@ -10,7 +10,7 @@ ) -class UsersServiceInterface(abc.ABC): +class UserServiceInterface(abc.ABC): @abc.abstractmethod def __init__(self, uow: UserUnitOfWorkInterface): self.uow = uow diff --git a/tests/fake_container.py b/tests/fake_container.py index 2e2b1f4..073cd7a 100644 --- a/tests/fake_container.py +++ b/tests/fake_container.py @@ -6,8 +6,8 @@ JobSqlAlchemyUnitOfWork, UserSqlAlchemyUnitOfWork, ) -from src.jobboard.adapters.use_cases.jobs import JobsService -from src.jobboard.adapters.use_cases.users import UsersService +from src.jobboard.adapters.use_cases.jobs import JobService +from src.jobboard.adapters.use_cases.users import UserService class Container(containers.DeclarativeContainer): @@ -35,8 +35,8 @@ class Container(containers.DeclarativeContainer): ) fake_user_service = providers.Factory( - UsersService, + UserService, uow=user_uow, ) - fake_job_service = providers.Factory(JobsService, uow=job_uow) + fake_job_service = providers.Factory(JobService, uow=job_uow) diff --git a/tests/utils/users.py b/tests/utils/users.py index c02e2df..f38b7a6 100644 --- a/tests/utils/users.py +++ b/tests/utils/users.py @@ -4,7 +4,7 @@ from src.jobboard.configurator.hashing import Hasher from src.jobboard.domain.model.model import user_model_factory -from src.jobboard.domain.ports.use_cases.users import UsersServiceInterface +from src.jobboard.domain.ports.use_cases.users import UserServiceInterface from src.jobboard.domain.schemas.users import UserCreateInputDto from tests.fake_container import Container @@ -27,7 +27,7 @@ def user_authentication_headers( def authentication_token_from_email( client: TestClient, email: str, - user_service: UsersServiceInterface = Depends(Provide[Container.fake_user_service]), + user_service: UserServiceInterface = Depends(Provide[Container.fake_user_service]), ): """ Return a valid token for the user with given email.