|
| 1 | +# SPDX-FileCopyrightText: 2023-2024 MTS PJSC |
| 2 | +# SPDX-License-Identifier: Apache-2.0 |
| 3 | +from functools import partial |
| 4 | + |
| 5 | +from fastapi import FastAPI |
| 6 | +from fastapi.openapi.docs import ( |
| 7 | + get_redoc_html, |
| 8 | + get_swagger_ui_html, |
| 9 | + get_swagger_ui_oauth2_redirect_html, |
| 10 | +) |
| 11 | +from fastapi.openapi.utils import get_openapi |
| 12 | +from starlette.requests import Request |
| 13 | +from starlette.responses import JSONResponse |
| 14 | + |
| 15 | +from syncmaster.settings.server.openapi import OpenAPISettings |
| 16 | + |
| 17 | + |
| 18 | +async def custom_openapi(request: Request) -> JSONResponse: |
| 19 | + app: FastAPI = request.app |
| 20 | + root_path = request.scope.get("root_path", "").rstrip("/") |
| 21 | + server_urls = set(filter(None, (server_data.get("url") for server_data in app.servers))) |
| 22 | + |
| 23 | + if root_path not in server_urls: |
| 24 | + if root_path and app.root_path_in_servers: |
| 25 | + app.servers.insert(0, {"url": root_path}) |
| 26 | + server_urls.add(root_path) |
| 27 | + |
| 28 | + return JSONResponse(app.openapi()) |
| 29 | + |
| 30 | + |
| 31 | +def custom_openapi_schema(app: FastAPI, settings: OpenAPISettings) -> dict: |
| 32 | + if app.openapi_schema: |
| 33 | + return app.openapi_schema |
| 34 | + |
| 35 | + openapi_schema = get_openapi( |
| 36 | + title=app.title, |
| 37 | + version=app.version, |
| 38 | + openapi_version=app.openapi_version, |
| 39 | + summary=app.summary, |
| 40 | + description=app.description, |
| 41 | + terms_of_service=app.terms_of_service, |
| 42 | + contact=app.contact, |
| 43 | + license_info=app.license_info, |
| 44 | + routes=app.routes, |
| 45 | + webhooks=app.webhooks.routes, |
| 46 | + tags=app.openapi_tags, |
| 47 | + servers=app.servers, |
| 48 | + separate_input_output_schemas=app.separate_input_output_schemas, |
| 49 | + ) |
| 50 | + # https://redocly.com/docs/api-reference-docs/specification-extensions/x-logo/ |
| 51 | + openapi_schema["info"]["x-logo"] = { |
| 52 | + "url": str(settings.logo.url), |
| 53 | + "altText": str(settings.logo.alt_text), |
| 54 | + "backgroundColor": f"#{settings.logo.background_color}", # noqa: WPS237 |
| 55 | + "href": str(settings.logo.href), |
| 56 | + } |
| 57 | + app.openapi_schema = openapi_schema |
| 58 | + return app.openapi_schema |
| 59 | + |
| 60 | + |
| 61 | +async def custom_swagger_ui_html(request: Request): |
| 62 | + app: FastAPI = request.app |
| 63 | + settings: OpenAPISettings = app.state.settings.server.openapi |
| 64 | + root_path = request.scope.get("root_path", "").rstrip("/") |
| 65 | + openapi_url = root_path + request.app.openapi_url # type: ignore[arg-type] |
| 66 | + return get_swagger_ui_html( |
| 67 | + openapi_url=openapi_url, |
| 68 | + title=f"{app.title} - Swagger UI", |
| 69 | + oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url, |
| 70 | + swagger_js_url=str(settings.swagger.js_url), |
| 71 | + swagger_css_url=str(settings.swagger.css_url), |
| 72 | + swagger_favicon_url=str(settings.favicon.url), |
| 73 | + ) |
| 74 | + |
| 75 | + |
| 76 | +async def custom_swagger_ui_redirect(request: Request): |
| 77 | + return get_swagger_ui_oauth2_redirect_html() |
| 78 | + |
| 79 | + |
| 80 | +async def custom_redoc_html(request: Request): |
| 81 | + app: FastAPI = request.app |
| 82 | + settings: OpenAPISettings = app.state.settings.server.openapi |
| 83 | + root_path = request.scope.get("root_path", "").rstrip("/") |
| 84 | + openapi_url = root_path + request.app.openapi_url # type: ignore[arg-type] |
| 85 | + return get_redoc_html( |
| 86 | + openapi_url=openapi_url, |
| 87 | + title=f"{app.title} - ReDoc", |
| 88 | + redoc_js_url=settings.redoc.js_url, |
| 89 | + redoc_favicon_url=settings.favicon.url, |
| 90 | + with_google_fonts=False, |
| 91 | + ) |
| 92 | + |
| 93 | + |
| 94 | +def apply_openapi_middleware(app: FastAPI, settings: OpenAPISettings) -> FastAPI: |
| 95 | + """Add OpenAPI middleware to the application.""" |
| 96 | + # https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/#include-the-custom-docs |
| 97 | + if settings.enabled: |
| 98 | + app.openapi_url = "/openapi.json" |
| 99 | + app.add_route(app.openapi_url, custom_openapi, include_in_schema=False) |
| 100 | + |
| 101 | + if settings.swagger.enabled: |
| 102 | + app.docs_url = "/docs" |
| 103 | + app.swagger_ui_oauth2_redirect_url = "/docs/oauth2-redirect" |
| 104 | + app.add_route(app.docs_url, custom_swagger_ui_html, include_in_schema=False) |
| 105 | + app.add_route(app.swagger_ui_oauth2_redirect_url, custom_swagger_ui_redirect, include_in_schema=False) |
| 106 | + |
| 107 | + if settings.redoc.enabled: |
| 108 | + app.redoc_url = "/redoc" |
| 109 | + app.add_route(app.redoc_url, custom_redoc_html, include_in_schema=False) |
| 110 | + |
| 111 | + app.openapi = partial(custom_openapi_schema, app=app, settings=settings) # type: ignore[method-assign] |
| 112 | + return app |
0 commit comments