"""FastApi error handlers.""" import json import logging from fastapi import status from fastapi.requests import Request from fastapi.responses import JSONResponse, Response from pydantic import ValidationError from delivery_metadata.exceptions import ( NotFoundException, ProductIneligible, SchemaValidationError, UnprocessableException, ) logger = logging.getLogger(__name__) DEFAULT_ERROR_MSG = ( "The server encountered an internal error and was unable to complete your request." ) async def default_error_handler(request: Request, exc: Exception) -> Response: """Handle error when uncaught exception is raised. Default exception handler Args: request: fastapi.Request exc: Exception object with error and trace info Returns: Response: A 500 response with JSON 'code' & 'message' payload. """ logger.exception(exc) return JSONResponse( content={ "code": "internal_error", "message": DEFAULT_ERROR_MSG, }, status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, ) async def upstream_error_handler(request: Request, exc: Exception) -> Response: logger.exception(exc) return JSONResponse(content={}, status_code=status.HTTP_502_BAD_GATEWAY) async def product_ineligible_error_handler( request: Request, exc: ProductIneligible ) -> Response: logger.exception(exc) return JSONResponse( content={"code": "product_ineligible", "message": str(exc)}, status_code=status.HTTP_422_UNPROCESSABLE_CONTENT, ) async def pydantic_validation_error_handler( request: Request, exc: ValidationError ) -> Response: # This ensures that everything in exc.errors(), including ValueError instances, is serializable. errors = json.loads(exc.json()) logger.exception(exc) return JSONResponse( content={ "code": "pydantic_validation_error", "errors": errors, "schema": exc.title, "message": f"{exc.title} missing value", }, status_code=status.HTTP_422_UNPROCESSABLE_CONTENT, ) async def not_found_error_handler(request: Request, exc: NotFoundException) -> Response: """Handle error when NotFoundException exception is raised. Not Found Exception handler Args: request: fastapi.Request exc: Exception object with error and trace info Returns: Response: A 4xx response with JSON 'code' & 'message' payload. """ logger.info(str(exc), extra={"code": status.HTTP_404_NOT_FOUND}) return JSONResponse( content={ "code": exc.__class__.__name__, "message": str(exc), }, status_code=status.HTTP_404_NOT_FOUND, ) async def incompatible_xml_value_error_handler( request: Request, exc: Exception ) -> Response: """Handle error when incompatible XML value is encountered. Incompatible XML Value Exception handler Args: request: fastapi.Request exc: Exception object with error and trace info Returns: Response: A 4xx response with JSON 'code' & 'message' payload. """ logger.exception(str(exc), extra={"code": status.HTTP_422_UNPROCESSABLE_CONTENT}) return JSONResponse( content={ "code": "incompatible_xml_value", "message": str(exc), }, status_code=status.HTTP_422_UNPROCESSABLE_CONTENT, ) async def unprocessable_error_handler( request: Request, exc: UnprocessableException ) -> Response: """Handle error when UnprocessableException exception is raised. Unprocessable Exception handler Args: request: fastapi.Request exc: Exception object with error and trace info Returns: Response: A 4xx response with JSON 'code' & 'message' payload. """ logger.info(str(exc), extra={"code": status.HTTP_422_UNPROCESSABLE_CONTENT}) return JSONResponse( content={ "code": exc.__class__.__name__, "message": str(exc), }, status_code=status.HTTP_422_UNPROCESSABLE_CONTENT, ) async def schema_validation_error_handler( request: Request, exc: SchemaValidationError ) -> Response: """Handle schema validation error. Schema Validation Exception handler Args: request: fastapi.Request exc: Exception object with error and trace info Returns: Response: A 5xx response with JSON 'code' & 'message' payload. """ logger.info(str(exc), extra={"code": status.HTTP_500_INTERNAL_SERVER_ERROR}) message = str(exc) return_xml = request.query_params.get("return_xml") if return_xml and return_xml == "true": message += "\n\n" + exc.xml_content return JSONResponse( content={ "code": exc.__class__.__name__, "message": message, }, status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, )