import logging from http import HTTPStatus from fastapi import FastAPI from fastapi.exceptions import HTTPException, RequestValidationError from fastapi.utils import is_body_allowed_for_status_code from humps import camelize from starlette import status from starlette.requests import Request from starlette.responses import JSONResponse, Response from campaigns.api import errors from campaigns.auth.exceptions import NotAuthenticated, PermissionDenied from campaigns.connectors.facebook.exceptions import FacebookClientError from campaigns.core.exceptions import OwsError, ValidationError logger = logging.getLogger(__name__) async def default_error_handler(request: Request, exc: Exception) -> Response: return JSONResponse( errors.transform( { "code": "internal_error", "message": ( "The server encountered an internal error " "and was unable to complete your request." ), } ), status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, ) async def http_exception_handler(request: Request, exc: HTTPException) -> Response: headers = getattr(exc, "headers", None) if not is_body_allowed_for_status_code(exc.status_code): return Response(status_code=exc.status_code, headers=headers) return JSONResponse( errors.transform( { "code": HTTPStatus(exc.status_code).name.lower(), "message": exc.detail, } ), status_code=exc.status_code, headers=headers, ) async def request_validation_error_handler( request: Request, exc: RequestValidationError ) -> Response: return await validation_error_handler( request, exc=ValidationError.from_pydantic_error(exc, loc_pos=1), ) async def validation_error_handler(request: Request, exc: ValidationError) -> Response: return JSONResponse( errors.transform( { "code": exc.code, "message": exc.message, "fields": { camelize(error.field): { "code": error.code, "message": error.message.capitalize(), } for error in exc.errors }, } ), status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, ) async def ows_error_handler(request: Request, exc: OwsError) -> Response: status_code = exc.context.pop("status_code", status.HTTP_500_INTERNAL_SERVER_ERROR) if status_code >= status.HTTP_500_INTERNAL_SERVER_ERROR: logger.error(exc.message, extra={"code": exc.code}) return JSONResponse( errors.transform( { "code": exc.code, "message": exc.message, } ), status_code=status_code, ) async def not_authenticated_error_handler( request: Request, exc: NotAuthenticated ) -> Response: return JSONResponse( errors.transform( { "code": exc.code, "message": exc.message, } ), status_code=status.HTTP_401_UNAUTHORIZED, ) async def permission_denied_handler( request: Request, exc: PermissionDenied ) -> Response: return JSONResponse( errors.transform( { "code": exc.code, "message": exc.message, } ), status_code=status.HTTP_403_FORBIDDEN, ) async def facebook_client_error_handler( request: Request, exc: FacebookClientError ) -> Response: logger.error( exc.fb_message or exc.fb_error_user_msg or exc.message, extra=exc.get_log_extra(), exc_info=True, ) status_code = exc.context.get("status_code", status.HTTP_400_BAD_REQUEST) return JSONResponse( errors.transform( { "code": exc.code, "message": exc.message, "facebookCode": exc.fb_code, "facebookMessage": exc.fb_message, "facebookErrorMessage": exc.fb_error_user_msg, "facebookErrorTitle": exc.fb_error_user_title, "facebookErrorData": exc.fb_error_data, } ), status_code=status_code, ) def register(app: FastAPI) -> None: app.add_exception_handler(Exception, default_error_handler) app.add_exception_handler(HTTPException, http_exception_handler) app.add_exception_handler(RequestValidationError, request_validation_error_handler) app.add_exception_handler(ValidationError, validation_error_handler) app.add_exception_handler(OwsError, ows_error_handler) app.add_exception_handler(NotAuthenticated, not_authenticated_error_handler) app.add_exception_handler(PermissionDenied, permission_denied_handler) app.add_exception_handler(FacebookClientError, facebook_client_error_handler)