scholarr/app/services/domains/users/application.py

98 lines
2.4 KiB
Python

from __future__ import annotations
import re
from sqlalchemy import select
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.asyncio import AsyncSession
from app.db.models import User
EMAIL_PATTERN = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$")
class UserServiceError(ValueError):
"""Raised for expected user-management validation failures."""
def normalize_email(value: str) -> str:
return value.strip().lower()
def validate_email(value: str) -> str:
email = normalize_email(value)
if not EMAIL_PATTERN.fullmatch(email):
raise UserServiceError("Enter a valid email address.")
return email
def validate_password(value: str) -> str:
password = value.strip()
if len(password) < 8:
raise UserServiceError("Password must be at least 8 characters.")
return password
async def get_user_by_id(db_session: AsyncSession, user_id: int) -> User | None:
result = await db_session.execute(select(User).where(User.id == user_id))
return result.scalar_one_or_none()
async def get_user_by_email(db_session: AsyncSession, email: str) -> User | None:
result = await db_session.execute(
select(User).where(User.email == normalize_email(email))
)
return result.scalar_one_or_none()
async def list_users(db_session: AsyncSession) -> list[User]:
result = await db_session.execute(select(User).order_by(User.email.asc()))
return list(result.scalars().all())
async def create_user(
db_session: AsyncSession,
*,
email: str,
password_hash: str,
is_admin: bool,
) -> User:
user = User(
email=validate_email(email),
password_hash=password_hash,
is_admin=is_admin,
is_active=True,
)
db_session.add(user)
try:
await db_session.commit()
except IntegrityError as exc:
await db_session.rollback()
raise UserServiceError("A user with that email already exists.") from exc
await db_session.refresh(user)
return user
async def set_user_active(
db_session: AsyncSession,
*,
user: User,
is_active: bool,
) -> User:
user.is_active = is_active
await db_session.commit()
await db_session.refresh(user)
return user
async def set_user_password_hash(
db_session: AsyncSession,
*,
user: User,
password_hash: str,
) -> User:
user.password_hash = password_hash
await db_session.commit()
await db_session.refresh(user)
return user