Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Elements, RapiDoc, and RapiPDF #308

Merged
merged 3 commits into from
Jul 1, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Support Elements, RapiDoc, and RapiPDF
- Add support for Elements, RapiDoc, and RapiPDF.
- Add APIFlask(docs_ui) parameter to control the UI to use.
- Deprecate `/redoc` path and `redoc_path` parameter.
  • Loading branch information
greyli committed Jun 28, 2022
commit 3482b64584cedb2e82459425cd020a03ef56e3ed
47 changes: 37 additions & 10 deletions src/apiflask/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
from .openapi import get_argument
from .openapi import get_security_and_security_schemes
from .ui_templates import redoc_template
from .ui_templates import swagger_ui_template
from .ui_templates import ui_templates
from .ui_templates import swagger_ui_oauth2_redirect_template
from .scaffold import APIScaffold

Expand Down Expand Up @@ -267,6 +267,7 @@ def __init__(
spec_path: str = '/openapi.json',
docs_path: str = '/docs',
docs_oauth2_redirect_path: str = '/docs/oauth2-redirect',
docs_ui: str = 'swagger-ui',
redoc_path: str = '/redoc',
openapi_blueprint_url_prefix: t.Optional[str] = None,
json_errors: bool = True,
Expand All @@ -293,9 +294,12 @@ def __init__(
spec_path: The path to OpenAPI Spec documentation. It
defaults to `/openapi.json`, if the path ends with `.yaml`
or `.yml`, the YAML format of the OAS will be returned.
docs_path: The path to Swagger UI documentation, defaults to `/docs`.
docs_path: The path to API UI documentation, defaults to `/docs`.
docs_ui: The UI of API documentation, one of `swagger-ui` (default), `redoc`,
`elements`, `rapidoc`, and `rapipdf`.
docs_oauth2_redirect_path: The path to Swagger UI OAuth redirect.
redoc_path: The path to Redoc documentation, defaults to `/redoc`.
redoc_path: Deprecated since 1.1, set `APIFlask(docs_ui='redoc')` to use Redoc.
The path to Redoc documentation, defaults to `/redoc`.
openapi_blueprint_url_prefix: The url prefix of the OpenAPI blueprint. This
prefix will append before all the OpenAPI-related paths (`sepc_path`,
`docs_path`, etc.), defaults to `None`.
Expand All @@ -304,9 +308,13 @@ def __init__(

Other keyword arguments are directly passed to `flask.Flask`.

*Version changed: 1.1.0*

- Add `docs_ui` parameter.

*Version changed: 0.7.0*

- Add `openapi_blueprint_url_prefix` argument.
- Add `openapi_blueprint_url_prefix` parameter.
"""
super().__init__(
import_name,
Expand All @@ -327,6 +335,7 @@ def __init__(
self.title = title
self.version = version
self.spec_path = spec_path
self.docs_ui = docs_ui
self.docs_path = docs_path
self.redoc_path = redoc_path
self.docs_oauth2_redirect_path = docs_oauth2_redirect_path
Expand Down Expand Up @@ -539,6 +548,10 @@ def _register_openapi_blueprint(self) -> None:
The name of the blueprint is "openapi". This blueprint will hold the view
functions for spec file, Swagger UI and Redoc.

*Version changed: 1.1.0*

- Deprecate the Redoc view at /redoc path.

*Version changed: 0.7.0*

- The format of the spec now rely on the `SPEC_FORMAT` config.
Expand All @@ -560,23 +573,37 @@ def spec() -> ResponseReturnValueType:
{'Content-Type': self.config['YAML_SPEC_MIMETYPE']}

if self.docs_path:
if self.docs_ui not in ui_templates:
valid_values = list(ui_templates.keys())
raise ValueError(
f'Invalid docs_ui value, expected one of {valid_values}, got "{self.docs_ui}".'
)

@bp.route(self.docs_path)
def swagger_ui() -> str:
def docs() -> str:
return render_template_string(
swagger_ui_template,
ui_templates[self.docs_ui],
title=self.title,
version=self.version,
oauth2_redirect_path=self.docs_oauth2_redirect_path
)

if self.docs_oauth2_redirect_path:
@bp.route(self.docs_oauth2_redirect_path)
def swagger_ui_oauth_redirect() -> str:
return render_template_string(swagger_ui_oauth2_redirect_template)
if self.docs_ui == 'swagger-ui':
if self.docs_oauth2_redirect_path:
@bp.route(self.docs_oauth2_redirect_path)
def swagger_ui_oauth_redirect() -> str:
return render_template_string(swagger_ui_oauth2_redirect_template)

if self.redoc_path:
@bp.route(self.redoc_path)
def redoc() -> str:
warnings.warn(
'The `/redoc` path and `redoc_path` parameter is deprecated '
'and will be removed in 2.0, Set `APIFlask(docs_ui="redoc")` '
'to use Redoc and then visit "/docs" instead.',
UserWarning,
stacklevel=2,
)
return render_template_string(
redoc_template,
title=self.title,
Expand Down
15 changes: 12 additions & 3 deletions src/apiflask/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,22 @@
DOCS_FAVICON: str = 'https://apiflask.com/_assets/favicon.png'
REDOC_USE_GOOGLE_FONT: bool = True
REDOC_STANDALONE_JS: str = 'https://cdn.jsdelivr.net/npm/redoc@next/bundles/\
redoc.standalone.js'
redoc.standalone.js' # TODO: rename to REDOC_JS
REDOC_CONFIG: t.Optional[dict] = None
SWAGGER_UI_CSS: str = 'https://cdn.jsdelivr.net/npm/swagger-ui-dist@3/swagger-ui.css'
SWAGGER_UI_BUNDLE_JS: str = 'https://cdn.jsdelivr.net/npm/swagger-ui-dist@3/\
swagger-ui-bundle.js'
swagger-ui-bundle.js' # TODO: rename to SWAGGER_UI_JS
SWAGGER_UI_STANDALONE_PRESET_JS: str = 'https://cdn.jsdelivr.net/npm/swagger-ui-dist@3/\
swagger-ui-standalone-preset.js'
swagger-ui-standalone-preset.js' # TODO: rename to SWAGGER_UI_STANDALONE_JS
SWAGGER_UI_LAYOUT: str = 'BaseLayout'
SWAGGER_UI_CONFIG: t.Optional[dict] = None
SWAGGER_UI_OAUTH_CONFIG: t.Optional[dict] = None
ELEMENTS_JS: str = 'https://unpkg.com/@stoplight/elements/web-components.min.js'
ELEMENTS_CSS: str = 'https://unpkg.com/@stoplight/elements/styles.min.css'
ELEMENTS_LAYOUT: str = 'sidebar'
ELEMENTS_CONFIG: t.Optional[dict] = None
RAPIDOC_JS: str = 'https://unpkg.com/rapidoc/dist/rapidoc-min.js'
RAPIDOC_THEME: str = 'light'
RAPIDOC_CONFIG: t.Optional[dict] = None
RAPIPDF_JS: str = 'https://unpkg.com/rapipdf/dist/rapipdf-min.js'
RAPIPDF_CONFIG: t.Optional[dict] = None
88 changes: 88 additions & 0 deletions src/apiflask/ui_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,91 @@

</html>
"""

elements_template = """
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>{{ title }} {{ version }} - Elements</title>
<link rel="icon" type="image/png"
href="{{ config.DOCS_FAVICON }}">
<script src="{{ config.ELEMENTS_JS }}"></script>
<link rel="stylesheet" href="{{ config.ELEMENTS_CSS }}">
</head>
<body>

<elements-api
apiDescriptionUrl="{{ url_for('openapi.spec') }}"
layout="{{ config.ELEMENTS_LAYOUT }}"
{% if config.ELEMENTS_CONFIG %}
{% for key, value in config.ELEMENTS_CONFIG.items() %}
{{ key }}={{ value | tojson }}
{% endfor %}
{% endif %}
/>

</body>
</html>
"""

rapidoc_template = """
<!doctype html> <!-- Important: must specify -->
<html>
<head>
<meta charset="utf-8"> <!-- Important: rapi-doc uses utf8 characters -->
<title>{{ title }} {{ version }} - RapiDoc</title>
<link rel="icon" type="image/png"
href="{{ config.DOCS_FAVICON }}">
<script type="module" src="{{ config.RAPIDOC_JS }}"></script>
</head>
<body>
<rapi-doc
spec-url="{{ url_for('openapi.spec') }}"
theme="{{ config.RAPIDOC_THEME }}"
{% if config.RAPIDOC_CONFIG and 'show-header' in config.RAPIDOC_CONFIG %}
{% set show_header = config.RAPIDOC_CONFIG['show-header'] %}
{% else %}
{% set show_header = False %}
{% endif %}
show-header="{{ show_header | tojson }}"
{% if config.RAPIDOC_CONFIG %}
{% for key, value in config.RAPIDOC_CONFIG.items() %}
{{ key }}={{ value | tojson }}
{% endfor %}
{% endif %}
> </rapi-doc>
</body>
</html>
"""

rapipdf_template = """
<!doctype html>
<html>
<head>
<title>{{ title }} {{ version }} - RapiPDF</title>
<link rel="icon" type="image/png"
href="{{ config.DOCS_FAVICON }}">
<script src="{{ config.RAPIPDF_JS }}"></script>
</head>
<body>
<rapi-pdf
spec-url = "{{ url_for('openapi.spec') }}"
{% if config.RAPIPDF_CONFIG %}
{% for key, value in config.RAPIPDF_CONFIG.items() %}
{{ key }}={{ value | tojson }}
{% endfor %}
{% endif %}
> </rapi-pdf>
</body>
</html>
"""

ui_templates = {
'swagger-ui': swagger_ui_template,
'redoc': redoc_template,
'elements': elements_template,
'rapidoc': rapidoc_template,
'rapipdf': rapipdf_template,
}