"""User management router (admin only).""" # Import uuid import uuid # Import APIRouter, Depends, status from fastapi from fastapi import APIRouter, Depends, status # Import Session from sqlalchemy.orm from sqlalchemy.orm import Session # Import get_db from app.database from app.database import get_db # Import require_role from app.dependencies.auth from app.dependencies.auth import require_role # Import UnitOfWork from app.domain.unit_of_work from app.domain.unit_of_work import UnitOfWork # Import User from app.models.user from app.models.user import User from app.dependencies.auth import get_current_user from app.schemas.user import UserCreate, UserUpdate, UserOut, UserPreferencesUpdate from app.services.audit_service import log_action # Import from app.services.user_service from app.services.user_service import ( create_user, get_user_or_raise, list_users, update_user, ) # Assign router = APIRouter(prefix="/users", tags=["users"]) router = APIRouter(prefix="/users", tags=["users"]) # --------------------------------------------------------------------------- # PATCH /users/me/preferences — update current user preferences # --------------------------------------------------------------------------- @router.patch("/me/preferences", response_model=UserOut) def update_my_preferences( payload: UserPreferencesUpdate, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): """Update the current user's notification preferences, Jira account ID and Jira API token. Send ``jira_api_token: ""`` to clear a previously stored token. The token is never returned in any response. """ update_data = payload.model_dump(exclude_unset=True) for field, value in update_data.items(): if field in ("jira_api_token", "jira_email", "tempo_api_token"): # Empty string means "clear the value" setattr(current_user, field, value if value else None) else: setattr(current_user, field, value) db.commit() db.refresh(current_user) return current_user # --------------------------------------------------------------------------- # GET /users/me — get current user's own profile # --------------------------------------------------------------------------- @router.get("/me", response_model=UserOut) def get_me( current_user: User = Depends(get_current_user), ): """Return the currently authenticated user's profile.""" return current_user # --------------------------------------------------------------------------- # GET /users — list all users # --------------------------------------------------------------------------- @router.get("", response_model=list[UserOut]) # Define function list_users_route def list_users_route( # Entry: db db: Session = Depends(get_db), # Entry: current_user current_user: User = Depends(require_role("admin")), ) -> list[UserOut]: """Return a list of all users. **Requires admin role.**.""" # Return list_users(db) return list_users(db) # --------------------------------------------------------------------------- # POST /users — create a new user # --------------------------------------------------------------------------- @router.post("", response_model=UserOut, status_code=status.HTTP_201_CREATED) # Define function create_user_route def create_user_route( # Entry: payload payload: UserCreate, # Entry: db db: Session = Depends(get_db), # Entry: current_user current_user: User = Depends(require_role("admin")), ) -> UserOut: """Create a new user. **Requires admin role.**.""" # Open context manager with UnitOfWork(db) as uow: # Assign user = create_user( user = create_user( db, # Keyword argument: username username=payload.username, # Keyword argument: email email=payload.email, # Keyword argument: password password=payload.password, # Keyword argument: role role=payload.role, ) # Call log_action() log_action( db, # Keyword argument: user_id user_id=current_user.id, # Keyword argument: action action="create_user", # Keyword argument: entity_type entity_type="user", # Keyword argument: entity_id entity_id=user.id, # Keyword argument: details details={"username": user.username, "role": user.role}, ) # Call uow.commit() uow.commit() # Reload ORM object attributes from the database db.refresh(user) # Return user return user # --------------------------------------------------------------------------- # GET /users/{id} — get a single user # --------------------------------------------------------------------------- @router.get("/{user_id}", response_model=UserOut) # Define function get_user def get_user( # Entry: user_id user_id: uuid.UUID, # Entry: db db: Session = Depends(get_db), # Entry: current_user current_user: User = Depends(require_role("admin")), ) -> UserOut: """Return a single user by ID. **Requires admin role.**.""" # Return get_user_or_raise(db, user_id) return get_user_or_raise(db, user_id) # --------------------------------------------------------------------------- # PATCH /users/{id} — update a user # --------------------------------------------------------------------------- @router.patch("/{user_id}", response_model=UserOut) # Define function update_user_route def update_user_route( # Entry: user_id user_id: uuid.UUID, # Entry: payload payload: UserUpdate, # Entry: db db: Session = Depends(get_db), # Entry: current_user current_user: User = Depends(require_role("admin")), ) -> UserOut: """Update one or more fields of an existing user. **Requires admin role.**.""" # Assign update_data = payload.model_dump(exclude_unset=True) update_data = payload.model_dump(exclude_unset=True) # Open context manager with UnitOfWork(db) as uow: # Assign user = update_user(db, user_id, **update_data) user = update_user(db, user_id, **update_data) # Call log_action() log_action( db, # Keyword argument: user_id user_id=current_user.id, # Keyword argument: action action="update_user", # Keyword argument: entity_type entity_type="user", # Keyword argument: entity_id entity_id=user.id, # Keyword argument: details details={"updated_fields": list(update_data.keys())}, ) # Call uow.commit() uow.commit() # Reload ORM object attributes from the database db.refresh(user) # Return user return user