345 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			345 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
import json
 | 
						|
from typing import Any, Dict, Optional
 | 
						|
 | 
						|
from fastapi.encoders import jsonable_encoder
 | 
						|
from starlette.responses import HTMLResponse
 | 
						|
from typing_extensions import Annotated, Doc
 | 
						|
 | 
						|
swagger_ui_default_parameters: Annotated[
 | 
						|
    Dict[str, Any],
 | 
						|
    Doc(
 | 
						|
        """
 | 
						|
        Default configurations for Swagger UI.
 | 
						|
 | 
						|
        You can use it as a template to add any other configurations needed.
 | 
						|
        """
 | 
						|
    ),
 | 
						|
] = {
 | 
						|
    "dom_id": "#swagger-ui",
 | 
						|
    "layout": "BaseLayout",
 | 
						|
    "deepLinking": True,
 | 
						|
    "showExtensions": True,
 | 
						|
    "showCommonExtensions": True,
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
def get_swagger_ui_html(
 | 
						|
    *,
 | 
						|
    openapi_url: Annotated[
 | 
						|
        str,
 | 
						|
        Doc(
 | 
						|
            """
 | 
						|
            The OpenAPI URL that Swagger UI should load and use.
 | 
						|
 | 
						|
            This is normally done automatically by FastAPI using the default URL
 | 
						|
            `/openapi.json`.
 | 
						|
            """
 | 
						|
        ),
 | 
						|
    ],
 | 
						|
    title: Annotated[
 | 
						|
        str,
 | 
						|
        Doc(
 | 
						|
            """
 | 
						|
            The HTML `<title>` content, normally shown in the browser tab.
 | 
						|
            """
 | 
						|
        ),
 | 
						|
    ],
 | 
						|
    swagger_js_url: Annotated[
 | 
						|
        str,
 | 
						|
        Doc(
 | 
						|
            """
 | 
						|
            The URL to use to load the Swagger UI JavaScript.
 | 
						|
 | 
						|
            It is normally set to a CDN URL.
 | 
						|
            """
 | 
						|
        ),
 | 
						|
    ] = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5.9.0/swagger-ui-bundle.js",
 | 
						|
    swagger_css_url: Annotated[
 | 
						|
        str,
 | 
						|
        Doc(
 | 
						|
            """
 | 
						|
            The URL to use to load the Swagger UI CSS.
 | 
						|
 | 
						|
            It is normally set to a CDN URL.
 | 
						|
            """
 | 
						|
        ),
 | 
						|
    ] = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5.9.0/swagger-ui.css",
 | 
						|
    swagger_favicon_url: Annotated[
 | 
						|
        str,
 | 
						|
        Doc(
 | 
						|
            """
 | 
						|
            The URL of the favicon to use. It is normally shown in the browser tab.
 | 
						|
            """
 | 
						|
        ),
 | 
						|
    ] = "https://fastapi.tiangolo.com/img/favicon.png",
 | 
						|
    oauth2_redirect_url: Annotated[
 | 
						|
        Optional[str],
 | 
						|
        Doc(
 | 
						|
            """
 | 
						|
            The OAuth2 redirect URL, it is normally automatically handled by FastAPI.
 | 
						|
            """
 | 
						|
        ),
 | 
						|
    ] = None,
 | 
						|
    init_oauth: Annotated[
 | 
						|
        Optional[Dict[str, Any]],
 | 
						|
        Doc(
 | 
						|
            """
 | 
						|
            A dictionary with Swagger UI OAuth2 initialization configurations.
 | 
						|
            """
 | 
						|
        ),
 | 
						|
    ] = None,
 | 
						|
    swagger_ui_parameters: Annotated[
 | 
						|
        Optional[Dict[str, Any]],
 | 
						|
        Doc(
 | 
						|
            """
 | 
						|
            Configuration parameters for Swagger UI.
 | 
						|
 | 
						|
            It defaults to [swagger_ui_default_parameters][fastapi.openapi.docs.swagger_ui_default_parameters].
 | 
						|
            """
 | 
						|
        ),
 | 
						|
    ] = None,
 | 
						|
) -> HTMLResponse:
 | 
						|
    """
 | 
						|
    Generate and return the HTML  that loads Swagger UI for the interactive
 | 
						|
    API docs (normally served at `/docs`).
 | 
						|
 | 
						|
    You would only call this function yourself if you needed to override some parts,
 | 
						|
    for example the URLs to use to load Swagger UI's JavaScript and CSS.
 | 
						|
 | 
						|
    Read more about it in the
 | 
						|
    [FastAPI docs for Configure Swagger UI](https://fastapi.tiangolo.com/how-to/configure-swagger-ui/)
 | 
						|
    and the [FastAPI docs for Custom Docs UI Static Assets (Self-Hosting)](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/).
 | 
						|
    """
 | 
						|
    current_swagger_ui_parameters = swagger_ui_default_parameters.copy()
 | 
						|
    if swagger_ui_parameters:
 | 
						|
        current_swagger_ui_parameters.update(swagger_ui_parameters)
 | 
						|
 | 
						|
    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>
 | 
						|
    const ui = SwaggerUIBundle({{
 | 
						|
        url: '{openapi_url}',
 | 
						|
    """
 | 
						|
 | 
						|
    for key, value in current_swagger_ui_parameters.items():
 | 
						|
        html += f"{json.dumps(key)}: {json.dumps(jsonable_encoder(value))},\n"
 | 
						|
 | 
						|
    if oauth2_redirect_url:
 | 
						|
        html += f"oauth2RedirectUrl: window.location.origin + '{oauth2_redirect_url}',"
 | 
						|
 | 
						|
    html += """
 | 
						|
    presets: [
 | 
						|
        SwaggerUIBundle.presets.apis,
 | 
						|
        SwaggerUIBundle.SwaggerUIStandalonePreset
 | 
						|
        ],
 | 
						|
    })"""
 | 
						|
 | 
						|
    if init_oauth:
 | 
						|
        html += f"""
 | 
						|
        ui.initOAuth({json.dumps(jsonable_encoder(init_oauth))})
 | 
						|
        """
 | 
						|
 | 
						|
    html += """
 | 
						|
    </script>
 | 
						|
    </body>
 | 
						|
    </html>
 | 
						|
    """
 | 
						|
    return HTMLResponse(html)
 | 
						|
 | 
						|
 | 
						|
def get_redoc_html(
 | 
						|
    *,
 | 
						|
    openapi_url: Annotated[
 | 
						|
        str,
 | 
						|
        Doc(
 | 
						|
            """
 | 
						|
            The OpenAPI URL that ReDoc should load and use.
 | 
						|
 | 
						|
            This is normally done automatically by FastAPI using the default URL
 | 
						|
            `/openapi.json`.
 | 
						|
            """
 | 
						|
        ),
 | 
						|
    ],
 | 
						|
    title: Annotated[
 | 
						|
        str,
 | 
						|
        Doc(
 | 
						|
            """
 | 
						|
            The HTML `<title>` content, normally shown in the browser tab.
 | 
						|
            """
 | 
						|
        ),
 | 
						|
    ],
 | 
						|
    redoc_js_url: Annotated[
 | 
						|
        str,
 | 
						|
        Doc(
 | 
						|
            """
 | 
						|
            The URL to use to load the ReDoc JavaScript.
 | 
						|
 | 
						|
            It is normally set to a CDN URL.
 | 
						|
            """
 | 
						|
        ),
 | 
						|
    ] = "https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js",
 | 
						|
    redoc_favicon_url: Annotated[
 | 
						|
        str,
 | 
						|
        Doc(
 | 
						|
            """
 | 
						|
            The URL of the favicon to use. It is normally shown in the browser tab.
 | 
						|
            """
 | 
						|
        ),
 | 
						|
    ] = "https://fastapi.tiangolo.com/img/favicon.png",
 | 
						|
    with_google_fonts: Annotated[
 | 
						|
        bool,
 | 
						|
        Doc(
 | 
						|
            """
 | 
						|
            Load and use Google Fonts.
 | 
						|
            """
 | 
						|
        ),
 | 
						|
    ] = True,
 | 
						|
) -> HTMLResponse:
 | 
						|
    """
 | 
						|
    Generate and return the HTML response that loads ReDoc for the alternative
 | 
						|
    API docs (normally served at `/redoc`).
 | 
						|
 | 
						|
    You would only call this function yourself if you needed to override some parts,
 | 
						|
    for example the URLs to use to load ReDoc's JavaScript and CSS.
 | 
						|
 | 
						|
    Read more about it in the
 | 
						|
    [FastAPI docs for Custom Docs UI Static Assets (Self-Hosting)](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/).
 | 
						|
    """
 | 
						|
    html = f"""
 | 
						|
    <!DOCTYPE html>
 | 
						|
    <html>
 | 
						|
    <head>
 | 
						|
    <title>{title}</title>
 | 
						|
    <!-- needed for adaptive design -->
 | 
						|
    <meta charset="utf-8"/>
 | 
						|
    <meta name="viewport" content="width=device-width, initial-scale=1">
 | 
						|
    """
 | 
						|
    if with_google_fonts:
 | 
						|
        html += """
 | 
						|
    <link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
 | 
						|
    """
 | 
						|
    html += f"""
 | 
						|
    <link rel="shortcut icon" href="{redoc_favicon_url}">
 | 
						|
    <!--
 | 
						|
    ReDoc doesn't change outer page styles
 | 
						|
    -->
 | 
						|
    <style>
 | 
						|
      body {{
 | 
						|
        margin: 0;
 | 
						|
        padding: 0;
 | 
						|
      }}
 | 
						|
    </style>
 | 
						|
    </head>
 | 
						|
    <body>
 | 
						|
    <noscript>
 | 
						|
        ReDoc requires Javascript to function. Please enable it to browse the documentation.
 | 
						|
    </noscript>
 | 
						|
    <redoc spec-url="{openapi_url}"></redoc>
 | 
						|
    <script src="{redoc_js_url}"> </script>
 | 
						|
    </body>
 | 
						|
    </html>
 | 
						|
    """
 | 
						|
    return HTMLResponse(html)
 | 
						|
 | 
						|
 | 
						|
def get_swagger_ui_oauth2_redirect_html() -> HTMLResponse:
 | 
						|
    """
 | 
						|
    Generate the HTML response with the OAuth2 redirection for Swagger UI.
 | 
						|
 | 
						|
    You normally don't need to use or change this.
 | 
						|
    """
 | 
						|
    # copied from https://github.com/swagger-api/swagger-ui/blob/v4.14.0/dist/oauth2-redirect.html
 | 
						|
    html = """
 | 
						|
    <!doctype html>
 | 
						|
    <html lang="en-US">
 | 
						|
    <head>
 | 
						|
        <title>Swagger UI: OAuth2 Redirect</title>
 | 
						|
    </head>
 | 
						|
    <body>
 | 
						|
    <script>
 | 
						|
        'use strict';
 | 
						|
        function run () {
 | 
						|
            var oauth2 = window.opener.swaggerUIRedirectOauth2;
 | 
						|
            var sentState = oauth2.state;
 | 
						|
            var redirectUrl = oauth2.redirectUrl;
 | 
						|
            var isValid, qp, arr;
 | 
						|
 | 
						|
            if (/code|token|error/.test(window.location.hash)) {
 | 
						|
                qp = window.location.hash.substring(1).replace('?', '&');
 | 
						|
            } else {
 | 
						|
                qp = location.search.substring(1);
 | 
						|
            }
 | 
						|
 | 
						|
            arr = qp.split("&");
 | 
						|
            arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';});
 | 
						|
            qp = qp ? JSON.parse('{' + arr.join() + '}',
 | 
						|
                    function (key, value) {
 | 
						|
                        return key === "" ? value : decodeURIComponent(value);
 | 
						|
                    }
 | 
						|
            ) : {};
 | 
						|
 | 
						|
            isValid = qp.state === sentState;
 | 
						|
 | 
						|
            if ((
 | 
						|
              oauth2.auth.schema.get("flow") === "accessCode" ||
 | 
						|
              oauth2.auth.schema.get("flow") === "authorizationCode" ||
 | 
						|
              oauth2.auth.schema.get("flow") === "authorization_code"
 | 
						|
            ) && !oauth2.auth.code) {
 | 
						|
                if (!isValid) {
 | 
						|
                    oauth2.errCb({
 | 
						|
                        authId: oauth2.auth.name,
 | 
						|
                        source: "auth",
 | 
						|
                        level: "warning",
 | 
						|
                        message: "Authorization may be unsafe, passed state was changed in server. The passed state wasn't returned from auth server."
 | 
						|
                    });
 | 
						|
                }
 | 
						|
 | 
						|
                if (qp.code) {
 | 
						|
                    delete oauth2.state;
 | 
						|
                    oauth2.auth.code = qp.code;
 | 
						|
                    oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
 | 
						|
                } else {
 | 
						|
                    let oauthErrorMsg;
 | 
						|
                    if (qp.error) {
 | 
						|
                        oauthErrorMsg = "["+qp.error+"]: " +
 | 
						|
                            (qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
 | 
						|
                            (qp.error_uri ? "More info: "+qp.error_uri : "");
 | 
						|
                    }
 | 
						|
 | 
						|
                    oauth2.errCb({
 | 
						|
                        authId: oauth2.auth.name,
 | 
						|
                        source: "auth",
 | 
						|
                        level: "error",
 | 
						|
                        message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server."
 | 
						|
                    });
 | 
						|
                }
 | 
						|
            } else {
 | 
						|
                oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl});
 | 
						|
            }
 | 
						|
            window.close();
 | 
						|
        }
 | 
						|
 | 
						|
        if (document.readyState !== 'loading') {
 | 
						|
            run();
 | 
						|
        } else {
 | 
						|
            document.addEventListener('DOMContentLoaded', function () {
 | 
						|
                run();
 | 
						|
            });
 | 
						|
        }
 | 
						|
    </script>
 | 
						|
    </body>
 | 
						|
    </html>
 | 
						|
        """
 | 
						|
    return HTMLResponse(content=html)
 |