2020-10-25 20:28:11 +01:00

86 lines
3.0 KiB
Python

from aiohttp.web import json_response, Response
from aiohttp_pydantic.oas.struct import OpenApiSpec3, OperationObject, PathItem
from typing import Type
from ..injectors import _parse_func_signature
from ..view import PydanticView, is_pydantic_view
JSON_SCHEMA_TYPES = {float: "number", str: "string", int: "integer"}
def _add_http_method_to_oas(oas_path: PathItem, method: str, view: Type[PydanticView]):
method = method.lower()
mtd: OperationObject = getattr(oas_path, method)
handler = getattr(view, method)
path_args, body_args, qs_args, header_args = _parse_func_signature(handler)
if body_args:
mtd.request_body.content = {
"application/json": {"schema": next(iter(body_args.values())).schema()}
}
i = 0
for i, (name, type_) in enumerate(path_args.items()):
mtd.parameters[i].required = True
mtd.parameters[i].in_ = "path"
mtd.parameters[i].name = name
mtd.parameters[i].schema = {"type": JSON_SCHEMA_TYPES[type_]}
for i, (name, type_) in enumerate(qs_args.items(), i + 1):
mtd.parameters[i].required = False
mtd.parameters[i].in_ = "query"
mtd.parameters[i].name = name
mtd.parameters[i].schema = {"type": JSON_SCHEMA_TYPES[type_]}
for i, (name, type_) in enumerate(header_args.items(), i + 1):
mtd.parameters[i].required = False
mtd.parameters[i].in_ = "header"
mtd.parameters[i].name = name
mtd.parameters[i].schema = {"type": JSON_SCHEMA_TYPES[type_]}
async def get_oas(request):
"""
Generate Open Api Specification from PydanticView in application.
"""
apps = request.app["apps to expose"]
oas = OpenApiSpec3()
for app in apps:
for resources in app.router.resources():
for resource_route in resources:
if is_pydantic_view(resource_route.handler):
view: Type[PydanticView] = resource_route.handler
info = resource_route.get_info()
path = oas.paths[info.get("path", info.get("formatter"))]
if resource_route.method == "*":
for method_name in view.allowed_methods:
_add_http_method_to_oas(path, method_name, view)
else:
_add_http_method_to_oas(path, resource_route.method, view)
return json_response(oas.spec)
async def oas_ui(request):
"""
View to serve the swagger-ui to read open api specification of application.
"""
template = request.app["index template"]
static_url = request.app.router["static"].url_for(filename="")
spec_url = request.app.router["spec"].url_for()
host = request.url.origin()
return Response(
text=template.render(
{
"openapi_spec_url": host.with_path(str(spec_url)),
"static_url": host.with_path(str(static_url)),
}
),
content_type="text/html",
charset="utf-8",
)