mirror of
https://github.com/babysor/MockingBird.git
synced 2024-03-22 13:11:31 +08:00
103 lines
3.3 KiB
Python
103 lines
3.3 KiB
Python
|
"""Collection of utilities for FastAPI apps."""
|
||
|
|
||
|
import inspect
|
||
|
from typing import Any, Type
|
||
|
|
||
|
from fastapi import FastAPI, Form
|
||
|
from pydantic import BaseModel
|
||
|
|
||
|
|
||
|
def as_form(cls: Type[BaseModel]) -> Any:
|
||
|
"""Adds an as_form class method to decorated models.
|
||
|
|
||
|
The as_form class method can be used with FastAPI endpoints
|
||
|
"""
|
||
|
new_params = [
|
||
|
inspect.Parameter(
|
||
|
field.alias,
|
||
|
inspect.Parameter.POSITIONAL_ONLY,
|
||
|
default=(Form(field.default) if not field.required else Form(...)),
|
||
|
)
|
||
|
for field in cls.__fields__.values()
|
||
|
]
|
||
|
|
||
|
async def _as_form(**data): # type: ignore
|
||
|
return cls(**data)
|
||
|
|
||
|
sig = inspect.signature(_as_form)
|
||
|
sig = sig.replace(parameters=new_params)
|
||
|
_as_form.__signature__ = sig # type: ignore
|
||
|
setattr(cls, "as_form", _as_form)
|
||
|
return cls
|
||
|
|
||
|
|
||
|
def patch_fastapi(app: FastAPI) -> None:
|
||
|
"""Patch function to allow relative url resolution.
|
||
|
|
||
|
This patch is required to make fastapi fully functional with a relative url path.
|
||
|
This code snippet can be copy-pasted to any Fastapi application.
|
||
|
"""
|
||
|
from fastapi.openapi.docs import get_redoc_html, get_swagger_ui_html
|
||
|
from starlette.requests import Request
|
||
|
from starlette.responses import HTMLResponse
|
||
|
|
||
|
async def redoc_ui_html(req: Request) -> HTMLResponse:
|
||
|
assert app.openapi_url is not None
|
||
|
redoc_ui = get_redoc_html(
|
||
|
openapi_url="./" + app.openapi_url.lstrip("/"),
|
||
|
title=app.title + " - Redoc UI",
|
||
|
)
|
||
|
|
||
|
return HTMLResponse(redoc_ui.body.decode("utf-8"))
|
||
|
|
||
|
async def swagger_ui_html(req: Request) -> HTMLResponse:
|
||
|
assert app.openapi_url is not None
|
||
|
swagger_ui = get_swagger_ui_html(
|
||
|
openapi_url="./" + app.openapi_url.lstrip("/"),
|
||
|
title=app.title + " - Swagger UI",
|
||
|
oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url,
|
||
|
)
|
||
|
|
||
|
# insert request interceptor to have all request run on relativ path
|
||
|
request_interceptor = (
|
||
|
"requestInterceptor: (e) => {"
|
||
|
"\n\t\t\tvar url = window.location.origin + window.location.pathname"
|
||
|
'\n\t\t\turl = url.substring( 0, url.lastIndexOf( "/" ) + 1);'
|
||
|
"\n\t\t\turl = e.url.replace(/http(s)?:\/\/[^/]*\//i, url);" # noqa: W605
|
||
|
"\n\t\t\te.contextUrl = url"
|
||
|
"\n\t\t\te.url = url"
|
||
|
"\n\t\t\treturn e;}"
|
||
|
)
|
||
|
|
||
|
return HTMLResponse(
|
||
|
swagger_ui.body.decode("utf-8").replace(
|
||
|
"dom_id: '#swagger-ui',",
|
||
|
"dom_id: '#swagger-ui',\n\t\t" + request_interceptor + ",",
|
||
|
)
|
||
|
)
|
||
|
|
||
|
# remove old docs route and add our patched route
|
||
|
routes_new = []
|
||
|
for app_route in app.routes:
|
||
|
if app_route.path == "/docs": # type: ignore
|
||
|
continue
|
||
|
|
||
|
if app_route.path == "/redoc": # type: ignore
|
||
|
continue
|
||
|
|
||
|
routes_new.append(app_route)
|
||
|
|
||
|
app.router.routes = routes_new
|
||
|
|
||
|
assert app.docs_url is not None
|
||
|
app.add_route(app.docs_url, swagger_ui_html, include_in_schema=False)
|
||
|
assert app.redoc_url is not None
|
||
|
app.add_route(app.redoc_url, redoc_ui_html, include_in_schema=False)
|
||
|
|
||
|
# Make graphql realtive
|
||
|
from starlette import graphql
|
||
|
|
||
|
graphql.GRAPHIQL = graphql.GRAPHIQL.replace(
|
||
|
"({{REQUEST_PATH}}", '("." + {{REQUEST_PATH}}'
|
||
|
)
|