104 lines
3.2 KiB
Python
104 lines
3.2 KiB
Python
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from typing import TYPE_CHECKING
|
||
|
|
|
||
|
|
from fastapi import HTTPException
|
||
|
|
from sqlalchemy.exc import SQLAlchemyError
|
||
|
|
|
||
|
|
from core.auth.models import CurrentUser
|
||
|
|
from core.db.base_service import BaseService
|
||
|
|
from core.logging import get_logger
|
||
|
|
from v1.users.repository import UserRepository
|
||
|
|
from v1.users.schemas import UserResponse, UserUpdateRequest
|
||
|
|
|
||
|
|
if TYPE_CHECKING:
|
||
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||
|
|
|
||
|
|
logger = get_logger("v1.users.service")
|
||
|
|
|
||
|
|
|
||
|
|
class UserService(BaseService):
|
||
|
|
"""User service handling business logic and transactions.
|
||
|
|
|
||
|
|
Responsibilities:
|
||
|
|
- Authorization checks
|
||
|
|
- Transaction boundary (commit/rollback)
|
||
|
|
- Converting ORM models to response schemas
|
||
|
|
"""
|
||
|
|
|
||
|
|
_repository: UserRepository
|
||
|
|
_session: AsyncSession
|
||
|
|
|
||
|
|
def __init__(
|
||
|
|
self,
|
||
|
|
repository: UserRepository,
|
||
|
|
session: AsyncSession,
|
||
|
|
current_user: CurrentUser | None,
|
||
|
|
) -> None:
|
||
|
|
super().__init__(current_user=current_user)
|
||
|
|
self._repository = repository
|
||
|
|
self._session = session
|
||
|
|
|
||
|
|
async def get_me(self) -> UserResponse:
|
||
|
|
user_id = self.require_user_id()
|
||
|
|
try:
|
||
|
|
user = await self._repository.get_by_user_id(user_id)
|
||
|
|
except SQLAlchemyError:
|
||
|
|
raise HTTPException(status_code=503, detail="User store unavailable")
|
||
|
|
|
||
|
|
if user is None:
|
||
|
|
raise HTTPException(status_code=404, detail="User not found")
|
||
|
|
return UserResponse(
|
||
|
|
id=str(user.id),
|
||
|
|
username=user.username,
|
||
|
|
avatar_url=user.avatar_url,
|
||
|
|
bio=user.bio,
|
||
|
|
)
|
||
|
|
|
||
|
|
async def update_me(self, update: UserUpdateRequest) -> UserResponse:
|
||
|
|
user_id = self.require_user_id()
|
||
|
|
update_data: dict[str, str | None] = {
|
||
|
|
key: value
|
||
|
|
for key, value in {
|
||
|
|
"username": update.username,
|
||
|
|
"avatar_url": update.avatar_url,
|
||
|
|
"bio": update.bio,
|
||
|
|
}.items()
|
||
|
|
if value is not None
|
||
|
|
}
|
||
|
|
|
||
|
|
if not update_data:
|
||
|
|
raise HTTPException(status_code=400, detail="No fields to update")
|
||
|
|
|
||
|
|
try:
|
||
|
|
user = await self._repository.update_by_user_id(user_id, update_data)
|
||
|
|
await self._session.commit()
|
||
|
|
except SQLAlchemyError:
|
||
|
|
await self._session.rollback()
|
||
|
|
raise HTTPException(status_code=503, detail="User store unavailable")
|
||
|
|
|
||
|
|
if user is None:
|
||
|
|
raise HTTPException(status_code=404, detail="User not found")
|
||
|
|
|
||
|
|
return UserResponse(
|
||
|
|
id=str(user.id),
|
||
|
|
username=user.username,
|
||
|
|
avatar_url=user.avatar_url,
|
||
|
|
bio=user.bio,
|
||
|
|
)
|
||
|
|
|
||
|
|
async def get_by_username(self, username: str) -> UserResponse:
|
||
|
|
try:
|
||
|
|
user = await self._repository.get_by_username(username)
|
||
|
|
except SQLAlchemyError:
|
||
|
|
raise HTTPException(status_code=503, detail="User store unavailable")
|
||
|
|
|
||
|
|
if user is None:
|
||
|
|
raise HTTPException(status_code=404, detail="User not found")
|
||
|
|
return UserResponse(
|
||
|
|
id=str(user.id),
|
||
|
|
username=user.username,
|
||
|
|
avatar_url=user.avatar_url,
|
||
|
|
bio=user.bio,
|
||
|
|
)
|