fast init

This commit is contained in:
ivankirpichnikov
2025-10-16 23:03:50 +03:00
parent b84e0370d6
commit 652da07d12
50 changed files with 1012 additions and 0 deletions
@@ -0,0 +1,17 @@
from abc import abstractmethod
from typing import Protocol
from template_project.application.user.entity import User, UserId
class UserDataGateway(Protocol):
@abstractmethod
async def load_with_id(self, id_: UserId) -> User | None:
raise NotImplementedError
@abstractmethod
async def exists_by_email(
self,
email: str
) -> bool:
raise NotImplementedError
@@ -0,0 +1,27 @@
from datetime import UTC, datetime
from typing import NewType, Self
from uuid import UUID
from uuid_utils.compat import uuid7
from template_project.application.common.entity import Entity, to_entity
UserId = NewType("UserId", UUID)
@to_entity
class User(Entity[UserId]):
email: str
hashed_password: str
@classmethod
def factory(
cls,
email: str,
hashed_password: str,
) -> Self:
return cls(
id=UserId(uuid7()),
email=email,
hashed_password=hashed_password,
created_at=datetime.now(tz=UTC),
)
@@ -0,0 +1,15 @@
from typing import override
from template_project.application.common.errors import ApplicationError, to_error
@to_error
class UserWithEmailAlreadyExistsError(ApplicationError):
email: str
@override
def __str__(self) -> str:
return f"User with the email={self.email!r} already exists"
@to_error
class UserUnauthorizedError(ApplicationError):
pass
@@ -0,0 +1,23 @@
from adaptix.conversion import get_converter
from template_project.application.common.data_structure import to_data_structure
from template_project.application.common.identity_provider import IdentityProvider
from template_project.application.common.interactor import to_interactor
from template_project.application.user.entity import User, UserId
@to_data_structure
class GetMeResponse:
id: UserId
email: str
response_converter = get_converter(User, GetMeResponse)
@to_interactor
class GetMeInteractor:
identity_provider: IdentityProvider
async def execute(self) -> GetMeResponse:
current_user = await self.identity_provider.get_current_user()
return response_converter(current_user)
@@ -0,0 +1,49 @@
from template_project.application.access_token.cryptographer import AccessTokenCryptographer
from template_project.application.access_token.entity_factory import AccessTokenFactory
from template_project.application.common.containers import SecretString
from template_project.application.common.data_structure import to_data_structure
from template_project.application.common.interactor import to_interactor
from template_project.application.common.unit_of_work import UnitOfWork
from template_project.application.user.data_gateway import UserDataGateway
from template_project.application.user.entity import User
from template_project.application.user.errors import UserWithEmailAlreadyExistsError
from template_project.application.user.password_utils import PasswordHasher
@to_data_structure
class UserSignUpResponse:
access_token: str
@to_interactor
class UserSignUpInteractor:
unit_of_work: UnitOfWork
password_hasher: PasswordHasher
user_data_gateway: UserDataGateway
access_token_factory: AccessTokenFactory
access_token_cryptographer: AccessTokenCryptographer
async def execute(
self,
email: str,
password: SecretString,
) -> UserSignUpResponse:
exists_by_email = await self.user_data_gateway.exists_by_email(email)
if exists_by_email:
raise UserWithEmailAlreadyExistsError(email=email)
hashed_password = self.password_hasher.hash(password)
user = User.factory(
email=email,
hashed_password=hashed_password,
)
access_token = self.access_token_factory.execute(user.id)
crypted_access_token = self.access_token_cryptographer.crypto(access_token)
response = UserSignUpResponse(access_token=crypted_access_token)
self.unit_of_work.add(user, access_token)
await self.unit_of_work.commit()
return response
@@ -0,0 +1,20 @@
from abc import abstractmethod
from typing import Protocol
from template_project.application.common.containers import SecretString
class PasswordHasher(Protocol):
@abstractmethod
def hash(self, password: SecretString) -> str:
raise NotImplementedError
class PasswordVerifying(Protocol):
@abstractmethod
def verify(
self,
verifiable_password: SecretString,
hashed_password: str,
) -> bool:
raise NotImplementedError