feat: add user endpoints
This commit is contained in:
@@ -5,6 +5,7 @@ from src.infrastructure.database.models.admin_session import AdminSessionModel
|
||||
from src.infrastructure.database.models.legal_entity import LegalEntityModel
|
||||
from src.infrastructure.database.models.organization_wallet import OrganizationWalletModel
|
||||
from src.infrastructure.database.models.purchase_request import PurchaseRequestModel
|
||||
from src.infrastructure.database.models.wallets import WalletModel
|
||||
|
||||
__all__ = [
|
||||
'Base',
|
||||
@@ -14,4 +15,5 @@ __all__ = [
|
||||
'LegalEntityModel',
|
||||
'OrganizationWalletModel',
|
||||
'PurchaseRequestModel',
|
||||
'WalletModel',
|
||||
]
|
||||
|
||||
28
src/infrastructure/database/models/wallets.py
Normal file
28
src/infrastructure/database/models/wallets.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import DateTime, ForeignKey, String, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from src.infrastructure.database.models.base import Base
|
||||
from src.infrastructure.database.models.mixins import UlidPrimaryKeyMixin
|
||||
|
||||
|
||||
class WalletModel(Base, UlidPrimaryKeyMixin):
|
||||
__tablename__ = 'wallets'
|
||||
|
||||
user_id: Mapped[str] = mapped_column(
|
||||
String(26),
|
||||
ForeignKey('users.id', ondelete='RESTRICT'),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
chain: Mapped[str] = mapped_column(String(16), nullable=False)
|
||||
address: Mapped[str] = mapped_column(String(128), nullable=False, index=True)
|
||||
derivation_path: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True),
|
||||
nullable=False,
|
||||
server_default=func.now(),
|
||||
)
|
||||
@@ -4,6 +4,7 @@ from src.infrastructure.database.repositories.user_repository import UserReposit
|
||||
from src.infrastructure.database.repositories.legal_entity_repository import LegalEntityRepository
|
||||
from src.infrastructure.database.repositories.organization_wallet_repository import OrganizationWalletRepository
|
||||
from src.infrastructure.database.repositories.purchase_request_repository import PurchaseRequestRepository
|
||||
from src.infrastructure.database.repositories.wallets_repository import WalletRepository
|
||||
|
||||
__all__ = [
|
||||
'AdminUserRepository',
|
||||
@@ -12,4 +13,5 @@ __all__ = [
|
||||
'LegalEntityRepository',
|
||||
'OrganizationWalletRepository',
|
||||
'PurchaseRequestRepository',
|
||||
'WalletRepository',
|
||||
]
|
||||
|
||||
@@ -45,6 +45,16 @@ class UserRepository(IUserRepository):
|
||||
provisioned_by=user.provisioned_by,
|
||||
provisioned_at=user.provisioned_at,
|
||||
)
|
||||
|
||||
async def get_by_id(self, user_id: str) -> UserEntity:
|
||||
try:
|
||||
user = await self._get_user(user_id)
|
||||
return self._to_entity(user)
|
||||
except ApplicationException:
|
||||
raise
|
||||
except SQLAlchemyError as exception:
|
||||
self._logger.exception(str(exception))
|
||||
raise InternalServerException(message=f'Database error: {str(exception)}')
|
||||
|
||||
async def create_legal_entity_user(
|
||||
self,
|
||||
@@ -104,9 +114,8 @@ class UserRepository(IUserRepository):
|
||||
self._logger.exception(str(exception))
|
||||
raise InternalServerException(message=f'Database error: {str(exception)}')
|
||||
|
||||
async def set_password(self, user_email: str, password_hash: str) -> UserEntity:
|
||||
user = await self.get_user_by_email(user_email)
|
||||
return await self._update_field(user.id, password_hash=password_hash)
|
||||
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 get_user_by_email(self, email: str) -> UserEntity:
|
||||
try:
|
||||
@@ -126,6 +135,41 @@ class UserRepository(IUserRepository):
|
||||
stmt = select(UserModel.id).where(UserModel.email == email, UserModel.is_deleted.is_(False)).limit(1)
|
||||
result = await self._session.execute(stmt)
|
||||
return result.scalar_one_or_none() is not None
|
||||
|
||||
def _search_filter(self, search: str | None):
|
||||
if not search or not search.strip():
|
||||
return None
|
||||
|
||||
pattern = f'%{search.strip()}%'
|
||||
return or_(
|
||||
UserModel.id.ilike(pattern),
|
||||
UserModel.email.ilike(pattern),
|
||||
UserModel.last_name.ilike(pattern),
|
||||
UserModel.first_name.ilike(pattern),
|
||||
UserModel.middle_name.ilike(pattern),
|
||||
UserModel.phone.ilike(pattern),
|
||||
UserModel.inn.ilike(pattern),
|
||||
)
|
||||
|
||||
async def list_all(
|
||||
self,
|
||||
*,
|
||||
limit: int,
|
||||
offset: int,
|
||||
search: str | None = None,
|
||||
) -> list[UserEntity]:
|
||||
stmt = (
|
||||
select(UserModel)
|
||||
.where(UserModel.is_deleted.is_(False))
|
||||
.order_by(UserModel.created_at.desc())
|
||||
.limit(limit)
|
||||
.offset(offset)
|
||||
)
|
||||
search_filter = self._search_filter(search)
|
||||
if search_filter is not None:
|
||||
stmt = stmt.where(search_filter)
|
||||
res = await self._session.execute(stmt)
|
||||
return [self._to_entity(m) for m in res.scalars().all()]
|
||||
|
||||
def _individual_search_filter(self, query: str):
|
||||
pattern = f'%{query.strip()}%'
|
||||
@@ -192,3 +236,14 @@ class UserRepository(IUserRepository):
|
||||
)
|
||||
result = await self._session.execute(stmt)
|
||||
return int(result.scalar_one())
|
||||
|
||||
async def count_all(self, *, search: str | None = None) -> int:
|
||||
stmt = (
|
||||
select(func.count(UserModel.id))
|
||||
.where(UserModel.is_deleted.is_(False))
|
||||
)
|
||||
search_filter = self._search_filter(search)
|
||||
if search_filter is not None:
|
||||
stmt = stmt.where(search_filter)
|
||||
res = await self._session.execute(stmt)
|
||||
return int(res.scalar_one())
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.exc import IntegrityError, SQLAlchemyError
|
||||
|
||||
from src.application.abstractions.repositories import IWalletRepository
|
||||
from src.application.contracts import ILogger
|
||||
from src.application.domain.entities.user import WalletEntity
|
||||
from src.application.domain.exceptions import ApplicationException
|
||||
from src.infrastructure.database.models import WalletModel
|
||||
|
||||
|
||||
class WalletRepository(IWalletRepository):
|
||||
def __init__(self, session: AsyncSession, logger: ILogger):
|
||||
self._session = session
|
||||
self._logger = logger
|
||||
|
||||
def _to_entity(self, m: WalletModel) -> WalletEntity:
|
||||
return WalletEntity(
|
||||
id=m.id,
|
||||
user_id=m.user_id,
|
||||
chain=m.chain,
|
||||
address=m.address,
|
||||
derivation_path=m.derivation_path,
|
||||
created_at=m.created_at,
|
||||
)
|
||||
|
||||
async def list_by_user(self, user_id: str) -> list[WalletEntity]:
|
||||
res = await self._session.execute(
|
||||
select(WalletModel)
|
||||
.where(WalletModel.user_id == user_id)
|
||||
.order_by(WalletModel.chain)
|
||||
)
|
||||
return [self._to_entity(m) for m in res.scalars().all()]
|
||||
|
||||
async def exists_for_user(self, user_id: str) -> bool:
|
||||
res = await self._session.execute(
|
||||
select(WalletModel.id)
|
||||
.where(WalletModel.user_id == user_id)
|
||||
.limit(1)
|
||||
)
|
||||
return res.scalar_one_or_none() is not None
|
||||
@@ -8,6 +8,7 @@ from src.application.abstractions.repositories import (
|
||||
IOrganizationWalletRepository,
|
||||
IPurchaseRequestRepository,
|
||||
IUserRepository,
|
||||
IWalletRepository,
|
||||
)
|
||||
from src.application.contracts import ILogger
|
||||
from src.application.domain.exceptions import RefreshConcurrentException
|
||||
@@ -18,6 +19,7 @@ from src.infrastructure.database.repositories import (
|
||||
OrganizationWalletRepository,
|
||||
PurchaseRequestRepository,
|
||||
UserRepository,
|
||||
WalletRepository,
|
||||
)
|
||||
|
||||
|
||||
@@ -31,6 +33,7 @@ class UnitOfWork(IUnitOfWork):
|
||||
self._legal_entity_repository: ILegalEntityRepository | None = None
|
||||
self._organization_wallet_repository: IOrganizationWalletRepository | None = None
|
||||
self._purchase_request_repository: IPurchaseRequestRepository | None = None
|
||||
self._wallet_repository: IWalletRepository | None = None
|
||||
self._logger: ILogger = logger
|
||||
|
||||
async def __aenter__(self):
|
||||
@@ -84,6 +87,14 @@ class UnitOfWork(IUnitOfWork):
|
||||
session=self._session, logger=self._logger
|
||||
)
|
||||
return self._organization_wallet_repository
|
||||
|
||||
@property
|
||||
def wallet_repository(self) -> IWalletRepository:
|
||||
if self._wallet_repository is None:
|
||||
self._wallet_repository = WalletRepository(
|
||||
session=self._session, logger=self._logger
|
||||
)
|
||||
return self._wallet_repository
|
||||
|
||||
@property
|
||||
def purchase_request_repository(self) -> IPurchaseRequestRepository:
|
||||
|
||||
Reference in New Issue
Block a user