143 lines
5.7 KiB
Python
143 lines
5.7 KiB
Python
from __future__ import annotations
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy.exc import SQLAlchemyError
|
|
from src.application.contracts import ILogger
|
|
from src.application.domain.exceptions import ApplicationException, BadRequestException, DataBaseErrorException, NotFoundException
|
|
from src.application.abstractions.repositories import IUserRepository
|
|
from src.application.domain.entities import UserEntity
|
|
from src.infrastructure.database.models import UserModel
|
|
|
|
|
|
class UserRepository(IUserRepository):
|
|
def __init__(self, session: AsyncSession, logger: ILogger):
|
|
self._session = session
|
|
self._logger = logger
|
|
|
|
async def _get_active_user(self, user_id: str) -> UserModel:
|
|
stmt = (
|
|
select(UserModel)
|
|
.where(
|
|
UserModel.id == user_id,
|
|
UserModel.is_deleted.is_(False),
|
|
)
|
|
)
|
|
result = await self._session.execute(stmt)
|
|
user: UserModel | None = result.scalar_one_or_none()
|
|
if user is None:
|
|
self._logger.warning(f'User not found with user_id {user_id}')
|
|
raise NotFoundException(message='User not found')
|
|
return user
|
|
|
|
@staticmethod
|
|
def _to_entity(user: UserModel) -> UserEntity:
|
|
return UserEntity(
|
|
id=user.id,
|
|
email=user.email,
|
|
password_hash=None,
|
|
first_name=user.first_name,
|
|
middle_name=user.middle_name,
|
|
last_name=user.last_name,
|
|
birth_date=user.birth_date,
|
|
encrypted_mnemonic=user.encrypted_mnemonic,
|
|
phone=user.phone,
|
|
passport_data=user.passport_data,
|
|
inn=user.inn,
|
|
erc20=user.erc20,
|
|
avatar_link=user.avatar_link,
|
|
kyc_verified_at=user.kyc_verified_at,
|
|
kyc_verified=user.kyc_verified,
|
|
is_deleted=user.is_deleted,
|
|
created_at=user.created_at,
|
|
updated_at=user.updated_at,
|
|
)
|
|
|
|
async def get_user_by_id(self, user_id: str) -> UserEntity:
|
|
try:
|
|
user = await self._get_active_user(user_id)
|
|
return self._to_entity(user)
|
|
except ApplicationException:
|
|
raise
|
|
except SQLAlchemyError as exception:
|
|
self._logger.exception(str(exception))
|
|
raise DataBaseErrorException(message=f'Database error: {str(exception)}')
|
|
|
|
async def _update_field(self, user_id: str, **fields: object) -> UserEntity:
|
|
try:
|
|
user = await self._get_active_user(user_id)
|
|
for key, value in fields.items():
|
|
setattr(user, key, value)
|
|
await self._session.flush()
|
|
await self._session.refresh(user)
|
|
return self._to_entity(user)
|
|
except ApplicationException:
|
|
raise
|
|
except SQLAlchemyError as exception:
|
|
self._logger.exception(str(exception))
|
|
raise DataBaseErrorException(message=f'Database error: {str(exception)}')
|
|
|
|
async def set_phone(self, user_id: str, phone: str) -> UserEntity:
|
|
return await self._update_field(user_id, phone=phone)
|
|
|
|
async def set_bank_details(self, user_id: str, **fields: str) -> UserEntity:
|
|
allowed = {'passport_data', 'inn', 'erc20'}
|
|
payload = {k: v for k, v in fields.items() if k in allowed and v is not None}
|
|
if not payload:
|
|
raise BadRequestException(message='No identity fields to update')
|
|
return await self._update_field(user_id, **payload)
|
|
|
|
async def set_encrypted_mnemonic(self, user_id: str, encrypted_mnemonic: str) -> UserEntity:
|
|
return await self._update_field(user_id, encrypted_mnemonic=encrypted_mnemonic)
|
|
|
|
async def set_avatar_link(self, user_id: str, avatar_link: str | None) -> UserEntity:
|
|
return await self._update_field(user_id, avatar_link=avatar_link)
|
|
|
|
async def get_password_hash(self, user_id: str) -> str:
|
|
try:
|
|
user = await self._get_active_user(user_id)
|
|
return user.password_hash
|
|
except ApplicationException:
|
|
raise
|
|
except SQLAlchemyError as exception:
|
|
self._logger.exception(str(exception))
|
|
raise DataBaseErrorException(message=f'Database error: {str(exception)}')
|
|
|
|
async def set_password(self, user_id: str, password_hash: str) -> UserEntity:
|
|
return await self._update_field(user_id, password_hash=password_hash)
|
|
|
|
async def set_email(self, user_id: str, email: str) -> UserEntity:
|
|
return await self._update_field(user_id, email=email)
|
|
|
|
async def email_exists(self, email: str) -> bool:
|
|
try:
|
|
stmt = (
|
|
select(UserModel)
|
|
.where(
|
|
UserModel.email == email,
|
|
UserModel.is_deleted.is_(False),
|
|
)
|
|
)
|
|
result = await self._session.execute(stmt)
|
|
return result.scalar_one_or_none() is not None
|
|
except SQLAlchemyError as exception:
|
|
self._logger.exception(str(exception))
|
|
raise DataBaseErrorException(message=f'Database error: {str(exception)}')
|
|
|
|
async def get_user_by_email(self, email: str) -> UserEntity | None:
|
|
try:
|
|
stmt = (
|
|
select(UserModel)
|
|
.where(
|
|
UserModel.email == email,
|
|
UserModel.is_deleted.is_(False),
|
|
)
|
|
)
|
|
result = await self._session.execute(stmt)
|
|
user: UserModel | None = result.scalar_one_or_none()
|
|
if user is None:
|
|
return None
|
|
return self._to_entity(user)
|
|
except SQLAlchemyError as exception:
|
|
self._logger.exception(str(exception))
|
|
raise DataBaseErrorException(message=f'Database error: {str(exception)}')
|