mirror of
https://github.com/LukeHagar/connexion.git
synced 2025-12-06 04:19:26 +00:00
Add swagger-ui docs and clean up swagger-ui options (#1739)
Contributes to #1531
This commit is contained in:
@@ -12,6 +12,7 @@ from starlette.types import ASGIApp, Receive, Scope, Send
|
|||||||
from connexion.jsonifier import Jsonifier
|
from connexion.jsonifier import Jsonifier
|
||||||
from connexion.middleware import ConnexionMiddleware, MiddlewarePosition, SpecMiddleware
|
from connexion.middleware import ConnexionMiddleware, MiddlewarePosition, SpecMiddleware
|
||||||
from connexion.middleware.lifespan import Lifespan
|
from connexion.middleware.lifespan import Lifespan
|
||||||
|
from connexion.options import SwaggerUIOptions
|
||||||
from connexion.resolver import Resolver
|
from connexion.resolver import Resolver
|
||||||
from connexion.uri_parsing import AbstractURIParser
|
from connexion.uri_parsing import AbstractURIParser
|
||||||
|
|
||||||
@@ -43,7 +44,7 @@ class AbstractApp:
|
|||||||
resolver: t.Optional[t.Union[Resolver, t.Callable]] = None,
|
resolver: t.Optional[t.Union[Resolver, t.Callable]] = None,
|
||||||
resolver_error: t.Optional[int] = None,
|
resolver_error: t.Optional[int] = None,
|
||||||
strict_validation: t.Optional[bool] = None,
|
strict_validation: t.Optional[bool] = None,
|
||||||
swagger_ui_options: t.Optional[dict] = None,
|
swagger_ui_options: t.Optional[SwaggerUIOptions] = None,
|
||||||
uri_parser_class: t.Optional[AbstractURIParser] = None,
|
uri_parser_class: t.Optional[AbstractURIParser] = None,
|
||||||
validate_responses: t.Optional[bool] = None,
|
validate_responses: t.Optional[bool] = None,
|
||||||
validator_map: t.Optional[dict] = None,
|
validator_map: t.Optional[dict] = None,
|
||||||
@@ -72,8 +73,8 @@ class AbstractApp:
|
|||||||
start.
|
start.
|
||||||
:param strict_validation: When True, extra form or query parameters not defined in the
|
:param strict_validation: When True, extra form or query parameters not defined in the
|
||||||
specification result in a validation error. Defaults to False.
|
specification result in a validation error. Defaults to False.
|
||||||
:param swagger_ui_options: A dict with configuration options for the swagger ui. See
|
:param swagger_ui_options: Instance of :class:`options.ConnexionOptions` with
|
||||||
:class:`options.ConnexionOptions`.
|
configuration options for the swagger ui.
|
||||||
:param uri_parser_class: Class to use for uri parsing. See :mod:`uri_parsing`.
|
:param uri_parser_class: Class to use for uri parsing. See :mod:`uri_parsing`.
|
||||||
:param validate_responses: Whether to validate responses against the specification. This has
|
:param validate_responses: Whether to validate responses against the specification. This has
|
||||||
an impact on performance. Defaults to False.
|
an impact on performance. Defaults to False.
|
||||||
@@ -128,7 +129,7 @@ class AbstractApp:
|
|||||||
resolver: t.Optional[t.Union[Resolver, t.Callable]] = None,
|
resolver: t.Optional[t.Union[Resolver, t.Callable]] = None,
|
||||||
resolver_error: t.Optional[int] = None,
|
resolver_error: t.Optional[int] = None,
|
||||||
strict_validation: t.Optional[bool] = None,
|
strict_validation: t.Optional[bool] = None,
|
||||||
swagger_ui_options: t.Optional[dict] = None,
|
swagger_ui_options: t.Optional[SwaggerUIOptions] = None,
|
||||||
uri_parser_class: t.Optional[AbstractURIParser] = None,
|
uri_parser_class: t.Optional[AbstractURIParser] = None,
|
||||||
validate_responses: t.Optional[bool] = None,
|
validate_responses: t.Optional[bool] = None,
|
||||||
validator_map: t.Optional[dict] = None,
|
validator_map: t.Optional[dict] = None,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ from connexion.jsonifier import Jsonifier
|
|||||||
from connexion.middleware.abstract import RoutedAPI, RoutedMiddleware
|
from connexion.middleware.abstract import RoutedAPI, RoutedMiddleware
|
||||||
from connexion.middleware.lifespan import Lifespan
|
from connexion.middleware.lifespan import Lifespan
|
||||||
from connexion.operations import AbstractOperation
|
from connexion.operations import AbstractOperation
|
||||||
|
from connexion.options import SwaggerUIOptions
|
||||||
from connexion.resolver import Resolver
|
from connexion.resolver import Resolver
|
||||||
from connexion.uri_parsing import AbstractURIParser
|
from connexion.uri_parsing import AbstractURIParser
|
||||||
|
|
||||||
@@ -131,7 +132,7 @@ class AsyncApp(AbstractApp):
|
|||||||
resolver: t.Optional[t.Union[Resolver, t.Callable]] = None,
|
resolver: t.Optional[t.Union[Resolver, t.Callable]] = None,
|
||||||
resolver_error: t.Optional[int] = None,
|
resolver_error: t.Optional[int] = None,
|
||||||
strict_validation: t.Optional[bool] = None,
|
strict_validation: t.Optional[bool] = None,
|
||||||
swagger_ui_options: t.Optional[dict] = None,
|
swagger_ui_options: t.Optional[SwaggerUIOptions] = None,
|
||||||
uri_parser_class: t.Optional[AbstractURIParser] = None,
|
uri_parser_class: t.Optional[AbstractURIParser] = None,
|
||||||
validate_responses: t.Optional[bool] = None,
|
validate_responses: t.Optional[bool] = None,
|
||||||
validator_map: t.Optional[dict] = None,
|
validator_map: t.Optional[dict] = None,
|
||||||
@@ -161,8 +162,8 @@ class AsyncApp(AbstractApp):
|
|||||||
start.
|
start.
|
||||||
:param strict_validation: When True, extra form or query parameters not defined in the
|
:param strict_validation: When True, extra form or query parameters not defined in the
|
||||||
specification result in a validation error. Defaults to False.
|
specification result in a validation error. Defaults to False.
|
||||||
:param swagger_ui_options: A dict with configuration options for the swagger ui. See
|
:param swagger_ui_options: Instance of :class:`options.ConnexionOptions` with
|
||||||
:class:`options.ConnexionOptions`.
|
configuration options for the swagger ui.
|
||||||
:param uri_parser_class: Class to use for uri parsing. See :mod:`uri_parsing`.
|
:param uri_parser_class: Class to use for uri parsing. See :mod:`uri_parsing`.
|
||||||
:param validate_responses: Whether to validate responses against the specification. This has
|
:param validate_responses: Whether to validate responses against the specification. This has
|
||||||
an impact on performance. Defaults to False.
|
an impact on performance. Defaults to False.
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ from connexion.jsonifier import Jsonifier
|
|||||||
from connexion.middleware.abstract import AbstractRoutingAPI, SpecMiddleware
|
from connexion.middleware.abstract import AbstractRoutingAPI, SpecMiddleware
|
||||||
from connexion.middleware.lifespan import Lifespan
|
from connexion.middleware.lifespan import Lifespan
|
||||||
from connexion.operations import AbstractOperation
|
from connexion.operations import AbstractOperation
|
||||||
|
from connexion.options import SwaggerUIOptions
|
||||||
from connexion.problem import problem
|
from connexion.problem import problem
|
||||||
from connexion.resolver import Resolver
|
from connexion.resolver import Resolver
|
||||||
from connexion.uri_parsing import AbstractURIParser
|
from connexion.uri_parsing import AbstractURIParser
|
||||||
@@ -188,7 +189,7 @@ class FlaskApp(AbstractApp):
|
|||||||
resolver: t.Optional[t.Union[Resolver, t.Callable]] = None,
|
resolver: t.Optional[t.Union[Resolver, t.Callable]] = None,
|
||||||
resolver_error: t.Optional[int] = None,
|
resolver_error: t.Optional[int] = None,
|
||||||
strict_validation: t.Optional[bool] = None,
|
strict_validation: t.Optional[bool] = None,
|
||||||
swagger_ui_options: t.Optional[dict] = None,
|
swagger_ui_options: t.Optional[SwaggerUIOptions] = None,
|
||||||
uri_parser_class: t.Optional[AbstractURIParser] = None,
|
uri_parser_class: t.Optional[AbstractURIParser] = None,
|
||||||
validate_responses: t.Optional[bool] = None,
|
validate_responses: t.Optional[bool] = None,
|
||||||
validator_map: t.Optional[dict] = None,
|
validator_map: t.Optional[dict] = None,
|
||||||
@@ -221,8 +222,8 @@ class FlaskApp(AbstractApp):
|
|||||||
start.
|
start.
|
||||||
:param strict_validation: When True, extra form or query parameters not defined in the
|
:param strict_validation: When True, extra form or query parameters not defined in the
|
||||||
specification result in a validation error. Defaults to False.
|
specification result in a validation error. Defaults to False.
|
||||||
:param swagger_ui_options: A dict with configuration options for the swagger ui. See
|
:param swagger_ui_options: Instance of :class:`options.ConnexionOptions` with
|
||||||
:class:`options.ConnexionOptions`.
|
configuration options for the swagger ui.
|
||||||
:param uri_parser_class: Class to use for uri parsing. See :mod:`uri_parsing`.
|
:param uri_parser_class: Class to use for uri parsing. See :mod:`uri_parsing`.
|
||||||
:param validate_responses: Whether to validate responses against the specification. This has
|
:param validate_responses: Whether to validate responses against the specification. This has
|
||||||
an impact on performance. Defaults to False.
|
an impact on performance. Defaults to False.
|
||||||
|
|||||||
@@ -8,12 +8,16 @@ import sys
|
|||||||
from os import path
|
from os import path
|
||||||
|
|
||||||
import click
|
import click
|
||||||
import importlib_metadata
|
|
||||||
from clickclick import AliasedGroup
|
from clickclick import AliasedGroup
|
||||||
|
|
||||||
import connexion
|
import connexion
|
||||||
from connexion.mock import MockResolver
|
from connexion.mock import MockResolver
|
||||||
|
|
||||||
|
try:
|
||||||
|
import importlib_metadata
|
||||||
|
except ImportError:
|
||||||
|
import importlib.metadata as importlib_metadata # type: ignore
|
||||||
|
|
||||||
logger = logging.getLogger("connexion.cli")
|
logger = logging.getLogger("connexion.cli")
|
||||||
|
|
||||||
FLASK_APP = "flask"
|
FLASK_APP = "flask"
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ from connexion.middleware.response_validation import ResponseValidationMiddlewar
|
|||||||
from connexion.middleware.routing import RoutingMiddleware
|
from connexion.middleware.routing import RoutingMiddleware
|
||||||
from connexion.middleware.security import SecurityMiddleware
|
from connexion.middleware.security import SecurityMiddleware
|
||||||
from connexion.middleware.swagger_ui import SwaggerUIMiddleware
|
from connexion.middleware.swagger_ui import SwaggerUIMiddleware
|
||||||
|
from connexion.options import SwaggerUIOptions
|
||||||
from connexion.resolver import Resolver
|
from connexion.resolver import Resolver
|
||||||
from connexion.uri_parsing import AbstractURIParser
|
from connexion.uri_parsing import AbstractURIParser
|
||||||
from connexion.utils import inspect_function_arguments
|
from connexion.utils import inspect_function_arguments
|
||||||
@@ -51,7 +52,7 @@ class _Options:
|
|||||||
resolver_error: t.Optional[int] = None
|
resolver_error: t.Optional[int] = None
|
||||||
resolver_error_handler: t.Optional[t.Callable] = field(init=False)
|
resolver_error_handler: t.Optional[t.Callable] = field(init=False)
|
||||||
strict_validation: t.Optional[bool] = False
|
strict_validation: t.Optional[bool] = False
|
||||||
swagger_ui_options: t.Optional[dict] = None
|
swagger_ui_options: t.Optional[SwaggerUIOptions] = None
|
||||||
uri_parser_class: t.Optional[AbstractURIParser] = None
|
uri_parser_class: t.Optional[AbstractURIParser] = None
|
||||||
validate_responses: t.Optional[bool] = False
|
validate_responses: t.Optional[bool] = False
|
||||||
validator_map: t.Optional[dict] = None
|
validator_map: t.Optional[dict] = None
|
||||||
@@ -186,7 +187,7 @@ class ConnexionMiddleware:
|
|||||||
resolver: t.Optional[t.Union[Resolver, t.Callable]] = None,
|
resolver: t.Optional[t.Union[Resolver, t.Callable]] = None,
|
||||||
resolver_error: t.Optional[int] = None,
|
resolver_error: t.Optional[int] = None,
|
||||||
strict_validation: t.Optional[bool] = None,
|
strict_validation: t.Optional[bool] = None,
|
||||||
swagger_ui_options: t.Optional[dict] = None,
|
swagger_ui_options: t.Optional[SwaggerUIOptions] = None,
|
||||||
uri_parser_class: t.Optional[AbstractURIParser] = None,
|
uri_parser_class: t.Optional[AbstractURIParser] = None,
|
||||||
validate_responses: t.Optional[bool] = None,
|
validate_responses: t.Optional[bool] = None,
|
||||||
validator_map: t.Optional[dict] = None,
|
validator_map: t.Optional[dict] = None,
|
||||||
@@ -214,8 +215,8 @@ class ConnexionMiddleware:
|
|||||||
start.
|
start.
|
||||||
:param strict_validation: When True, extra form or query parameters not defined in the
|
:param strict_validation: When True, extra form or query parameters not defined in the
|
||||||
specification result in a validation error. Defaults to False.
|
specification result in a validation error. Defaults to False.
|
||||||
:param swagger_ui_options: A dict with configuration options for the swagger ui. See
|
:param swagger_ui_options: Instance of :class:`options.ConnexionOptions` with
|
||||||
:class:`options.ConnexionOptions`.
|
configuration options for the swagger ui.
|
||||||
:param uri_parser_class: Class to use for uri parsing. See :mod:`uri_parsing`.
|
:param uri_parser_class: Class to use for uri parsing. See :mod:`uri_parsing`.
|
||||||
:param validate_responses: Whether to validate responses against the specification. This has
|
:param validate_responses: Whether to validate responses against the specification. This has
|
||||||
an impact on performance. Defaults to False.
|
an impact on performance. Defaults to False.
|
||||||
@@ -338,7 +339,7 @@ class ConnexionMiddleware:
|
|||||||
resolver: t.Optional[t.Union[Resolver, t.Callable]] = None,
|
resolver: t.Optional[t.Union[Resolver, t.Callable]] = None,
|
||||||
resolver_error: t.Optional[int] = None,
|
resolver_error: t.Optional[int] = None,
|
||||||
strict_validation: t.Optional[bool] = None,
|
strict_validation: t.Optional[bool] = None,
|
||||||
swagger_ui_options: t.Optional[dict] = None,
|
swagger_ui_options: t.Optional[SwaggerUIOptions] = None,
|
||||||
uri_parser_class: t.Optional[AbstractURIParser] = None,
|
uri_parser_class: t.Optional[AbstractURIParser] = None,
|
||||||
validate_responses: t.Optional[bool] = None,
|
validate_responses: t.Optional[bool] = None,
|
||||||
validator_map: t.Optional[dict] = None,
|
validator_map: t.Optional[dict] = None,
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ from starlette.types import ASGIApp, Receive, Scope, Send
|
|||||||
from connexion.jsonifier import Jsonifier
|
from connexion.jsonifier import Jsonifier
|
||||||
from connexion.middleware import SpecMiddleware
|
from connexion.middleware import SpecMiddleware
|
||||||
from connexion.middleware.abstract import AbstractSpecAPI
|
from connexion.middleware.abstract import AbstractSpecAPI
|
||||||
from connexion.options import SwaggerUIOptions
|
from connexion.options import SwaggerUIConfig, SwaggerUIOptions
|
||||||
from connexion.utils import yamldumper
|
from connexion.utils import yamldumper
|
||||||
|
|
||||||
logger = logging.getLogger("connexion.middleware.swagger_ui")
|
logger = logging.getLogger("connexion.middleware.swagger_ui")
|
||||||
@@ -30,13 +30,13 @@ class SwaggerUIAPI(AbstractSpecAPI):
|
|||||||
self,
|
self,
|
||||||
*args,
|
*args,
|
||||||
default: ASGIApp,
|
default: ASGIApp,
|
||||||
swagger_ui_options: t.Optional[dict] = None,
|
swagger_ui_options: t.Optional[SwaggerUIOptions] = None,
|
||||||
**kwargs
|
**kwargs
|
||||||
):
|
):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.router = Router(default=default)
|
self.router = Router(default=default)
|
||||||
self.options = SwaggerUIOptions(
|
self.options = SwaggerUIConfig(
|
||||||
swagger_ui_options, oas_version=self.specification.version
|
swagger_ui_options, oas_version=self.specification.version
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -44,11 +44,11 @@ class SwaggerUIAPI(AbstractSpecAPI):
|
|||||||
self.add_openapi_json()
|
self.add_openapi_json()
|
||||||
self.add_openapi_yaml()
|
self.add_openapi_yaml()
|
||||||
|
|
||||||
if self.options.openapi_console_ui_available:
|
if self.options.swagger_ui_available:
|
||||||
self.add_swagger_ui()
|
self.add_swagger_ui()
|
||||||
|
|
||||||
self._templates = Jinja2Templates(
|
self._templates = Jinja2Templates(
|
||||||
directory=str(self.options.openapi_console_ui_from_dir)
|
directory=str(self.options.swagger_ui_template_dir)
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -121,7 +121,7 @@ class SwaggerUIAPI(AbstractSpecAPI):
|
|||||||
"""
|
"""
|
||||||
Adds swagger ui to {base_path}/ui/
|
Adds swagger ui to {base_path}/ui/
|
||||||
"""
|
"""
|
||||||
console_ui_path = self.options.openapi_console_ui_path.strip().rstrip("/")
|
console_ui_path = self.options.swagger_ui_path.strip().rstrip("/")
|
||||||
logger.debug("Adding swagger-ui: %s%s/", self.base_path, console_ui_path)
|
logger.debug("Adding swagger-ui: %s%s/", self.base_path, console_ui_path)
|
||||||
|
|
||||||
for path in (
|
for path in (
|
||||||
@@ -132,7 +132,7 @@ class SwaggerUIAPI(AbstractSpecAPI):
|
|||||||
methods=["GET"], path=path, endpoint=self._get_swagger_ui_home
|
methods=["GET"], path=path, endpoint=self._get_swagger_ui_home
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.options.openapi_console_ui_config is not None:
|
if self.options.swagger_ui_config:
|
||||||
self.router.add_route(
|
self.router.add_route(
|
||||||
methods=["GET"],
|
methods=["GET"],
|
||||||
path=console_ui_path + "/swagger-ui-config.json",
|
path=console_ui_path + "/swagger-ui-config.json",
|
||||||
@@ -155,7 +155,7 @@ class SwaggerUIAPI(AbstractSpecAPI):
|
|||||||
# serve index.html, so we add the redirect above.
|
# serve index.html, so we add the redirect above.
|
||||||
self.router.mount(
|
self.router.mount(
|
||||||
path=console_ui_path,
|
path=console_ui_path,
|
||||||
app=StaticFiles(directory=str(self.options.openapi_console_ui_from_dir)),
|
app=StaticFiles(directory=str(self.options.swagger_ui_template_dir)),
|
||||||
name="swagger_ui_static",
|
name="swagger_ui_static",
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -164,9 +164,9 @@ class SwaggerUIAPI(AbstractSpecAPI):
|
|||||||
template_variables = {
|
template_variables = {
|
||||||
"request": req,
|
"request": req,
|
||||||
"openapi_spec_url": (base_path + self.options.openapi_spec_path),
|
"openapi_spec_url": (base_path + self.options.openapi_spec_path),
|
||||||
**self.options.openapi_console_ui_index_template_variables,
|
**self.options.swagger_ui_template_arguments,
|
||||||
}
|
}
|
||||||
if self.options.openapi_console_ui_config is not None:
|
if self.options.swagger_ui_config:
|
||||||
template_variables["configUrl"] = "swagger-ui-config.json"
|
template_variables["configUrl"] = "swagger-ui-config.json"
|
||||||
|
|
||||||
return self._templates.TemplateResponse("index.j2", template_variables)
|
return self._templates.TemplateResponse("index.j2", template_variables)
|
||||||
@@ -175,7 +175,7 @@ class SwaggerUIAPI(AbstractSpecAPI):
|
|||||||
return StarletteResponse(
|
return StarletteResponse(
|
||||||
status_code=200,
|
status_code=200,
|
||||||
media_type="application/json",
|
media_type="application/json",
|
||||||
content=json.dumps(self.options.openapi_console_ui_config),
|
content=json.dumps(self.options.swagger_ui_config),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
"""
|
"""
|
||||||
This module defines a Connexion specific options class to pass to the Connexion App or API.
|
This module defines a Connexion specific options class to pass to the Connexion App or API.
|
||||||
"""
|
"""
|
||||||
|
import dataclasses
|
||||||
import logging
|
import logging
|
||||||
from typing import Optional # NOQA
|
import typing as t
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from py_swagger_ui import swagger_ui_path
|
from py_swagger_ui import swagger_ui_path as default_template_dir
|
||||||
except ImportError:
|
except ImportError:
|
||||||
swagger_ui_path = None
|
default_template_dir = None
|
||||||
|
|
||||||
NO_UI_MSG = """The swagger_ui directory could not be found.
|
NO_UI_MSG = """The swagger_ui directory could not be found.
|
||||||
Please install connexion with extra install: pip install connexion[swagger-ui]
|
Please install connexion with extra install: pip install connexion[swagger-ui]
|
||||||
@@ -18,131 +18,82 @@ NO_UI_MSG = """The swagger_ui directory could not be found.
|
|||||||
logger = logging.getLogger("connexion.options")
|
logger = logging.getLogger("connexion.options")
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
class SwaggerUIOptions:
|
class SwaggerUIOptions:
|
||||||
|
"""Options to configure the Swagger UI.
|
||||||
|
|
||||||
|
:param serve_spec: Whether to serve the Swagger / OpenAPI Specification
|
||||||
|
:param spec_path: Where to serve the Swagger / OpenAPI Specification
|
||||||
|
|
||||||
|
:param swagger_ui: Whether to serve the Swagger UI
|
||||||
|
:param swagger_ui_path: Where to serve the Swagger UI
|
||||||
|
:param swagger_ui_config: Options to configure the Swagger UI. See
|
||||||
|
https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration
|
||||||
|
for an overview of the available options.
|
||||||
|
:param swagger_ui_template_dir: Directory with static files to use to serve Swagger UI
|
||||||
|
:param swagger_ui_template_arguments: Arguments passed to the Swagger UI template. Useful
|
||||||
|
when providing your own template dir with additional template arguments.
|
||||||
|
"""
|
||||||
|
|
||||||
|
serve_spec: bool = True
|
||||||
|
spec_path: t.Optional[str] = None
|
||||||
|
|
||||||
|
swagger_ui: bool = True
|
||||||
|
swagger_ui_config: dict = dataclasses.field(default_factory=dict)
|
||||||
|
swagger_ui_path: str = "/ui"
|
||||||
|
swagger_ui_template_dir: t.Optional[str] = None
|
||||||
|
swagger_ui_template_arguments: dict = dataclasses.field(default_factory=dict)
|
||||||
|
|
||||||
|
|
||||||
|
class SwaggerUIConfig:
|
||||||
"""Class holding swagger UI specific options."""
|
"""Class holding swagger UI specific options."""
|
||||||
|
|
||||||
def __init__(self, options=None, oas_version=(2,)):
|
def __init__(
|
||||||
self._options = {}
|
self,
|
||||||
self.oas_version = oas_version
|
options: t.Optional[SwaggerUIOptions] = None,
|
||||||
self.swagger_ui_local_path = swagger_ui_path
|
oas_version: t.Tuple[int, ...] = (2,),
|
||||||
if self.oas_version >= (3, 0, 0):
|
):
|
||||||
self.openapi_spec_name = "/openapi.json"
|
if oas_version >= (3, 0, 0):
|
||||||
|
self.spec_path = "/openapi.json"
|
||||||
else:
|
else:
|
||||||
self.openapi_spec_name = "/swagger.json"
|
self.spec_path = "/swagger.json"
|
||||||
|
|
||||||
if options:
|
self._options = options or SwaggerUIOptions()
|
||||||
self._options.update(filter_values(options))
|
|
||||||
|
|
||||||
def extend(self, new_values=None):
|
|
||||||
# type: (Optional[dict]) -> SwaggerUIOptions
|
|
||||||
"""
|
|
||||||
Return a new instance of `ConnexionOptions` using as default the currently
|
|
||||||
defined options.
|
|
||||||
"""
|
|
||||||
if new_values is None:
|
|
||||||
new_values = {}
|
|
||||||
|
|
||||||
options = dict(self._options)
|
|
||||||
options.update(filter_values(new_values))
|
|
||||||
return SwaggerUIOptions(options, self.oas_version)
|
|
||||||
|
|
||||||
def as_dict(self):
|
|
||||||
return self._options
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def openapi_spec_available(self):
|
def openapi_spec_available(self) -> bool:
|
||||||
# type: () -> bool
|
"""Whether to make the OpenAPI Specification available."""
|
||||||
"""
|
return self._options.serve_spec
|
||||||
Whether to make available the OpenAPI Specification under
|
|
||||||
`openapi_spec_path`.
|
|
||||||
|
|
||||||
Default: True
|
|
||||||
"""
|
|
||||||
deprecated_option = self._options.get("swagger_json", True)
|
|
||||||
serve_spec = self._options.get("serve_spec", deprecated_option)
|
|
||||||
if "swagger_json" in self._options:
|
|
||||||
deprecation_warning = (
|
|
||||||
"The 'swagger_json' option is deprecated. "
|
|
||||||
"Please use 'serve_spec' instead"
|
|
||||||
)
|
|
||||||
logger.warning(deprecation_warning)
|
|
||||||
return serve_spec
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def openapi_console_ui_available(self):
|
def openapi_spec_path(self) -> str:
|
||||||
# type: () -> bool
|
"""Path to host the Swagger UI."""
|
||||||
"""
|
return self._options.spec_path or self.spec_path
|
||||||
Whether to make the OpenAPI Console UI available under the path
|
|
||||||
defined in `openapi_console_ui_path` option.
|
|
||||||
|
|
||||||
Default: True
|
@property
|
||||||
"""
|
def swagger_ui_available(self) -> bool:
|
||||||
if (
|
"""Whether to make the Swagger UI available."""
|
||||||
self._options.get("swagger_ui", True)
|
if self._options.swagger_ui and self.swagger_ui_template_dir is None:
|
||||||
and self.openapi_console_ui_from_dir is None
|
|
||||||
):
|
|
||||||
logger.warning(NO_UI_MSG)
|
logger.warning(NO_UI_MSG)
|
||||||
return False
|
return False
|
||||||
return self._options.get("swagger_ui", True)
|
return self._options.swagger_ui
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def openapi_spec_path(self):
|
def swagger_ui_path(self) -> str:
|
||||||
# type: () -> str
|
"""Path to mount the Swagger UI and make it accessible via a browser."""
|
||||||
"""
|
return self._options.swagger_ui_path
|
||||||
Path to mount the OpenAPI Console UI and make it accessible via a browser.
|
|
||||||
|
|
||||||
Default: /openapi.json for openapi3, otherwise /swagger.json
|
|
||||||
"""
|
|
||||||
return self._options.get("openapi_spec_path", self.openapi_spec_name)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def openapi_console_ui_path(self):
|
def swagger_ui_template_dir(self) -> str:
|
||||||
# type: () -> str
|
"""Directory with static files to use to serve Swagger UI."""
|
||||||
"""
|
return self._options.swagger_ui_template_dir or default_template_dir
|
||||||
Path to mount the OpenAPI Console UI and make it accessible via a browser.
|
|
||||||
|
|
||||||
Default: /ui
|
|
||||||
"""
|
|
||||||
return self._options.get("swagger_url", "/ui")
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def openapi_console_ui_from_dir(self):
|
def swagger_ui_config(self) -> dict:
|
||||||
# type: () -> str
|
"""Options to configure the Swagger UI."""
|
||||||
"""
|
return self._options.swagger_ui_config
|
||||||
Custom OpenAPI Console UI directory from where Connexion will serve
|
|
||||||
the static files.
|
|
||||||
|
|
||||||
Default: Connexion's vendored version of the OpenAPI Console UI.
|
|
||||||
"""
|
|
||||||
return self._options.get("swagger_path", self.swagger_ui_local_path)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def openapi_console_ui_config(self):
|
def swagger_ui_template_arguments(self) -> dict:
|
||||||
# type: () -> dict
|
"""Arguments passed to the Swagger UI template."""
|
||||||
"""
|
return self._options.swagger_ui_template_arguments
|
||||||
Custom OpenAPI Console UI config.
|
|
||||||
|
|
||||||
Default: None
|
|
||||||
"""
|
|
||||||
return self._options.get("swagger_ui_config", None)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def openapi_console_ui_index_template_variables(self):
|
|
||||||
# type: () -> dict
|
|
||||||
"""
|
|
||||||
Custom variables passed to the OpenAPI Console UI template.
|
|
||||||
|
|
||||||
Default: {}
|
|
||||||
"""
|
|
||||||
return self._options.get("swagger_ui_template_arguments", {})
|
|
||||||
|
|
||||||
|
|
||||||
def filter_values(dictionary):
|
|
||||||
# type: (dict) -> dict
|
|
||||||
"""
|
|
||||||
Remove `None` value entries in the dictionary.
|
|
||||||
|
|
||||||
:param dictionary:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
return {key: value for key, value in dictionary.items() if value is not None}
|
|
||||||
|
|||||||
@@ -64,8 +64,9 @@ Documentation
|
|||||||
|
|
||||||
quickstart
|
quickstart
|
||||||
middleware
|
middleware
|
||||||
cli
|
|
||||||
routing
|
routing
|
||||||
|
swagger_ui
|
||||||
|
cli
|
||||||
request
|
request
|
||||||
response
|
response
|
||||||
security
|
security
|
||||||
|
|||||||
@@ -236,10 +236,12 @@ If you installed connexion using the :code:`swagger-ui` extra, a Swagger UI is a
|
|||||||
API, providing interactive documentation. By default the UI is hosted at :code:`{base_path}/ui/`
|
API, providing interactive documentation. By default the UI is hosted at :code:`{base_path}/ui/`
|
||||||
where :code:`base_path`` is the base path of the API.
|
where :code:`base_path`` is the base path of the API.
|
||||||
|
|
||||||
**https://localhost:{port}/{base_path}/ui/**
|
**https://{host}/{base_path}/ui/**
|
||||||
|
|
||||||
.. image:: images/swagger_ui.png
|
.. image:: images/swagger_ui.png
|
||||||
|
|
||||||
|
Check :doc:`swagger_ui` for information on how to configure the UI.
|
||||||
|
|
||||||
Full App class reference
|
Full App class reference
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
|
|||||||
71
docs/swagger_ui.rst
Normal file
71
docs/swagger_ui.rst
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
The Swagger UI
|
||||||
|
==============
|
||||||
|
|
||||||
|
If you installed connexion using the :code:`swagger-ui` extra, a Swagger UI is available for each
|
||||||
|
API, providing interactive documentation. By default the UI is hosted at :code:`{base_path}/ui/`
|
||||||
|
where :code:`base_path`` is the base path of the API.
|
||||||
|
|
||||||
|
**https://{host}/{base_path}/ui/**
|
||||||
|
|
||||||
|
.. image:: images/swagger_ui.png
|
||||||
|
|
||||||
|
Configuring the Swagger UI
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
You can change this path through the ``swagger_ui_options`` argument, either whe instantiating
|
||||||
|
your application, or when adding your api:
|
||||||
|
|
||||||
|
|
||||||
|
.. tab-set::
|
||||||
|
|
||||||
|
.. tab-item:: AsyncApp
|
||||||
|
:sync: AsyncApp
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:caption: **app.py**
|
||||||
|
|
||||||
|
from connexion import AsyncApp
|
||||||
|
from connexion.options import SwaggerUIOptions
|
||||||
|
|
||||||
|
options = SwaggerUIOptions(swagger_ui_path="/docs")
|
||||||
|
|
||||||
|
app = AsyncApp(__name__, swagger_ui_options=options)
|
||||||
|
app.add_api("openapi.yaml", swagger_ui_options=options)
|
||||||
|
|
||||||
|
.. tab-item:: FlaskApp
|
||||||
|
:sync: FlaskApp
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:caption: **app.py**
|
||||||
|
|
||||||
|
from connexion import FlaskApp
|
||||||
|
from connexion.options import SwaggerUIOptions
|
||||||
|
|
||||||
|
options = SwaggerUIOptions(swagger_ui_path="/docs")
|
||||||
|
|
||||||
|
app = FlaskApp(__name__, swagger_ui_options=options)
|
||||||
|
app.add_api("openapi.yaml", swagger_ui_options=options)
|
||||||
|
|
||||||
|
.. tab-item:: ConnexionMiddleware
|
||||||
|
:sync: ConnexionMiddleware
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:caption: **app.py**
|
||||||
|
|
||||||
|
from asgi_framework import App
|
||||||
|
from connexion import ConnexionMiddleware
|
||||||
|
from connexion.options import SwaggerUIOptions
|
||||||
|
|
||||||
|
options = SwaggerUIOptions(swagger_ui_path="/docs")
|
||||||
|
|
||||||
|
app = App(__name__)
|
||||||
|
app = ConnexionMiddleware(app, swagger_ui_options=options)
|
||||||
|
app.add_api("openapi.yaml", swagger_ui_options=options):
|
||||||
|
|
||||||
|
For a description of all available options, check the :class:`.SwaggerUIOptions`
|
||||||
|
class.
|
||||||
|
|
||||||
|
.. dropdown:: View a detailed reference of the :code:`SwaggerUIOptions` class
|
||||||
|
:icon: eye
|
||||||
|
|
||||||
|
.. autoclass:: connexion.options.SwaggerUIOptions
|
||||||
@@ -105,7 +105,9 @@ should work, connexion comes with ``uvicorn`` as an extra:
|
|||||||
Smaller breaking changes
|
Smaller breaking changes
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
* The ``options`` argument has been renamed to ``swagger_ui_options``
|
* The ``options`` argument has been renamed to ``swagger_ui_options`` and now takes an instance
|
||||||
|
of the :class:`.SwaggerUIOptions`. The naming of the options themselves have been changed to
|
||||||
|
better represent their meaning.
|
||||||
* The ``uri_parser_class`` is now passed to the ``App`` or its ``add_api()`` method directly
|
* The ``uri_parser_class`` is now passed to the ``App`` or its ``add_api()`` method directly
|
||||||
instead of via the ``options`` argument.
|
instead of via the ``options`` argument.
|
||||||
* The ``jsonifier`` is now passed to the ``App`` or its ``add_api()`` method instead of setting it
|
* The ``jsonifier`` is now passed to the ``App`` or its ``add_api()`` method instead of setting it
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from connexion.exceptions import InvalidSpecification
|
|||||||
from connexion.http_facts import METHODS
|
from connexion.http_facts import METHODS
|
||||||
from connexion.json_schema import ExtendedSafeLoader
|
from connexion.json_schema import ExtendedSafeLoader
|
||||||
from connexion.middleware.abstract import AbstractRoutingAPI
|
from connexion.middleware.abstract import AbstractRoutingAPI
|
||||||
|
from connexion.options import SwaggerUIOptions
|
||||||
|
|
||||||
from conftest import TEST_FOLDER, build_app_from_fixture
|
from conftest import TEST_FOLDER, build_app_from_fixture
|
||||||
|
|
||||||
@@ -57,7 +58,7 @@ def test_swagger_ui(simple_api_spec_dir, spec):
|
|||||||
|
|
||||||
def test_swagger_ui_with_config(simple_api_spec_dir, spec):
|
def test_swagger_ui_with_config(simple_api_spec_dir, spec):
|
||||||
swagger_ui_config = {"displayOperationId": True}
|
swagger_ui_config = {"displayOperationId": True}
|
||||||
swagger_ui_options = {"swagger_ui_config": swagger_ui_config}
|
swagger_ui_options = SwaggerUIOptions(swagger_ui_config=swagger_ui_config)
|
||||||
app = App(
|
app = App(
|
||||||
__name__,
|
__name__,
|
||||||
specification_dir=simple_api_spec_dir,
|
specification_dir=simple_api_spec_dir,
|
||||||
@@ -72,7 +73,7 @@ def test_swagger_ui_with_config(simple_api_spec_dir, spec):
|
|||||||
|
|
||||||
|
|
||||||
def test_no_swagger_ui(simple_api_spec_dir, spec):
|
def test_no_swagger_ui(simple_api_spec_dir, spec):
|
||||||
swagger_ui_options = {"swagger_ui": False}
|
swagger_ui_options = SwaggerUIOptions(swagger_ui=False)
|
||||||
app = App(
|
app = App(
|
||||||
__name__,
|
__name__,
|
||||||
specification_dir=simple_api_spec_dir,
|
specification_dir=simple_api_spec_dir,
|
||||||
@@ -85,7 +86,7 @@ def test_no_swagger_ui(simple_api_spec_dir, spec):
|
|||||||
assert swagger_ui.status_code == 404
|
assert swagger_ui.status_code == 404
|
||||||
|
|
||||||
app2 = App(__name__, specification_dir=simple_api_spec_dir)
|
app2 = App(__name__, specification_dir=simple_api_spec_dir)
|
||||||
app2.add_api(spec, swagger_ui_options={"swagger_ui": False})
|
app2.add_api(spec, swagger_ui_options=SwaggerUIOptions(swagger_ui=False))
|
||||||
app2_client = app2.test_client()
|
app2_client = app2.test_client()
|
||||||
swagger_ui2 = app2_client.get("/v1.0/ui/")
|
swagger_ui2 = app2_client.get("/v1.0/ui/")
|
||||||
assert swagger_ui2.status_code == 404
|
assert swagger_ui2.status_code == 404
|
||||||
@@ -94,7 +95,7 @@ def test_no_swagger_ui(simple_api_spec_dir, spec):
|
|||||||
def test_swagger_ui_config_json(simple_api_spec_dir, spec):
|
def test_swagger_ui_config_json(simple_api_spec_dir, spec):
|
||||||
"""Verify the swagger-ui-config.json file is returned for swagger_ui_config option passed to app."""
|
"""Verify the swagger-ui-config.json file is returned for swagger_ui_config option passed to app."""
|
||||||
swagger_ui_config = {"displayOperationId": True}
|
swagger_ui_config = {"displayOperationId": True}
|
||||||
swagger_ui_options = {"swagger_ui_config": swagger_ui_config}
|
swagger_ui_options = SwaggerUIOptions(swagger_ui_config=swagger_ui_config)
|
||||||
app = App(
|
app = App(
|
||||||
__name__,
|
__name__,
|
||||||
specification_dir=simple_api_spec_dir,
|
specification_dir=simple_api_spec_dir,
|
||||||
@@ -142,7 +143,7 @@ def test_swagger_yaml_app(simple_api_spec_dir, spec):
|
|||||||
|
|
||||||
def test_no_swagger_json_app(simple_api_spec_dir, spec):
|
def test_no_swagger_json_app(simple_api_spec_dir, spec):
|
||||||
"""Verify the spec json file is not returned when set to False when creating app."""
|
"""Verify the spec json file is not returned when set to False when creating app."""
|
||||||
swagger_ui_options = {"serve_spec": False}
|
swagger_ui_options = SwaggerUIOptions(serve_spec=False)
|
||||||
app = App(
|
app = App(
|
||||||
__name__,
|
__name__,
|
||||||
specification_dir=simple_api_spec_dir,
|
specification_dir=simple_api_spec_dir,
|
||||||
@@ -193,7 +194,7 @@ def test_swagger_json_api(simple_api_spec_dir, spec):
|
|||||||
def test_no_swagger_json_api(simple_api_spec_dir, spec):
|
def test_no_swagger_json_api(simple_api_spec_dir, spec):
|
||||||
"""Verify the spec json file is not returned when set to False when adding api."""
|
"""Verify the spec json file is not returned when set to False when adding api."""
|
||||||
app = App(__name__, specification_dir=simple_api_spec_dir)
|
app = App(__name__, specification_dir=simple_api_spec_dir)
|
||||||
app.add_api(spec, swagger_ui_options={"serve_spec": False})
|
app.add_api(spec, swagger_ui_options=SwaggerUIOptions(serve_spec=False))
|
||||||
|
|
||||||
app_client = app.test_client()
|
app_client = app.test_client()
|
||||||
url = "/v1.0/{spec}".format(spec=spec.replace("yaml", "json"))
|
url = "/v1.0/{spec}".format(spec=spec.replace("yaml", "json"))
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
import importlib_metadata
|
|
||||||
import pytest
|
import pytest
|
||||||
from click.testing import CliRunner
|
from click.testing import CliRunner
|
||||||
from connexion.cli import main
|
from connexion.cli import main
|
||||||
@@ -9,6 +8,11 @@ from connexion.exceptions import ResolverError
|
|||||||
|
|
||||||
from conftest import FIXTURES_FOLDER
|
from conftest import FIXTURES_FOLDER
|
||||||
|
|
||||||
|
try:
|
||||||
|
import importlib_metadata
|
||||||
|
except ImportError:
|
||||||
|
import importlib.metadata as importlib_metadata
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
def mock_app_run(app_class, monkeypatch):
|
def mock_app_run(app_class, monkeypatch):
|
||||||
|
|||||||
Reference in New Issue
Block a user