Files
users/src/infrastructure/database/repositories/user_repository.py

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)}')