80 lines
3.6 KiB
Python
80 lines
3.6 KiB
Python
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy.future import select
|
|
from sqlalchemy.orm import selectinload
|
|
from sqlalchemy.exc import SQLAlchemyError, IntegrityError, OperationalError
|
|
from typing import Optional
|
|
import logging
|
|
|
|
from app.models import User as UserModel, UserGroup as UserGroupModel
|
|
from app.schemas.user import UserCreate
|
|
from app.core.security import hash_password
|
|
from app.core.exceptions import (
|
|
UserCreationError,
|
|
EmailAlreadyRegisteredError,
|
|
DatabaseConnectionError,
|
|
DatabaseIntegrityError,
|
|
DatabaseQueryError,
|
|
DatabaseTransactionError,
|
|
UserOperationError
|
|
)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
async def get_user_by_email(db: AsyncSession, email: str) -> Optional[UserModel]:
|
|
"""Fetches a user from the database by email, with common relationships."""
|
|
try:
|
|
stmt = (
|
|
select(UserModel)
|
|
.filter(UserModel.email == email)
|
|
.options(
|
|
selectinload(UserModel.group_associations).selectinload(UserGroupModel.group),
|
|
)
|
|
)
|
|
result = await db.execute(stmt)
|
|
return result.scalars().first()
|
|
except OperationalError as e:
|
|
logger.error(f"Database connection error while fetching user by email '{email}': {str(e)}", exc_info=True)
|
|
raise DatabaseConnectionError(f"Failed to connect to database: {str(e)}")
|
|
except SQLAlchemyError as e:
|
|
logger.error(f"Unexpected SQLAlchemy error while fetching user by email '{email}': {str(e)}", exc_info=True)
|
|
raise DatabaseQueryError(f"Failed to query user: {str(e)}")
|
|
|
|
async def create_user(db: AsyncSession, user_in: UserCreate) -> UserModel:
|
|
"""Creates a new user record in the database with common relationships loaded."""
|
|
try:
|
|
async with db.begin_nested() if db.in_transaction() else db.begin() as transaction:
|
|
_hashed_password = hash_password(user_in.password)
|
|
db_user = UserModel(
|
|
email=user_in.email,
|
|
hashed_password=_hashed_password,
|
|
name=user_in.name
|
|
)
|
|
db.add(db_user)
|
|
await db.flush()
|
|
|
|
stmt = (
|
|
select(UserModel)
|
|
.where(UserModel.id == db_user.id)
|
|
.options(
|
|
selectinload(UserModel.group_associations).selectinload(UserGroupModel.group),
|
|
selectinload(UserModel.created_groups)
|
|
)
|
|
)
|
|
result = await db.execute(stmt)
|
|
loaded_user = result.scalar_one_or_none()
|
|
|
|
if loaded_user is None:
|
|
raise UserOperationError("Failed to load user after creation.")
|
|
|
|
return loaded_user
|
|
except IntegrityError as e:
|
|
logger.error(f"Database integrity error during user creation for email '{user_in.email}': {str(e)}", exc_info=True)
|
|
if "unique constraint" in str(e).lower() and ("users_email_key" in str(e).lower() or "ix_users_email" in str(e).lower()):
|
|
raise EmailAlreadyRegisteredError(email=user_in.email)
|
|
raise DatabaseIntegrityError(f"Failed to create user due to integrity issue: {str(e)}")
|
|
except OperationalError as e:
|
|
logger.error(f"Database connection error during user creation for email '{user_in.email}': {str(e)}", exc_info=True)
|
|
raise DatabaseConnectionError(f"Database connection error during user creation: {str(e)}")
|
|
except SQLAlchemyError as e:
|
|
logger.error(f"Unexpected SQLAlchemy error during user creation for email '{user_in.email}': {str(e)}", exc_info=True)
|
|
raise DatabaseTransactionError(f"Failed to create user due to other DB error: {str(e)}") |