You've already forked RekomenciBackend
feat(): profiles
This commit is contained in:
@@ -14,6 +14,7 @@ from template_project.application.common.oauth.yandex import (
|
||||
from template_project.application.common.unit_of_work import UnitOfWork
|
||||
from template_project.application.user.entity import User, UserId
|
||||
from template_project.application.user.password_utils import PasswordHasher
|
||||
from template_project.application.user.profile.entity import Profile
|
||||
|
||||
|
||||
@to_data_structure
|
||||
@@ -46,10 +47,7 @@ class SignUpInteractor:
|
||||
|
||||
hashed_password = self.password_hasher.hash(password)
|
||||
|
||||
user = User.factory(
|
||||
email=email,
|
||||
hashed_password=hashed_password,
|
||||
)
|
||||
user = User.factory()
|
||||
|
||||
auth_identity = AuthIdentity.factory(
|
||||
user_id=user.id,
|
||||
@@ -61,7 +59,9 @@ class SignUpInteractor:
|
||||
access_token = self.access_token_factory.execute(user.id)
|
||||
crypted_access_token = self.access_token_cryptographer.crypto(access_token.id)
|
||||
|
||||
for entity in (user, auth_identity, access_token):
|
||||
profile = Profile.factory(user_id=user.id, email=email)
|
||||
|
||||
for entity in (user, auth_identity, access_token, profile):
|
||||
await self.unit_of_work.add(entity)
|
||||
|
||||
await self.unit_of_work.commit()
|
||||
@@ -101,8 +101,11 @@ class SignUpInteractor:
|
||||
secret_key=None,
|
||||
)
|
||||
|
||||
profile = Profile.factory(user_id=user_id)
|
||||
|
||||
await self.unit_of_work.add(user)
|
||||
await self.unit_of_work.add(auth_identity)
|
||||
await self.unit_of_work.add(profile)
|
||||
|
||||
access_token = self.access_token_factory.execute(user_id)
|
||||
crypted_access_token = self.access_token_cryptographer.crypto(access_token.id)
|
||||
|
||||
@@ -11,18 +11,9 @@ UserId = NewType("UserId", UUID)
|
||||
|
||||
@to_entity
|
||||
class User(Entity[UserId]):
|
||||
email: str | None
|
||||
hashed_password: str | None
|
||||
|
||||
@classmethod
|
||||
def factory(
|
||||
cls,
|
||||
email: str | None = None,
|
||||
hashed_password: str | None = None,
|
||||
) -> Self:
|
||||
def factory(cls) -> Self:
|
||||
return cls(
|
||||
id=UserId(uuid7()),
|
||||
email=email,
|
||||
hashed_password=hashed_password,
|
||||
created_at=datetime.now(tz=UTC),
|
||||
)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
from abc import abstractmethod
|
||||
from typing import Protocol
|
||||
|
||||
from template_project.application.user.entity import UserId
|
||||
from template_project.application.user.profile.entity import Profile
|
||||
|
||||
|
||||
class ProfileDataGateway(Protocol):
|
||||
@abstractmethod
|
||||
async def load_by_user_id(self, user_id: UserId) -> Profile | None:
|
||||
raise NotImplementedError
|
||||
@@ -0,0 +1,44 @@
|
||||
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
|
||||
from template_project.application.user.entity import UserId
|
||||
|
||||
ProfileId = NewType("ProfileId", UUID)
|
||||
|
||||
|
||||
@to_entity
|
||||
class Profile(Entity[ProfileId]):
|
||||
user_id: UserId
|
||||
email: str | None
|
||||
display_name: str | None
|
||||
first_name: str | None
|
||||
last_name: str | None
|
||||
avatar_url: str | None
|
||||
phone: str | None
|
||||
|
||||
@classmethod
|
||||
def factory(
|
||||
cls,
|
||||
user_id: UserId,
|
||||
email: str | None = None,
|
||||
display_name: str | None = None,
|
||||
first_name: str | None = None,
|
||||
last_name: str | None = None,
|
||||
avatar_url: str | None = None,
|
||||
phone: str | None = None,
|
||||
) -> Self:
|
||||
return cls(
|
||||
id=ProfileId(uuid7()),
|
||||
user_id=user_id,
|
||||
email=email,
|
||||
display_name=display_name,
|
||||
first_name=first_name,
|
||||
last_name=last_name,
|
||||
avatar_url=avatar_url,
|
||||
phone=phone,
|
||||
created_at=datetime.now(tz=UTC),
|
||||
)
|
||||
@@ -0,0 +1,39 @@
|
||||
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 UserId
|
||||
from template_project.application.user.profile.data_gateway import ProfileDataGateway
|
||||
|
||||
|
||||
@to_data_structure
|
||||
class GetProfileResponse:
|
||||
id: UserId
|
||||
email: str | None
|
||||
display_name: str | None
|
||||
first_name: str | None
|
||||
last_name: str | None
|
||||
avatar_url: str | None
|
||||
phone: str | None
|
||||
|
||||
|
||||
@to_interactor
|
||||
class GetProfileInteractor:
|
||||
identity_provider: IdentityProvider
|
||||
profile_data_gateway: ProfileDataGateway
|
||||
|
||||
async def execute(self) -> GetProfileResponse | None:
|
||||
current_user = await self.identity_provider.get_current_user()
|
||||
profile = await self.profile_data_gateway.load_by_user_id(current_user.id)
|
||||
|
||||
if not profile:
|
||||
return None
|
||||
|
||||
return GetProfileResponse(
|
||||
id=profile.user_id,
|
||||
email=profile.email,
|
||||
display_name=profile.display_name,
|
||||
first_name=profile.first_name,
|
||||
last_name=profile.last_name,
|
||||
avatar_url=profile.avatar_url,
|
||||
phone=profile.phone,
|
||||
)
|
||||
@@ -0,0 +1,70 @@
|
||||
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.common.unit_of_work import UnitOfWork
|
||||
from template_project.application.user.entity import UserId
|
||||
from template_project.application.user.profile.data_gateway import ProfileDataGateway
|
||||
from template_project.application.user.profile.entity import Profile
|
||||
|
||||
|
||||
@to_data_structure
|
||||
class PatchProfileRequest:
|
||||
email: str | None = None
|
||||
display_name: str | None = None
|
||||
first_name: str | None = None
|
||||
last_name: str | None = None
|
||||
avatar_url: str | None = None
|
||||
phone: str | None = None
|
||||
|
||||
|
||||
@to_data_structure
|
||||
class PatchProfileResponse:
|
||||
id: UserId
|
||||
email: str | None
|
||||
display_name: str | None
|
||||
first_name: str | None
|
||||
last_name: str | None
|
||||
avatar_url: str | None
|
||||
phone: str | None
|
||||
|
||||
|
||||
@to_interactor
|
||||
class PatchProfileInteractor:
|
||||
identity_provider: IdentityProvider
|
||||
profile_data_gateway: ProfileDataGateway
|
||||
unit_of_work: UnitOfWork
|
||||
|
||||
async def execute(self, request: PatchProfileRequest) -> PatchProfileResponse:
|
||||
current_user = await self.identity_provider.get_current_user()
|
||||
profile = await self.profile_data_gateway.load_by_user_id(current_user.id)
|
||||
|
||||
if profile:
|
||||
for attr in ("email", "display_name", "first_name", "last_name", "avatar_url", "phone"):
|
||||
value = getattr(request, attr)
|
||||
if value is not None:
|
||||
setattr(profile, attr, value)
|
||||
|
||||
await self.unit_of_work.add(profile)
|
||||
else:
|
||||
profile = Profile.factory(
|
||||
user_id=current_user.id,
|
||||
email=request.email,
|
||||
display_name=request.display_name,
|
||||
first_name=request.first_name,
|
||||
last_name=request.last_name,
|
||||
avatar_url=request.avatar_url,
|
||||
phone=request.phone,
|
||||
)
|
||||
await self.unit_of_work.add(profile)
|
||||
|
||||
await self.unit_of_work.commit()
|
||||
|
||||
return PatchProfileResponse(
|
||||
id=profile.user_id,
|
||||
email=profile.email,
|
||||
display_name=profile.display_name,
|
||||
first_name=profile.first_name,
|
||||
last_name=profile.last_name,
|
||||
avatar_url=profile.avatar_url,
|
||||
phone=profile.phone,
|
||||
)
|
||||
Reference in New Issue
Block a user