""" Manejadores globales de excepciones para FastAPI """ import logging from typing import Any, Dict from fastapi import Request, status from fastapi.responses import JSONResponse from fastapi.exceptions import RequestValidationError from sqlalchemy.exc import IntegrityError, SQLAlchemyError from .exceptions import BaseAPIException logger = logging.getLogger(__name__) async def base_exception_handler( request: Request, exc: BaseAPIException, ) -> JSONResponse: """ Manejador para todas las excepciones personalizadas de la API """ logger.warning( f"API Exception: {exc.error_code} - {exc.message}", extra={ "path": request.url.path, "method": request.method, "status_code": exc.status_code, }, ) # Log detailed errors if they exist if hasattr(exc, "errors") and exc.errors: logger.warning(f"Validation errors details: {exc.errors}") return JSONResponse( status_code=exc.status_code, content=exc.to_dict(), ) async def validation_exception_handler( request: Request, exc: RequestValidationError, ) -> JSONResponse: """ Manejador para errores de validación de Pydantic/FastAPI """ errors = [] for error in exc.errors(): field = ".".join(str(loc) for loc in error["loc"] if loc != "body") errors.append( { "field": field, "message": error["msg"], "type": error["type"], } ) logger.warning( f"Validation Error en {request.url.path}", extra={"errors": errors}, ) return JSONResponse( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, content={ "error": "VALIDATION_ERROR", "message": "Error de validación en los datos recibidos", "status_code": status.HTTP_422_UNPROCESSABLE_ENTITY, "errors": errors, }, ) async def integrity_error_handler( request: Request, exc: IntegrityError, ) -> JSONResponse: """ Manejador para errores de integridad de la base de datos """ logger.error( f"Database Integrity Error: {str(exc.orig)}", extra={ "path": request.url.path, "method": request.method, }, ) # Intentar extraer información útil del error error_message = "Error de integridad en la base de datos" orig_msg = str(exc.orig).lower() if "unique constraint" in orig_msg or "duplicate key" in orig_msg: error_message = "El registro ya existe. Verifica los campos únicos." elif "foreign key" in orig_msg: error_message = "Referencia inválida a otro registro." elif "not null" in orig_msg: error_message = "Falta un campo requerido." return JSONResponse( status_code=status.HTTP_409_CONFLICT, content={ "error": "DATABASE_INTEGRITY_ERROR", "message": error_message, "status_code": status.HTTP_409_CONFLICT, }, ) async def sqlalchemy_error_handler( request: Request, exc: SQLAlchemyError, ) -> JSONResponse: """ Manejador para errores generales de SQLAlchemy """ logger.error( f"Database Error: {str(exc)}", extra={ "path": request.url.path, "method": request.method, }, exc_info=True, ) return JSONResponse( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content={ "error": "DATABASE_ERROR", "message": "Error en la operación de base de datos", "status_code": status.HTTP_500_INTERNAL_SERVER_ERROR, }, ) async def general_exception_handler( request: Request, exc: Exception, ) -> JSONResponse: """ Manejador para excepciones no capturadas """ logger.error( f"Unhandled Exception: {str(exc)}", extra={ "path": request.url.path, "method": request.method, }, exc_info=True, ) return JSONResponse( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content={ "error": "INTERNAL_SERVER_ERROR", "message": "Error interno del servidor", "status_code": status.HTTP_500_INTERNAL_SERVER_ERROR, }, ) def register_exception_handlers(app) -> None: """ Registra todos los manejadores de excepciones en la aplicación FastAPI Args: app: Instancia de FastAPI """ app.add_exception_handler(BaseAPIException, base_exception_handler) app.add_exception_handler(RequestValidationError, validation_exception_handler) app.add_exception_handler(IntegrityError, integrity_error_handler) app.add_exception_handler(SQLAlchemyError, sqlalchemy_error_handler) app.add_exception_handler(Exception, general_exception_handler) logger.info("Exception handlers registered successfully")