11import inspect
22from collections import defaultdict
33from logging import getLogger
4- from typing import Any , Awaitable , Callable , Dict , Optional , TypeVar , get_type_hints
4+ from typing import (
5+ Any ,
6+ Awaitable ,
7+ Callable ,
8+ Dict ,
9+ Optional ,
10+ Tuple ,
11+ TypeVar ,
12+ get_type_hints ,
13+ )
514
615import pydantic
716from aiohttp import web
1322
1423_T = TypeVar ("_T" ) # noqa: WPS111
1524
25+ REF_TEMPLATE = "#/components/schemas/{model}"
1626SCHEMA_KEY = "openapi_schema"
1727SWAGGER_HTML_TEMPALTE = """
1828<html lang="en">
@@ -75,12 +85,13 @@ def dummy(_var: annotation.annotation) -> None: # type: ignore
7585 return var == Optional [var ]
7686
7787
78- def _add_route_def ( # noqa: C901, WPS210
88+ def _add_route_def ( # noqa: C901, WPS210, WPS211
7989 openapi_schema : Dict [str , Any ],
8090 route : web .ResourceRoute ,
8191 method : str ,
8292 graph : DependencyGraph ,
8393 extra_openapi : Dict [str , Any ],
94+ extra_openapi_schemas : Dict [str , Any ],
8495) -> None :
8596 route_info : Dict [str , Any ] = {
8697 "description" : inspect .getdoc (graph .target ),
@@ -90,7 +101,10 @@ def _add_route_def( # noqa: C901, WPS210
90101 if route .resource is None : # pragma: no cover
91102 return
92103
93- params : Dict [tuple [str , str ], Any ] = {}
104+ if extra_openapi_schemas :
105+ openapi_schema ["components" ]["schemas" ].update (extra_openapi_schemas )
106+
107+ params : Dict [Tuple [str , str ], Any ] = {}
94108
95109 def _insert_in_params (data : Dict [str , Any ]) -> None :
96110 element = params .get ((data ["name" ], data ["in" ]))
@@ -114,9 +128,9 @@ def _insert_in_params(data: Dict[str, Any]) -> None:
114128 ):
115129 input_schema = pydantic .TypeAdapter (
116130 dependency .signature .annotation ,
117- ).json_schema ()
131+ ).json_schema (ref_template = REF_TEMPLATE )
118132 openapi_schema ["components" ]["schemas" ].update (
119- input_schema .pop ("definitions " , {}),
133+ input_schema .pop ("$defs " , {}),
120134 )
121135 route_info ["requestBody" ] = {
122136 "content" : {content_type : {"schema" : input_schema }},
@@ -216,13 +230,19 @@ async def event_handler(app: web.Application) -> None:
216230 "__extra_openapi__" ,
217231 {},
218232 )
233+ extra_schemas = getattr (
234+ route ._handler .original_handler ,
235+ "__extra_openapi_schemas__" ,
236+ {},
237+ )
219238 try :
220239 _add_route_def (
221240 openapi_schema ,
222241 route , # type: ignore
223242 route .method ,
224243 route ._handler .graph ,
225244 extra_openapi = extra_openapi ,
245+ extra_openapi_schemas = extra_schemas ,
226246 )
227247 except Exception as exc : # pragma: no cover
228248 logger .warn (
@@ -234,20 +254,23 @@ async def event_handler(app: web.Application) -> None:
234254 elif isinstance (route ._handler , InjectableViewHandler ):
235255 for key , graph in route ._handler .graph_map .items ():
236256 extra_openapi = getattr (
237- getattr (
238- route ._handler .original_handler ,
239- key ,
240- ),
257+ getattr (route ._handler .original_handler , key ),
241258 "__extra_openapi__" ,
242259 {},
243260 )
261+ extra_schemas = getattr (
262+ getattr (route ._handler .original_handler , key ),
263+ "__extra_openapi_schemas__" ,
264+ {},
265+ )
244266 try :
245267 _add_route_def (
246268 openapi_schema ,
247269 route , # type: ignore
248270 key ,
249271 graph ,
250272 extra_openapi = extra_openapi ,
273+ extra_openapi_schemas = extra_schemas ,
251274 )
252275 except Exception as exc : # pragma: no cover
253276 logger .warn (
@@ -315,16 +338,20 @@ def openapi_response(
315338
316339 def decorator (func : _T ) -> _T :
317340 openapi = getattr (func , "__extra_openapi__" , {})
341+ openapi_schemas = getattr (func , "__extra_openapi_schemas__" , {})
318342 adapter : "pydantic.TypeAdapter[Any]" = pydantic .TypeAdapter (model )
319343 responses = openapi .get ("responses" , {})
320344 status_response = responses .get (status , {})
321345 if not status_response :
322346 status_response ["description" ] = description
323347 status_response ["content" ] = status_response .get ("content" , {})
324- status_response ["content" ][content_type ] = {"schema" : adapter .json_schema ()}
348+ response_schema = adapter .json_schema (ref_template = REF_TEMPLATE )
349+ openapi_schemas .update (response_schema .pop ("$defs" , {}))
350+ status_response ["content" ][content_type ] = {"schema" : response_schema }
325351 responses [status ] = status_response
326352 openapi ["responses" ] = responses
327353 func .__extra_openapi__ = openapi # type: ignore
354+ func .__extra_openapi_schemas__ = openapi_schemas # type: ignore
328355 return func
329356
330357 return decorator
0 commit comments