"""Domain error → HTTP response mapping. This module provides a single exception handler that converts domain-layer errors into structured JSON responses, keeping the service layer free from FastAPI's ``HTTPException``. """ # Import Request from fastapi from fastapi import Request # Import JSONResponse from fastapi.responses from fastapi.responses import JSONResponse # Import from app.domain.errors from app.domain.errors import ( BusinessRuleViolation, DomainError, DuplicateEntityError, EntityNotFoundError, InvalidOperationError, InvalidStateTransition, PermissionViolation, ) # Assign EXCEPTION_STATUS_MAP = { EXCEPTION_STATUS_MAP: dict[type[DomainError], int] = { # Entry: EntityNotFoundError EntityNotFoundError: 404, # Entry: DuplicateEntityError DuplicateEntityError: 409, # Entry: InvalidStateTransition InvalidStateTransition: 400, # Entry: InvalidOperationError InvalidOperationError: 400, # Entry: BusinessRuleViolation BusinessRuleViolation: 400, # Entry: PermissionViolation PermissionViolation: 403, } # Define async function domain_exception_handler async def domain_exception_handler( # Entry: request request: Request, # Entry: exc exc: DomainError, ) -> JSONResponse: """Convert a :class:`DomainError` into a JSON error response.""" # Assign status_code = EXCEPTION_STATUS_MAP.get(type(exc), 400) status_code = EXCEPTION_STATUS_MAP.get(type(exc), 400) # Assign content = {"detail": exc.message, "code": exc.code} content: dict = {"detail": exc.message, "code": exc.code} # Check: isinstance(exc, InvalidStateTransition) if isinstance(exc, InvalidStateTransition): # Assign content["current_state"] = exc.current_state content["current_state"] = exc.current_state # Assign content["target_state"] = exc.target_state content["target_state"] = exc.target_state # Assign content["valid_transitions"] = exc.valid_transitions content["valid_transitions"] = exc.valid_transitions # Return JSONResponse(status_code=status_code, content=content) return JSONResponse(status_code=status_code, content=content)