Source code for fastapi_icontract.swagger_ui

"""Provide rendering of Swagger UI with the contracts plugin included."""

import json
from typing import Optional, Dict, Any

import fastapi
import fastapi.openapi.docs
from fastapi.encoders import jsonable_encoder
from starlette.requests import Request
from starlette.responses import HTMLResponse


[docs]def get_swagger_ui_html( *, openapi_url: str, title: str, swagger_js_url: str = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@3/swagger-ui-bundle.js", swagger_css_url: str = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@3/swagger-ui.css", swagger_favicon_url: str = "https://fastapi.tiangolo.com/img/favicon.png", swagger_ui_plugin_contracts_url: str = "https://unpkg.com/swagger-ui-plugin-contracts", oauth2_redirect_url: Optional[str] = None, init_oauth: Optional[Dict[str, Any]] = None, ) -> HTMLResponse: """ Generate the HTML for Swagger UI endpoint. This is a patched version of the original fastapi.applications.get_swagger_ui_html which includes a separate JavaScript code to display contracts in a pretty format. """ html = f""" <!DOCTYPE html> <html> <head> <link type="text/css" rel="stylesheet" href="{swagger_css_url}"> <link rel="shortcut icon" href="{swagger_favicon_url}"> <title>{title}</title> </head> <body> <div id="swagger-ui"> </div> <script src="{swagger_js_url}"></script> <!-- `SwaggerUIBundle` is now available on the page --> <script src="{swagger_ui_plugin_contracts_url}"></script> <script> const ui = SwaggerUIBundle({{ url: '{openapi_url}', """ if oauth2_redirect_url: html += f"oauth2RedirectUrl: window.location.origin + '{oauth2_redirect_url}'," html += """ dom_id: '#swagger-ui', presets: [ SwaggerUIBundle.presets.apis, SwaggerUIBundle.SwaggerUIStandalonePreset ], plugins: [ ContractsPlugin ], layout: "BaseLayout", deepLinking: true, showExtensions: true, showCommonExtensions: true })""" if init_oauth: html += f""" ui.initOAuth({json.dumps(jsonable_encoder(init_oauth))}) """ html += """ </script> </body> </html> """ return HTMLResponse(html)
[docs]def set_up_route_for_docs_with_contracts_plugin( app: fastapi.FastAPI, path: str = "/docs" ) -> None: """ Set up the route for Swagger UI with included plugin swagger-ui-plugin-contracts. The path must not be set before. You must explicitly tell FastAPI to exclude it during initialization with: .. code-block:: python app = FastAPI(docs_url=None) """ for route in app.routes: if not isinstance(route, fastapi.routing.APIRoute): continue assert isinstance(route, fastapi.routing.APIRoute) if route.path == path and "GET" in route.methods: raise ValueError( f"The FastAPI app {app} has already the route with the method 'GET' set up for " f"{path!r}. " f"No route with method 'GET' must be set for {path!r} if you want to set up " f"an alternative Swagger UI with contracts plugin." ) if app.openapi_url is None: raise ValueError( f"The FastAPI app {app} has the OpenAPI URL set to None. " f"Swagger UI with contracts plug-in can not be generated " f"if OpenAPI schema is not available." ) # The part below has been adapted from fastapi.applications.FastAPI.setup() async def swagger_ui_html(req: Request) -> HTMLResponse: root_path = req.scope.get("root_path", "").rstrip("/") openapi_url = root_path + app.openapi_url oauth2_redirect_url = app.swagger_ui_oauth2_redirect_url if oauth2_redirect_url: oauth2_redirect_url = root_path + oauth2_redirect_url return get_swagger_ui_html( openapi_url=openapi_url, title=app.title + " - Swagger UI", oauth2_redirect_url=oauth2_redirect_url, init_oauth=app.swagger_ui_init_oauth, ) app.add_route(path, swagger_ui_html, include_in_schema=False) if app.swagger_ui_oauth2_redirect_url: oauth2_already_set_up = False for route in app.routes: if not isinstance(route, fastapi.routing.APIRoute): continue assert isinstance(route, fastapi.routing.APIRoute) if ( route.path == app.swagger_ui_oauth2_redirect_url and "GET" in route.methods ): oauth2_already_set_up = True if not oauth2_already_set_up: # We need to set up the Oauth2 route if it has not been already set since # it will be not automatically set in app.setup(). # # Here is the relevant part of the app.setup() implementation: # # .. code-block:: python # # if self.openapi_url and self.docs_url: # ... # if self.swagger_ui_oauth2_redirect_url: # ... # async def swagger_ui_redirect( req: Request, # pylint: disable=unused-argument ) -> HTMLResponse: return fastapi.openapi.docs.get_swagger_ui_oauth2_redirect_html() app.add_route( app.swagger_ui_oauth2_redirect_url, swagger_ui_redirect, include_in_schema=False, )