docs.py
 1  from typing import Any, Type, cast
 2  
 3  from pydantic import BaseConfig, BaseModel
 4  from uvicorn.protocols.http.h11_impl import STATUS_PHRASES
 5  
 6  from ..exceptions.api_exception import APIException
 7  
 8  
 9  def responses(default: type, *args: Type[APIException]) -> dict[int | str, dict[str, Any]]:
10      exceptions: dict[int, list[Type[APIException]]] = {}
11      for exc in args:
12          exceptions.setdefault(exc.status_code, []).append(exc)
13  
14      out: dict[int | str, dict[str, Any]] = {}
15      for code, excs in exceptions.items():
16          examples = {}
17          for i, exc in enumerate(excs):
18              name = exc.__name__ if len(excs) == 1 else f"{exc.__name__} ({i + 1}/{len(excs)})"
19              examples[name] = {"description": exc.description, "value": {"detail": exc.detail}}
20  
21          out[code] = {"description": STATUS_PHRASES[code], "content": {"application/json": {"examples": examples}}}
22  
23      return out | {200: {"model": default}}
24  
25  
26  def get_example(arg: Type[BaseModel]) -> dict[str, Any]:
27      return cast(dict[str, dict[str, Any]], arg.Config.schema_extra)["example"]
28  
29  
30  def example(*args: Type[BaseModel], **kwargs: Any) -> Type[BaseConfig]:
31      ex = dict(e for arg in args for e in get_example(arg).items())
32      return cast(Type[BaseConfig], type("Config", (BaseConfig,), {"schema_extra": {"example": ex | kwargs}}))
33  
34  
35  def add_endpoint_links_to_openapi_docs(openapi_schema: dict[str, Any]) -> None:
36      anchors: dict[str, str] = {
37          f"{method.upper()} {name}": f"docs#/{route['tags'][0]}/{route['operationId']}"
38          for name, path in openapi_schema["paths"].items()
39          for method, route in path.items()
40      }
41  
42      def replace(text: str) -> str:
43          for endpoint, anchor in anchors.items():
44              text = text.replace(f"`{endpoint}`", f"[`{endpoint}`]({anchor})")
45          return text
46  
47      def add_links(schema: Any) -> Any:
48          if isinstance(schema, dict):
49              for k, v in schema.items():
50                  schema[k] = add_links(v)
51          elif isinstance(schema, list):
52              for i, v in enumerate(schema):
53                  schema[i] = add_links(v)
54          elif isinstance(schema, str):
55              schema = replace(schema)
56          return schema
57  
58      add_links(openapi_schema)