from typing import Any from fastapi import FastAPI from fastapi.openapi.constants import REF_PREFIX from fastapi.openapi.utils import get_openapi from fansifter_common.utils.urlpath import is_path_match_re FIELD_ERROR_SCHEMA = { "title": "FieldError", "type": "object", "properties": { "message": {"title": "Message", "type": "string"}, "code": {"title": "Error code", "type": "string"}, }, "required": ["message", "code"], } VALIDATION_ERROR_RESPONSE_SCHEMA = { "title": "HTTPValidationError", "type": "object", "properties": { "message": {"title": "Message", "type": "string"}, "code": {"title": "Error code", "type": "string"}, "fieldErrors": { "title": "Field errors", "type": "object", "additionalProperties": {"$ref": REF_PREFIX + "FieldError"}, }, }, "required": ["message", "code", "fields"], "example": { "message": "Invalid input", "code": "invalid_input", "fieldErrors": { "name": { "message": "Cannot be blank", "code": "missing", }, "email": { "message": "Not valid email", "code": "invalid_email", }, }, }, } def extend( # noqa: C901 app: FastAPI, *, jwt_auth_enabled: bool = False, jwt_auth_exclude_paths: list[str] | None = None, bearer_auth_scheme_name: str = "bearerAuth", custom_validation_error_schema: bool = True, ) -> None: jwt_auth_exclude_paths = jwt_auth_exclude_paths or [] def _openapi() -> dict[str, Any]: if app.openapi_schema: return app.openapi_schema openapi_schema = get_openapi( title=app.title, version=app.version, openapi_version=app.openapi_version, description=app.description, servers=app.servers, routes=app.routes, ) if custom_validation_error_schema: del openapi_schema["components"]["schemas"]["ValidationError"] openapi_schema["components"]["schemas"].update( { "FieldError": FIELD_ERROR_SCHEMA, "HTTPValidationError": VALIDATION_ERROR_RESPONSE_SCHEMA, } ) if jwt_auth_enabled: if "securitySchemes" not in openapi_schema["components"]: openapi_schema["components"]["securitySchemes"] = {} if ( bearer_auth_scheme_name not in openapi_schema["components"]["securitySchemes"] ): openapi_schema["components"]["securitySchemes"][ bearer_auth_scheme_name ] = { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", } for path, path_data in openapi_schema["paths"].items(): if is_path_match_re(path, match=jwt_auth_exclude_paths): continue for method in path_data.values(): if "security" not in method: method["security"] = [{bearer_auth_scheme_name: []}] app.openapi_schema = openapi_schema return app.openapi_schema app.openapi = _openapi # type: ignore[method-assign] # ty: ignore[invalid-assignment]