You've already forked RekomenciBackend
fast init
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user