Repository: maximdanilchenko/aiohttp-apispec Branch: master Commit: 3232c78a580f Files: 43 Total size: 2.0 MB Directory structure: gitextract_02v9aofl/ ├── .gitignore ├── .isort.cfg ├── .travis.yml ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.md ├── aiohttp_apispec/ │ ├── __init__.py │ ├── aiohttp_apispec.py │ ├── decorators/ │ │ ├── __init__.py │ │ ├── docs.py │ │ ├── request.py │ │ └── response.py │ ├── middlewares.py │ ├── static/ │ │ ├── index.html │ │ ├── oauth2-redirect.html │ │ ├── swagger-ui-bundle.js │ │ ├── swagger-ui-standalone-preset.js │ │ ├── swagger-ui.css │ │ └── swagger-ui.js │ └── utils.py ├── dev-requirements.txt ├── docs/ │ ├── api.rst │ ├── conf.py │ ├── index.rst │ ├── install.rst │ └── usage.rst ├── example/ │ ├── __init__.py │ ├── app.py │ ├── routes.py │ ├── schemas.py │ └── views.py ├── example_app.py ├── pyproject.toml ├── readthedocs.yaml ├── requirements.txt ├── setup.py └── tests/ ├── __init__.py ├── conftest.py ├── pytest.ini ├── test_decorators.py ├── test_documentation.py └── test_web_app.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .idea/ *.pyc *.cache /lib /__pycache__ .coverage .pypirc .pytest_cache /docs/build /build *.egg-info /dist /venv .mypy_cache/ .DS_Store .python-version ================================================ FILE: .isort.cfg ================================================ [settings] skip = venv multi_line_output = 3 include_trailing_comma = true line_length = 88 ================================================ FILE: .travis.yml ================================================ dist: xenial language: python python: - 3.6 - 3.7.9 - 3.8.12 - 3.9 - nightly - pypy3 matrix: allow_failures: - python: nightly - python: pypy3 install: - pip install -q -r dev-requirements.txt --no-cache-dir --upgrade script: - make check_format - make test after_success: codecov ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2017 Maksim Danilchenko Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: MANIFEST.in ================================================ include LICENSE include README.md include requirements.txt recursive-include aiohttp_apispec/static * graft aiohttp_apispec graft docs global-exclude *.pyc global-exclude *.pyd global-exclude *.so global-exclude *.lib global-exclude *.dll global-exclude *.a global-exclude *.obj prune docs/_build prune example ================================================ FILE: Makefile ================================================ test: pytest tests -x -v upload: if [ -d dist ]; then rm -Rf dist; fi python setup.py sdist twine upload dist/* format: isort . black --target-version py36 . check_format: isort . --check --diff black . --check --diff ================================================ FILE: README.md ================================================
Build and document REST APIs with aiohttp and apispec
```aiohttp-apispec``` key features:
- ```docs``` and ```request_schema``` decorators
to add swagger spec support out of the box;
- ```validation_middleware``` middleware to enable validating
with marshmallow schemas from those decorators;
- **SwaggerUI** support.
- *New from version 2.0* - ```match_info_schema```, ```querystring_schema```,
```form_schema```, ```json_schema```, ```headers_schema``` and ```cookies_schema```
decorators for specific request parts validation.
Look [here](#more-decorators) for more info.
```aiohttp-apispec``` api is fully inspired by ```flask-apispec``` library
**Version 3.0.0b1 with apispec>=5.0 webargs>=8.0 is in beta now** (`pip install aiohttp-apispec==3.0.0b1`).
## Contents
- [Install](#install)
- [Quickstart](#quickstart)
- [Adding validation middleware](#adding-validation-middleware)
- [More decorators](#more-decorators)
- [Custom error handling](#custom-error-handling)
- [Build swagger web client](#build-swagger-web-client)
- [Versioning](#versioning)
## Install
```
pip install aiohttp-apispec
```
## Quickstart
*Also you can read [blog post](https://dmax.blog/how_to_easily_build_modern_web_apis_with_python_and_aiohttp) about quickstart with aiohttp-apispec*
```Python
from aiohttp_apispec import (
docs,
request_schema,
setup_aiohttp_apispec,
)
from aiohttp import web
from marshmallow import Schema, fields
class RequestSchema(Schema):
id = fields.Int()
name = fields.Str(description="name")
@docs(
tags=["mytag"],
summary="Test method summary",
description="Test method description",
)
@request_schema(RequestSchema(strict=True))
async def index(request):
return web.json_response({"msg": "done", "data": {}})
app = web.Application()
app.router.add_post("/v1/test", index)
# init docs with all parameters, usual for ApiSpec
setup_aiohttp_apispec(
app=app,
title="My Documentation",
version="v1",
url="/api/docs/swagger.json",
swagger_path="/api/docs",
)
# Now we can find spec on 'http://localhost:8080/api/docs/swagger.json'
# and docs on 'http://localhost:8080/api/docs'
web.run_app(app)
```
Class based views are also supported:
```python
class TheView(web.View):
@docs(
tags=["mytag"],
summary="View method summary",
description="View method description",
)
@request_schema(RequestSchema(strict=True))
@response_schema(ResponseSchema(), 200)
def delete(self):
return web.json_response(
{"msg": "done", "data": {"name": self.request["data"]["name"]}}
)
app.router.add_view("/v1/view", TheView)
```
As alternative you can add responses info to `docs` decorator, which is more compact way.
And it allows you not to use schemas for responses documentation:
```python
@docs(
tags=["mytag"],
summary="Test method summary",
description="Test method description",
responses={
200: {
"schema": ResponseSchema,
"description": "Success response",
}, # regular response
404: {"description": "Not found"}, # responses without schema
422: {"description": "Validation error"},
},
)
@request_schema(RequestSchema(strict=True))
async def index(request):
return web.json_response({"msg": "done", "data": {}})
```
## Adding validation middleware
```Python
from aiohttp_apispec import validation_middleware
...
app.middlewares.append(validation_middleware)
```
Now you can access all validated data in route from ```request['data']``` like so:
```Python
@docs(
tags=["mytag"],
summary="Test method summary",
description="Test method description",
)
@request_schema(RequestSchema(strict=True))
@response_schema(ResponseSchema, 200)
async def index(request):
uid = request["data"]["id"]
name = request["data"]["name"]
return web.json_response(
{"msg": "done", "data": {"info": f"name - {name}, id - {uid}"}}
)
```
You can change ``Request``'s ``'data'`` param to another with ``request_data_name`` argument of
``setup_aiohttp_apispec`` function:
```python
setup_aiohttp_apispec(
app=app,
request_data_name="validated_data",
)
...
@request_schema(RequestSchema(strict=True))
async def index(request):
uid = request["validated_data"]["id"]
...
```
Also you can do it for specific view using ```put_into```
parameter (beginning from version 2.0):
```python
@request_schema(RequestSchema(strict=True), put_into="validated_data")
async def index(request):
uid = request["validated_data"]["id"]
...
```
## More decorators
Starting from version 2.0 you can use shortenings for documenting and validating
specific request parts like cookies, headers etc using those decorators:
| Decorator name | Default put_into param |
|:----------|:-----------------|
| match_info_schema | match_info |
| querystring_schema | querystring |
| form_schema | form |
| json_schema | json |
| headers_schema | headers |
| cookies_schema | cookies |
And example:
```python
@docs(
tags=["users"],
summary="Create new user",
description="Add new user to our toy database",
responses={
200: {"description": "Ok. User created", "schema": OkResponse},
401: {"description": "Unauthorized"},
422: {"description": "Validation error"},
500: {"description": "Server error"},
},
)
@headers_schema(AuthHeaders) # <- schema for headers validation
@json_schema(UserMeta) # <- schema for json body validation
@querystring_schema(UserParams) # <- schema for querystring params validation
async def create_user(request: web.Request):
headers = request["headers"] # <- validated headers!
json_data = request["json"] # <- validated json!
query_params = request["querystring"] # <- validated querystring!
...
```
## Custom error handling
If you want to catch validation errors by yourself you
could use `error_callback` parameter and create your custom error handler. Note that
it can be one of coroutine or callable and it should
have interface exactly like in examples below:
```python
from marshmallow import ValidationError, Schema
from aiohttp import web
from typing import Optional, Mapping, NoReturn
def my_error_handler(
error: ValidationError,
req: web.Request,
schema: Schema,
error_status_code: Optional[int] = None,
error_headers: Optional[Mapping[str, str]] = None,
) -> NoReturn:
raise web.HTTPBadRequest(
body=json.dumps(error.messages),
headers=error_headers,
content_type="application/json",
)
setup_aiohttp_apispec(app, error_callback=my_error_handler)
```
Also you can create your own exceptions and create
regular Request in middleware like so:
```python
class MyException(Exception):
def __init__(self, message):
self.message = message
# It can be coroutine as well:
async def my_error_handler(
error, req, schema, error_status_code, error_headers
):
await req.app["db"].do_smth() # So you can use some async stuff
raise MyException({"errors": error.messages, "text": "Oops"})
# This middleware will handle your own exceptions:
@web.middleware
async def intercept_error(request, handler):
try:
return await handler(request)
except MyException as e:
return web.json_response(e.message, status=400)
setup_aiohttp_apispec(app, error_callback=my_error_handler)
# Do not forget to add your own middleware before validation_middleware
app.middlewares.extend([intercept_error, validation_middleware])
```
## Build swagger web client
#### 3.X SwaggerUI version
Just add `swagger_path` parameter to `setup_aiohttp_apispec` function.
For example:
```python
setup_aiohttp_apispec(app, swagger_path="/docs")
```
Then go to `/docs` and see awesome SwaggerUI
#### 2.X SwaggerUI version
If you prefer older version you can use
[aiohttp_swagger](https://github.com/cr0hn/aiohttp-swagger) library.
`aiohttp-apispec` adds `swagger_dict` parameter to aiohttp web application
after initialization (with `setup_aiohttp_apispec` function).
So you can use it easily like:
```Python
from aiohttp_apispec import setup_aiohttp_apispec
from aiohttp_swagger import setup_swagger
def create_app(app):
setup_aiohttp_apispec(app)
async def swagger(app):
setup_swagger(
app=app, swagger_url="/api/doc", swagger_info=app["swagger_dict"]
)
app.on_startup.append(swagger)
# now we can access swagger client on '/api/doc' url
...
return app
```
## Versioning
This software follows [Semantic Versioning](http://semver.org/).
------
Please star this repository if this project helped you!
================================================
FILE: aiohttp_apispec/__init__.py
================================================
from .aiohttp_apispec import AiohttpApiSpec, setup_aiohttp_apispec
from .decorators import (
cookies_schema,
docs,
form_schema,
headers_schema,
json_schema,
marshal_with,
match_info_schema,
querystring_schema,
request_schema,
response_schema,
use_kwargs,
)
from .middlewares import validation_middleware
__all__ = [
# setup
"AiohttpApiSpec",
"setup_aiohttp_apispec",
# decorators
"docs",
"request_schema",
"match_info_schema",
"querystring_schema",
"form_schema",
"json_schema",
"headers_schema",
"cookies_schema",
"response_schema",
"use_kwargs",
"marshal_with",
# middleware
"validation_middleware",
]
================================================
FILE: aiohttp_apispec/aiohttp_apispec.py
================================================
import copy
import enum
import json
import os
from pathlib import Path
from typing import Awaitable, Callable, Union
from aiohttp import web
from aiohttp.hdrs import METH_ALL, METH_ANY
from apispec import APISpec
from apispec.core import VALID_METHODS_OPENAPI_V2
from apispec.ext.marshmallow import MarshmallowPlugin, common
from jinja2 import Template
from webargs.aiohttpparser import parser
from .utils import get_path, get_path_keys, issubclass_py37fix
_AiohttpView = Callable[[web.Request], Awaitable[web.StreamResponse]]
VALID_RESPONSE_FIELDS = {"description", "headers", "examples"}
DEFAULT_RESPONSE_LOCATION = "json"
NAME_SWAGGER_SPEC = "swagger.spec"
NAME_SWAGGER_DOCS = "swagger.docs"
NAME_SWAGGER_STATIC = "swagger.static"
INDEX_PAGE = "index.html"
def resolver(schema):
schema_instance = common.resolve_schema_instance(schema)
prefix = "Partial-" if schema_instance.partial else ""
schema_cls = common.resolve_schema_cls(schema)
name = prefix + schema_cls.__name__
if name.endswith("Schema"):
return name[:-6] or name
return name
class OpenApiVersion(str, enum.Enum):
V20 = "2.0"
V300 = "3.0.0"
V301 = "3.0.1"
V302 = "3.0.2"
V303 = "3.0.3"
class AiohttpApiSpec:
def __init__(
self,
url="/api/docs/swagger.json",
app=None,
request_data_name="data",
swagger_path=None,
static_path='/static/swagger',
error_callback=None,
in_place=False,
prefix='',
schema_name_resolver=resolver,
openapi_version=None,
**kwargs,
):
openapi_version = openapi_version or OpenApiVersion.V20
try:
openapi_version = OpenApiVersion(openapi_version)
except ValueError:
raise ValueError(
f"Invalid `openapi_version`: {openapi_version!r}"
) from None
self.plugin = MarshmallowPlugin(schema_name_resolver=schema_name_resolver)
self.spec = APISpec(
plugins=(self.plugin,),
openapi_version=openapi_version.value,
**kwargs,
)
self.url = url
self.swagger_path = swagger_path
self.static_path = static_path
self._registered = False
self._request_data_name = request_data_name
self.error_callback = error_callback
self.prefix = prefix
self._index_page = None
if app is not None:
self.register(app, in_place)
def swagger_dict(self):
"""Returns swagger spec representation in JSON format"""
return self.spec.to_dict()
def register(self, app: web.Application, in_place: bool = False):
"""Creates spec based on registered app routes and registers needed view"""
if self._registered is True:
return None
app["_apispec_request_data_name"] = self._request_data_name
if self.error_callback:
parser.error_callback = self.error_callback
app["_apispec_parser"] = parser
if in_place:
self._register(app)
else:
async def doc_routes(app_):
self._register(app_)
app.on_startup.append(doc_routes)
self._registered = True
if self.url is not None:
async def swagger_handler(request):
return web.json_response(request.app["swagger_dict"])
route_url = self.url
if not self.url.startswith("/"):
route_url = "/{}".format(self.url)
app.router.add_route(
"GET", route_url, swagger_handler, name=NAME_SWAGGER_SPEC
)
if self.swagger_path is not None:
self._add_swagger_web_page(app, self.static_path, self.swagger_path)
def _get_index_page(self, app, static_files, static_path):
if self._index_page is not None:
return self._index_page
with open(str(static_files / INDEX_PAGE)) as swg_tmp:
url = self.url if app is None else app.router[NAME_SWAGGER_SPEC].url_for()
if app is not None:
static_path = app.router[NAME_SWAGGER_STATIC].url_for(
filename=INDEX_PAGE
)
static_path = os.path.dirname(str(static_path))
if not self.spec.options.get("display_configurations"):
self.spec.options["display_configurations"] = {}
self._index_page = Template(swg_tmp.read()).render(
path=url,
static=static_path,
display_configurations=json.dumps(
self.spec.options["display_configurations"]
),
)
return self._index_page
def _add_swagger_web_page(
self, app: web.Application, static_path: str, view_path: str
):
static_files = Path(__file__).parent / "static"
app.router.add_static(static_path, static_files, name=NAME_SWAGGER_STATIC)
async def swagger_view(_):
index_page = self._get_index_page(app, static_files, static_path)
return web.Response(text=index_page, content_type="text/html")
app.router.add_route("GET", view_path, swagger_view, name=NAME_SWAGGER_DOCS)
def _register(self, app: web.Application):
for route in app.router.routes():
if issubclass_py37fix(route.handler, web.View) and route.method == METH_ANY:
for attr in dir(route.handler):
if attr.upper() in METH_ALL:
view = getattr(route.handler, attr)
method = attr
self._register_route(route, method, view)
else:
method = route.method.lower()
view = route.handler
self._register_route(route, method, view)
app["swagger_dict"] = self.swagger_dict()
def _register_route(
self, route: web.AbstractRoute, method: str, view: _AiohttpView
):
if not hasattr(view, "__apispec__"):
return None
url_path = get_path(route)
if not url_path:
return None
self._update_paths(view.__apispec__, method, self.prefix + url_path)
def _update_paths(self, data: dict, method: str, url_path: str):
if method not in VALID_METHODS_OPENAPI_V2:
return None
for schema in data.pop("schemas", []):
parameters = self.plugin.converter.schema2parameters(
schema["schema"], location=schema["location"], **schema["options"]
)
self._add_examples(schema["schema"], parameters, schema["example"])
data["parameters"].extend(parameters)
existing = [p["name"] for p in data["parameters"] if p["in"] == "path"]
data["parameters"].extend(
{"in": "path", "name": path_key, "required": True, "type": "string"}
for path_key in get_path_keys(url_path)
if path_key not in existing
)
if "responses" in data:
responses = {}
for code, actual_params in data["responses"].items():
if "schema" in actual_params:
raw_parameters = self.plugin.converter.schema2parameters(
actual_params["schema"],
location=DEFAULT_RESPONSE_LOCATION,
required=actual_params.get("required", False),
)[0]
updated_params = {
k: v
for k, v in raw_parameters.items()
if k in VALID_RESPONSE_FIELDS
}
if self.spec.components.openapi_version.major < 3:
updated_params['schema'] = actual_params["schema"]
else:
updated_params["content"] = {
"application/json": {
"schema": actual_params["schema"],
},
}
for extra_info in ("description", "headers", "examples"):
if extra_info in actual_params:
updated_params[extra_info] = actual_params[extra_info]
responses[code] = updated_params
else:
responses[code] = actual_params
data["responses"] = responses
operations = copy.deepcopy(data)
self.spec.path(path=url_path, operations={method: operations})
def _add_examples(self, ref_schema, endpoint_schema, example):
def add_to_endpoint_or_ref():
if add_to_refs:
self.spec.components.schemas[name]["example"] = example
else:
endpoint_schema[0]['schema']['allOf'] = [
endpoint_schema[0]['schema'].pop('$ref')
]
endpoint_schema[0]['schema']["example"] = example
if not example:
return
schema_instance = common.resolve_schema_instance(ref_schema)
name = self.plugin.converter.schema_name_resolver(schema_instance)
add_to_refs = example.pop('add_to_refs')
if self.spec.components.openapi_version.major < 3:
if name and name in self.spec.components.schemas:
add_to_endpoint_or_ref()
else:
add_to_endpoint_or_ref()
def setup_aiohttp_apispec(
app: web.Application,
*,
title: str = "API documentation",
version: str = "0.0.1",
url: str = "/api/docs/swagger.json",
request_data_name: str = "data",
swagger_path: str = None,
static_path: str = '/static/swagger',
error_callback=None,
in_place: bool = False,
prefix: str = '',
schema_name_resolver: Callable = resolver,
openapi_version: Union[str, OpenApiVersion] = OpenApiVersion.V20,
**kwargs,
) -> AiohttpApiSpec:
"""
aiohttp-apispec extension.
Usage:
.. code-block:: python
from aiohttp_apispec import docs, request_schema, setup_aiohttp_apispec
from aiohttp import web
from marshmallow import Schema, fields
class RequestSchema(Schema):
id = fields.Int()
name = fields.Str(description='name')
bool_field = fields.Bool()
@docs(tags=['mytag'],
summary='Test method summary',
description='Test method description')
@request_schema(RequestSchema)
async def index(request):
return web.json_response({'msg': 'done', 'data': {}})
app = web.Application()
app.router.add_post('/v1/test', index)
# init docs with all parameters, usual for ApiSpec
setup_aiohttp_apispec(app=app,
title='My Documentation',
version='v1',
url='/api/docs/api-docs')
# now we can find it on 'http://localhost:8080/api/docs/api-docs'
web.run_app(app)
:param Application app: aiohttp web app
:param str title: API title
:param str version: API version
:param str url: url for swagger spec in JSON format
:param str request_data_name: name of the key in Request object
where validated data will be placed by
validation_middleware (``'data'`` by default)
:param str swagger_path: experimental SwaggerUI support (starting from v1.1.0).
By default it is None (disabled)
:param str static_path: path for static files used by SwaggerUI
(if it is enabled with ``swagger_path``)
:param error_callback: custom error handler
:param in_place: register all routes at the moment of calling this function
instead of the moment of the on_startup signal.
If True, be sure all routes are added to router
:param prefix: prefix to add to all registered routes
:param schema_name_resolver: custom schema_name_resolver for MarshmallowPlugin.
:param openapi_version: version of OpenAPI schema
:param kwargs: any apispec.APISpec kwargs
:return: return instance of AiohttpApiSpec class
:rtype: AiohttpApiSpec
"""
return AiohttpApiSpec(
url,
app,
request_data_name,
title=title,
version=version,
swagger_path=swagger_path,
static_path=static_path,
error_callback=error_callback,
in_place=in_place,
prefix=prefix,
schema_name_resolver=schema_name_resolver,
openapi_version=openapi_version,
**kwargs,
)
================================================
FILE: aiohttp_apispec/decorators/__init__.py
================================================
from .docs import docs
from .request import (
cookies_schema,
form_schema,
headers_schema,
json_schema,
match_info_schema,
querystring_schema,
request_schema,
use_kwargs,
)
from .response import marshal_with, response_schema
================================================
FILE: aiohttp_apispec/decorators/docs.py
================================================
def docs(**kwargs):
"""
Annotate the decorated view function with the specified Swagger
attributes.
Usage:
.. code-block:: python
from aiohttp import web
@docs(tags=['my_tag'],
summary='Test method summary',
description='Test method description',
parameters=[{
'in': 'header',
'name': 'X-Request-ID',
'schema': {'type': 'string', 'format': 'uuid'},
'required': 'true'
}]
)
async def index(request):
return web.json_response({'msg': 'done', 'data': {}})
"""
def wrapper(func):
if not kwargs.get("produces"):
kwargs["produces"] = ["application/json"]
if not hasattr(func, "__apispec__"):
func.__apispec__ = {"schemas": [], "responses": {}, "parameters": []}
func.__schemas__ = []
extra_parameters = kwargs.pop("parameters", [])
extra_responses = kwargs.pop("responses", {})
func.__apispec__["parameters"].extend(extra_parameters)
func.__apispec__["responses"].update(extra_responses)
func.__apispec__.update(kwargs)
return func
return wrapper
================================================
FILE: aiohttp_apispec/decorators/request.py
================================================
import copy
from functools import partial
# locations supported by both openapi and webargs.aiohttpparser
VALID_SCHEMA_LOCATIONS = (
"cookies",
"files",
"form",
"headers",
"json",
"match_info",
"path",
"query",
"querystring",
)
def request_schema(
schema, location="json", put_into=None, example=None, add_to_refs=False, **kwargs
):
"""
Add request info into the swagger spec and
prepare injection keyword arguments from the specified
webargs arguments into the decorated view function in
request['data'] for validation_middleware validation middleware.
Usage:
.. code-block:: python
from aiohttp import web
from marshmallow import Schema, fields
class RequestSchema(Schema):
id = fields.Int()
name = fields.Str(description='name')
@request_schema(RequestSchema(strict=True))
async def index(request):
# aiohttp_apispec_middleware should be used for it
data = request['data']
return web.json_response({'name': data['name'],
'id': data['id']})
:param schema: :class:`Schema f?new Ct([],r):h;if(h&&p>f&&iv;g-=v){var b=f>>>g&y;m=m.array[b]=Lt(m.array[b],r)}m.array[f>>>v&y]=h}if(u=p)i-=p,u-=p,s=v,c=null,d=d&&d.removeBefore(r,0,i);else if(i>o||po&&(c=c.removeBefore(r,s,i-l)),c&&p4)}function l(e){var t=e.get("swagger");return"string"==typeof t&&i()(t).call(t,"2.0")}function f(e){return function(t,n){return function(r){return n&&n.specSelectors&&n.specSelectors.specJson?c(n.specSelectors.specJson())?s.a.createElement(e,o()({},r,n,{Ori:t})):s.a.createElement(t,r):(console.warn("OAS3 wrapper: couldn't get spec"),null)}}}},function(e,t,n){e.exports=n(513)},function(e,t){e.exports=function(e){try{return!!e()}catch(e){return!0}}},function(e,t,n){var r=n(38),o=n(202),a=n(51),i=n(164),u=n(201),s=n(316),c=o("wks"),l=r.Symbol,f=s?l:l&&l.withoutSetter||i;e.exports=function(e){return a(c,e)&&(u||"string"==typeof c[e])||(u&&a(l,e)?c[e]=l[e]:c[e]=f("Symbol."+e)),c[e]}},function(e,t,n){var r=n(230);e.exports=function(e,t,n){var o=null==e?void 0:r(e,t);return void 0===o?n:o}},function(e,t,n){e.exports=n(807)},function(e,t,n){(function(t){var n=function(e){return e&&e.Math==Math&&e};e.exports=n("object"==typeof globalThis&&globalThis)||n("object"==typeof window&&window)||n("object"==typeof self&&self)||n("object"==typeof t&&t)||function(){return this}()||Function("return this")()}).call(this,n(53))},function(e,t,n){var r=n(31);e.exports=function(e){return r[e+"Prototype"]}},function(e,t,n){var r=n(31),o=n(51),a=n(211),i=n(62).f;e.exports=function(e){var t=r.Symbol||(r.Symbol={});o(t,e)||i(t,e,{value:a.f(e)})}},function(e,t){e.exports=function(e){return"object"==typeof e?null!==e:"function"==typeof e}},function(e,t,n){"use strict";n.r(t),n.d(t,"UPDATE_SPEC",(function(){return ee})),n.d(t,"UPDATE_URL",(function(){return te})),n.d(t,"UPDATE_JSON",(function(){return ne})),n.d(t,"UPDATE_PARAM",(function(){return re})),n.d(t,"UPDATE_EMPTY_PARAM_INCLUSION",(function(){return oe})),n.d(t,"VALIDATE_PARAMS",(function(){return ae})),n.d(t,"SET_RESPONSE",(function(){return ie})),n.d(t,"SET_REQUEST",(function(){return ue})),n.d(t,"SET_MUTATED_REQUEST",(function(){return se})),n.d(t,"LOG_REQUEST",(function(){return ce})),n.d(t,"CLEAR_RESPONSE",(function(){return le})),n.d(t,"CLEAR_REQUEST",(function(){return fe})),n.d(t,"CLEAR_VALIDATE_PARAMS",(function(){return pe})),n.d(t,"UPDATE_OPERATION_META_VALUE",(function(){return he})),n.d(t,"UPDATE_RESOLVED",(function(){return de})),n.d(t,"UPDATE_RESOLVED_SUBTREE",(function(){return me})),n.d(t,"SET_SCHEME",(function(){return ve})),n.d(t,"updateSpec",(function(){return ge})),n.d(t,"updateResolved",(function(){return ye})),n.d(t,"updateUrl",(function(){return be})),n.d(t,"updateJsonSpec",(function(){return we})),n.d(t,"parseToJson",(function(){return xe})),n.d(t,"resolveSpec",(function(){return Ee})),n.d(t,"requestResolvedSubtree",(function(){return Ae})),n.d(t,"changeParam",(function(){return Oe})),n.d(t,"changeParamByIdentity",(function(){return Ce})),n.d(t,"updateResolvedSubtree",(function(){return je})),n.d(t,"invalidateResolvedSubtreeCache",(function(){return Te})),n.d(t,"validateParams",(function(){return Ie})),n.d(t,"updateEmptyParamInclusion",(function(){return Ne})),n.d(t,"clearValidateParams",(function(){return Pe})),n.d(t,"changeConsumesValue",(function(){return Me})),n.d(t,"changeProducesValue",(function(){return Re})),n.d(t,"setResponse",(function(){return De})),n.d(t,"setRequest",(function(){return Le})),n.d(t,"setMutatedRequest",(function(){return Be})),n.d(t,"logRequest",(function(){return Fe})),n.d(t,"executeRequest",(function(){return ze})),n.d(t,"execute",(function(){return qe})),n.d(t,"clearResponse",(function(){return Ue})),n.d(t,"clearRequest",(function(){return Ve})),n.d(t,"setScheme",(function(){return We}));var r=n(24),o=n.n(r),a=n(49),i=n.n(a),u=n(68),s=n.n(u),c=n(18),l=n.n(c),f=n(37),p=n.n(f),h=n(23),d=n.n(h),m=n(4),v=n.n(m),g=n(306),y=n.n(g),b=n(28),w=n.n(b),x=n(189),_=n.n(x),E=n(60),S=n.n(E),k=n(12),A=n.n(k),O=n(190),C=n.n(O),j=n(17),T=n.n(j),I=n(22),N=n.n(I),P=n(2),M=n.n(P),R=n(15),D=n.n(R),L=n(20),B=n.n(L),F=n(307),z=n.n(F),q=n(66),U=n(1),V=n(82),W=n.n(V),H=n(134),$=n(439),J=n.n($),K=n(440),Y=n.n(K),G=n(308),Q=n.n(G),Z=n(5),X=["path","method"],ee="spec_update_spec",te="spec_update_url",ne="spec_update_json",re="spec_update_param",oe="spec_update_empty_param_inclusion",ae="spec_validate_param",ie="spec_set_response",ue="spec_set_request",se="spec_set_mutated_request",ce="spec_log_request",le="spec_clear_response",fe="spec_clear_request",pe="spec_clear_validate_param",he="spec_update_operation_meta_value",de="spec_update_resolved",me="spec_update_resolved_subtree",ve="set_scheme";function ge(e){var t,n=(t=e,J()(t)?t:"").replace(/\t/g," ");if("string"==typeof e)return{type:ee,payload:n}}function ye(e){return{type:de,payload:e}}function be(e){return{type:te,payload:e}}function we(e){return{type:ne,payload:e}}var xe=function(e){return function(t){var n=t.specActions,r=t.specSelectors,o=t.errActions,a=r.specStr,i=null;try{e=e||a(),o.clear({source:"parser"}),i=q.a.load(e)}catch(e){return console.error(e),o.newSpecErr({source:"parser",level:"error",message:e.reason,line:e.mark&&e.mark.line?e.mark.line+1:void 0})}return i&&"object"===l()(i)?n.updateJsonSpec(i):{}}},_e=!1,Ee=function(e,t){return function(n){var r=n.specActions,o=n.specSelectors,a=n.errActions,i=n.fn,u=i.fetch,s=i.resolve,c=i.AST,l=void 0===c?{}:c,f=n.getConfigs;_e||(console.warn("specActions.resolveSpec is deprecated since v3.10.0 and will be removed in v4.0.0; use requestResolvedSubtree instead!"),_e=!0);var p=f(),h=p.modelPropertyMacro,m=p.parameterMacro,g=p.requestInterceptor,b=p.responseInterceptor;void 0===e&&(e=o.specJson()),void 0===t&&(t=o.url());var w=l.getLineNumberForPath?l.getLineNumberForPath:function(){},x=o.specStr();return s({fetch:u,spec:e,baseDoc:t,modelPropertyMacro:h,parameterMacro:m,requestInterceptor:g,responseInterceptor:b}).then((function(e){var t=e.spec,n=e.errors;if(a.clear({type:"thrown"}),d()(n)&&n.length>0){var o=v()(n).call(n,(function(e){return console.error(e),e.line=e.fullPath?w(x,e.fullPath):null,e.path=e.fullPath?e.fullPath.join("."):null,e.level="error",e.type="thrown",e.source="resolver",y()(e,"message",{enumerable:!0,value:e.message}),e}));a.newThrownErrBatch(o)}return r.updateResolved(t)}))}},Se=[],ke=Y()(s()(p.a.mark((function e(){var t,n,r,o,a,i,u,c,l,f,h,m,g,b,x,E,k,O;return p.a.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=Se.system){e.next=4;break}return console.error("debResolveSubtrees: don't have a system to operate on, aborting."),e.abrupt("return");case 4:if(n=t.errActions,r=t.errSelectors,o=t.fn,a=o.resolveSubtree,i=o.fetch,u=o.AST,c=void 0===u?{}:u,l=t.specSelectors,f=t.specActions,a){e.next=8;break}return console.error("Error: Swagger-Client did not provide a `resolveSubtree` method, doing nothing."),e.abrupt("return");case 8:return h=c.getLineNumberForPath?c.getLineNumberForPath:function(){},m=l.specStr(),g=t.getConfigs(),b=g.modelPropertyMacro,x=g.parameterMacro,E=g.requestInterceptor,k=g.responseInterceptor,e.prev=11,e.next=14,w()(Se).call(Se,function(){var e=s()(p.a.mark((function e(t,o){var u,c,f,g,w,O,j,T,I;return p.a.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,t;case 2:return u=e.sent,c=u.resultMap,f=u.specWithCurrentSubtrees,e.next=7,a(f,o,{baseDoc:l.url(),modelPropertyMacro:b,parameterMacro:x,requestInterceptor:E,responseInterceptor:k});case 7:if(g=e.sent,w=g.errors,O=g.spec,r.allErrors().size&&n.clearBy((function(e){var t;return"thrown"!==e.get("type")||"resolver"!==e.get("source")||!_()(t=e.get("fullPath")).call(t,(function(e,t){return e===o[t]||void 0===o[t]}))})),d()(w)&&w.length>0&&(j=v()(w).call(w,(function(e){return e.line=e.fullPath?h(m,e.fullPath):null,e.path=e.fullPath?e.fullPath.join("."):null,e.level="error",e.type="thrown",e.source="resolver",y()(e,"message",{enumerable:!0,value:e.message}),e})),n.newThrownErrBatch(j)),!O||!l.isOAS3()||"components"!==o[0]||"securitySchemes"!==o[1]){e.next=15;break}return e.next=15,S.a.all(v()(T=A()(I=C()(O)).call(I,(function(e){return"openIdConnect"===e.type}))).call(T,function(){var e=s()(p.a.mark((function e(t){var n,r;return p.a.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n={url:t.openIdConnectUrl,requestInterceptor:E,responseInterceptor:k},e.prev=1,e.next=4,i(n);case 4:(r=e.sent)instanceof Error||r.status>=400?console.error(r.statusText+" "+n.url):t.openIdConnectData=JSON.parse(r.text),e.next=11;break;case 8:e.prev=8,e.t0=e.catch(1),console.error(e.t0);case 11:case"end":return e.stop()}}),e,null,[[1,8]])})));return function(t){return e.apply(this,arguments)}}()));case 15:return Q()(c,o,O),Q()(f,o,O),e.abrupt("return",{resultMap:c,specWithCurrentSubtrees:f});case 18:case"end":return e.stop()}}),e)})));return function(t,n){return e.apply(this,arguments)}}(),S.a.resolve({resultMap:(l.specResolvedSubtree([])||Object(U.Map)()).toJS(),specWithCurrentSubtrees:l.specJson().toJS()}));case 14:O=e.sent,delete Se.system,Se=[],e.next=22;break;case 19:e.prev=19,e.t0=e.catch(11),console.error(e.t0);case 22:f.updateResolvedSubtree([],O.resultMap);case 23:case"end":return e.stop()}}),e,null,[[11,19]])}))),35),Ae=function(e){return function(t){var n;T()(n=v()(Se).call(Se,(function(e){return e.join("@@")}))).call(n,e.join("@@"))>-1||(Se.push(e),Se.system=t,ke())}};function Oe(e,t,n,r,o){return{type:re,payload:{path:e,value:r,paramName:t,paramIn:n,isXml:o}}}function Ce(e,t,n,r){return{type:re,payload:{path:e,param:t,value:n,isXml:r}}}var je=function(e,t){return{type:me,payload:{path:e,value:t}}},Te=function(){return{type:me,payload:{path:[],value:Object(U.Map)()}}},Ie=function(e,t){return{type:ae,payload:{pathMethod:e,isOAS3:t}}},Ne=function(e,t,n,r){return{type:oe,payload:{pathMethod:e,paramName:t,paramIn:n,includeEmptyValue:r}}};function Pe(e){return{type:pe,payload:{pathMethod:e}}}function Me(e,t){return{type:he,payload:{path:e,value:t,key:"consumes_value"}}}function Re(e,t){return{type:he,payload:{path:e,value:t,key:"produces_value"}}}var De=function(e,t,n){return{payload:{path:e,method:t,res:n},type:ie}},Le=function(e,t,n){return{payload:{path:e,method:t,req:n},type:ue}},Be=function(e,t,n){return{payload:{path:e,method:t,req:n},type:se}},Fe=function(e){return{payload:e,type:ce}},ze=function(e){return function(t){var n,r,o=t.fn,a=t.specActions,i=t.specSelectors,u=t.getConfigs,c=t.oas3Selectors,l=e.pathName,f=e.method,h=e.operation,m=u(),g=m.requestInterceptor,y=m.responseInterceptor,b=h.toJS();h&&h.get("parameters")&&N()(n=A()(r=h.get("parameters")).call(r,(function(e){return e&&!0===e.get("allowEmptyValue")}))).call(n,(function(t){if(i.parameterInclusionSettingFor([l,f],t.get("name"),t.get("in"))){e.parameters=e.parameters||{};var n=Object(Z.B)(t,e.parameters);(!n||n&&0===n.size)&&(e.parameters[t.get("name")]="")}}));if(e.contextUrl=W()(i.url()).toString(),b&&b.operationId?e.operationId=b.operationId:b&&l&&f&&(e.operationId=o.opId(b,l,f)),i.isOAS3()){var w,x=M()(w="".concat(l,":")).call(w,f);e.server=c.selectedServer(x)||c.selectedServer();var _=c.serverVariables({server:e.server,namespace:x}).toJS(),E=c.serverVariables({server:e.server}).toJS();e.serverVariables=D()(_).length?_:E,e.requestContentType=c.requestContentType(l,f),e.responseContentType=c.responseContentType(l,f)||"*/*";var S,k=c.requestBodyValue(l,f),O=c.requestBodyInclusionSetting(l,f);if(k&&k.toJS)e.requestBody=A()(S=v()(k).call(k,(function(e){return U.Map.isMap(e)?e.get("value"):e}))).call(S,(function(e,t){return(d()(e)?0!==e.length:!Object(Z.q)(e))||O.get(t)})).toJS();else e.requestBody=k}var C=B()({},e);C=o.buildRequest(C),a.setRequest(e.pathName,e.method,C);var j=function(){var t=s()(p.a.mark((function t(n){var r,o;return p.a.wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return t.next=2,g.apply(undefined,[n]);case 2:return r=t.sent,o=B()({},r),a.setMutatedRequest(e.pathName,e.method,o),t.abrupt("return",r);case 6:case"end":return t.stop()}}),t)})));return function(e){return t.apply(this,arguments)}}();e.requestInterceptor=j,e.responseInterceptor=y;var T=z()();return o.execute(e).then((function(t){t.duration=z()()-T,a.setResponse(e.pathName,e.method,t)})).catch((function(t){"Failed to fetch"===t.message&&(t.name="",t.message='**Failed to fetch.** \n**Possible Reasons:** \n - CORS \n - Network Failure \n - URL scheme must be "http" or "https" for CORS request.'),a.setResponse(e.pathName,e.method,{error:!0,err:Object(H.serializeError)(t)})}))}},qe=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.path,n=e.method,r=i()(e,X);return function(e){var a=e.fn.fetch,i=e.specSelectors,u=e.specActions,s=i.specJsonWithResolvedSubtrees().toJS(),c=i.operationScheme(t,n),l=i.contentTypeValues([t,n]).toJS(),f=l.requestContentType,p=l.responseContentType,h=/xml/i.test(f),d=i.parameterValues([t,n],h).toJS();return u.executeRequest(o()(o()({},r),{},{fetch:a,spec:s,pathName:t,method:n,parameters:d,requestContentType:f,scheme:c,responseContentType:p}))}};function Ue(e,t){return{type:le,payload:{path:e,method:t}}}function Ve(e,t){return{type:fe,payload:{path:e,method:t}}}function We(e,t,n){return{type:ve,payload:{scheme:e,path:t,method:n}}}},function(e,t,n){var r;!function(){"use strict";var n={}.hasOwnProperty;function o(){for(var e=[],t=0;t