""" User routes for the backend API. Implements the endpoints for user management. """ from fastapi import APIRouter, HTTPException, Depends, Body, Path, Request, Response from typing import List, Dict, Any, Optional from fastapi import status from datetime import datetime import logging import inspect import importlib import os from pydantic import BaseModel # Import interfaces and models import modules.interfaces.serviceManagementClass as serviceManagementClass from modules.security.auth import getCurrentUser, limiter, getCurrentUser # Import the attribute definition and helper functions from modules.interfaces.serviceAppModel import User, AttributeDefinition as ServiceAppAttributeDefinition from modules.shared.attributeUtils import getModelAttributeDefinitions # Configure logger logger = logging.getLogger(__name__) router = APIRouter( prefix="/api/users", tags=["Manage Users"], responses={404: {"description": "Not found"}} ) @router.get("/", response_model=List[User]) @limiter.limit("30/minute") async def get_users( request: Request, currentUser: User = Depends(getCurrentUser) ) -> List[User]: """Get all users in the current mandate""" try: managementInterface = serviceManagementClass.getInterface(currentUser) users = managementInterface.getUsers() return [User.from_dict(user) for user in users] except Exception as e: logger.error(f"Error getting users: {str(e)}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to get users: {str(e)}" ) @router.get("/{userId}", response_model=User) @limiter.limit("30/minute") async def get_user( request: Request, userId: str = Path(..., description="ID of the user"), currentUser: User = Depends(getCurrentUser) ) -> User: """Get a specific user by ID""" try: managementInterface = serviceManagementClass.getInterface(currentUser) user = managementInterface.getUser(userId) if not user: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=f"User with ID {userId} not found" ) return User.from_dict(user) except HTTPException: raise except Exception as e: logger.error(f"Error getting user {userId}: {str(e)}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to get user: {str(e)}" ) @router.post("", response_model=User) @limiter.limit("10/minute") async def create_user( request: Request, user: User, currentUser: User = Depends(getCurrentUser) ) -> User: """Create a new user""" managementInterface = serviceManagementClass.getInterface(currentUser) # Convert User to dict for interface user_data = user.to_dict() # Create user newUser = managementInterface.createUser(user_data) # Set current time for createdAt if it exists in the model if "createdAt" in User.getModelAttributeDefinitions() and hasattr(newUser, "createdAt"): newUser["createdAt"] = datetime.now().isoformat() return User.from_dict(newUser) @router.put("/{userId}", response_model=User) @limiter.limit("10/minute") async def update_user( request: Request, userId: str = Path(..., description="ID of the user to update"), userData: User = Body(...), currentUser: User = Depends(getCurrentUser) ) -> User: """Update an existing user""" managementInterface = serviceManagementClass.getInterface(currentUser) # Check if the user exists existingUser = managementInterface.getUser(userId) if not existingUser: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=f"User with ID {userId} not found" ) # Convert User to dict for interface update_data = userData.to_dict() # Update user updatedUser = managementInterface.updateUser(userId, update_data) if not updatedUser: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Error updating the user" ) return User.from_dict(updatedUser) @router.delete("/{userId}", response_model=Dict[str, Any]) @limiter.limit("10/minute") async def delete_user( request: Request, userId: str = Path(..., description="ID of the user to delete"), currentUser: User = Depends(getCurrentUser) ) -> Dict[str, Any]: """Delete a user""" try: appInterface = serviceManagementClass.getInterface(currentUser) appInterface.deleteUser(userId) return {"message": f"User {userId} deleted successfully"} except ValueError as e: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=str(e) ) except Exception as e: logger.error(f"Error deleting user {userId}: {str(e)}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to delete user: {str(e)}" ) @router.get("/attributes", response_model=List[ServiceAppAttributeDefinition]) @limiter.limit("30/minute") async def get_user_attributes( request: Request, currentUser: User = Depends(getCurrentUser) ) -> List[ServiceAppAttributeDefinition]: """ Retrieves the attribute definitions for users. This can be used for dynamic form generation. Returns: - A list of attribute definitions that can be used to generate forms """ # Get attributes from the User model class return User.getModelAttributeDefinitions()