Skip to content

Commit

Permalink
Upgrade to Swagger UI 3.4.0 (fix python-restx#338)
Browse files Browse the repository at this point in the history
  • Loading branch information
noirbizarre committed Oct 24, 2017
1 parent 9b27879 commit 962a100
Show file tree
Hide file tree
Showing 12 changed files with 130 additions and 158 deletions.
3 changes: 0 additions & 3 deletions .bowerrc

This file was deleted.

3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,5 @@ prof/
doc/_build/

# Specifics
flask_restplus/static/bower
flask_restplus/static
node_modules
4 changes: 0 additions & 4 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,4 @@ include README.rst CHANGELOG.rst MANIFEST.in LICENSE
recursive-include flask_restplus *
recursive-include requirements *.pip

prune flask_restplus/static/bower

recursive-include flask_restplus/static/bower/swagger-ui/dist *

global-exclude *.pyc
28 changes: 0 additions & 28 deletions bower.json

This file was deleted.

20 changes: 3 additions & 17 deletions doc/swagger.rst
Original file line number Diff line number Diff line change
Expand Up @@ -898,33 +898,19 @@ setting (``'none'``, ``'list'`` or ``'full'``):
api = Api(app)
You can enable a JSON editor in Swagger UI by setting ``config.SWAGGER_UI_JSONEDITOR`` to ``True``:
By default, operation ID is hidden as well as request duration, you can enable them respectively with:

.. code-block:: python
from flask import Flask
from flask_restplus import Api
app = Flask(__name__)
app.config.SWAGGER_UI_JSONEDITOR = True
app.config.SWAGGER_UI_OPERATION_ID = True
app.config.SWAGGER_UI_REQUEST_DURATION = True
api = Api(app)
It also support optionnal translations through ``config.SWAGGER_UI_LANGUAGES``:

.. code-block:: python
from flask import Flask
from flask_restplus import Api
app = Flask(__name__)
app.config.SWAGGER_UI_LANGUAGES = ['en', 'fr']
api = Api(app)
See `the official documentation <https://github.com/swagger-api/swagger-ui#user-content-localization-and-translation>`_
for more details.

If you need a custom UI,
you can register a custom view function with the :meth:`~Api.documentation` decorator:
Expand Down
3 changes: 1 addition & 2 deletions flask_restplus/apidoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ def register(self, *args, **kwargs):

@apidoc.add_app_template_global
def swagger_static(filename):
return url_for('restplus_doc.static',
filename='bower/swagger-ui/dist/{0}'.format(filename))
return url_for('restplus_doc.static', filename=filename)


def ui_for(api):
Expand Down
24 changes: 23 additions & 1 deletion flask_restplus/templates/swagger-ui-css.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<link media="screen" rel="stylesheet" type="text/css"
{#<link media="screen" rel="stylesheet" type="text/css"
href="{{ swagger_static('css/typography.css') }}" />
<link media="screen" rel="stylesheet" type="text/css"
href="{{ swagger_static('css/reset.css') }}" />
Expand All @@ -8,3 +8,25 @@
href="{{ swagger_static('css/reset.css') }}"/>
<link media="print" rel="stylesheet" type="text/css"
href="{{ swagger_static('css/screen.css') }}" />
#}
<link media="screen" rel="stylesheet" type="text/css" href="{{ swagger_static('droid-sans.css') }}" />
<link media="screen" rel="stylesheet" type="text/css" href="{{ swagger_static('swagger-ui.css') }}" />
<style>
html
{
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}
*,
*:before,
*:after
{
box-sizing: inherit;
}

body {
margin:0;
background: #fafafa;
}
</style>
25 changes: 4 additions & 21 deletions flask_restplus/templates/swagger-ui-libs.html
Original file line number Diff line number Diff line change
@@ -1,28 +1,11 @@
<script src="{{ swagger_static('lib/object-assign-pollyfill.js') }}" type="text/javascript"></script>
<script src="{{ swagger_static('lib/jquery-1.8.0.min.js') }}" type="text/javascript"></script>
<script src="{{ swagger_static('lib/jquery.slideto.min.js') }}" type="text/javascript"></script>
<script src="{{ swagger_static('lib/jquery.wiggle.min.js') }}" type="text/javascript"></script>
<script src="{{ swagger_static('lib/jquery.ba-bbq.min.js') }}" type="text/javascript"></script>
<script src="{{ swagger_static('lib/handlebars-4.0.5.js') }}" type="text/javascript"></script>
<script src="{{ swagger_static('lib/lodash.min.js') }}" type="text/javascript"></script>
<script src="{{ swagger_static('lib/backbone-min.js') }}" type="text/javascript"></script>
{% if config.DEBUG %}
<script src="{{ swagger_static('swagger-ui.js') }}" type="text/javascript"></script>
{% else %}
<script src="{{ swagger_static('swagger-ui.min.js') }}" type="text/javascript"></script>
{% endif %}
<script src="{{ swagger_static('lib/highlight.9.1.0.pack.js') }}" type="text/javascript"></script>
<script src="{{ swagger_static('lib/highlight.9.1.0.pack_extended.js') }}" type="text/javascript"></script>
<script src="{{ swagger_static('lib/jsoneditor.min.js') }}" type='text/javascript'></script>
<script src="{{ swagger_static('lib/marked.js') }}" type="text/javascript"></script>

<!-- enabling this will enable oauth2 implicit scope support -->
<script src="{{ swagger_static('lib/swagger-oauth.js') }}" type="text/javascript"></script>

{#
{% if config.SWAGGER_UI_LANGUAGES %}
<script src="{{ swagger_static('lang/translator.js') }}" type='text/javascript'></script>
{% for lang in config.SWAGGER_UI_LANGUAGES %}
{% set filename = 'lang/{0}.js'.format(lang) %}
<script src="{{ swagger_static(filename) }}" type='text/javascript'></script>
{% endfor%}
{% endif %}
#}
<script src="{{ swagger_static('swagger-ui-bundle.js') }}"></script>
<script src="{{ swagger_static('swagger-ui-standalone-preset.js') }}"></script>
119 changes: 64 additions & 55 deletions flask_restplus/templates/swagger-ui.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,67 +3,76 @@
<head>
<title>{{ title }}</title>
{% include 'swagger-ui-css.html' %}
{% include 'swagger-ui-libs.html' %}
<script type="text/javascript">
$(function () {
// Performance tuning for Highlight.js
hljs.configure({
highlightSizeThreshold: 5000
});

{% if config.SWAGGER_UI_LANGUAGES %}
// Pre load translate...
if(window.SwaggerTranslator) {
window.SwaggerTranslator.translate();
}
{% endif %}
<link rel="icon" type="image/png" href="{{ swagger_static('favicon-32x32.png') }}" sizes="32x32" />
<link rel="icon" type="image/png" href="{{ swagger_static('favicon-16x16.png') }}" sizes="16x16" />

window.swaggerUi = new SwaggerUi({
url: "{{ specs_url }}",
validatorUrl: "{{ config.SWAGGER_VALIDATOR_URL }}" || null,
dom_id: "swagger-ui-container",
supportedSubmitMethods: ['get', 'post', 'put', 'delete', 'patch'],
onComplete: function(swaggerApi, swaggerUi){
if(typeof initOAuth == "function") {
{% if config.SWAGGER_UI_OAUTH_CLIENT_ID -%}
initOAuth({
clientId: "{{ config.SWAGGER_UI_OAUTH_CLIENT_ID }}",
realm: "{{ config.SWAGGER_UI_OAUTH_REALM }}",
appName: "{{ config.SWAGGER_UI_OAUTH_APP_NAME }}"
});
{%- endif %}
}
</head>

<body>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position:absolute;width:0;height:0">
<defs>
<symbol viewBox="0 0 20 20" id="unlocked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V6h2v-.801C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8z"></path>
</symbol>

<symbol viewBox="0 0 20 20" id="locked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8zM12 8H8V5.199C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8z"/>
</symbol>

<symbol viewBox="0 0 20 20" id="close">
<path d="M14.348 14.849c-.469.469-1.229.469-1.697 0L10 11.819l-2.651 3.029c-.469.469-1.229.469-1.697 0-.469-.469-.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-.469-.469-.469-1.228 0-1.697.469-.469 1.228-.469 1.697 0L10 8.183l2.651-3.031c.469-.469 1.228-.469 1.697 0 .469.469.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c.469.469.469 1.229 0 1.698z"/>
</symbol>

<symbol viewBox="0 0 20 20" id="large-arrow">
<path d="M13.25 10L6.109 2.58c-.268-.27-.268-.707 0-.979.268-.27.701-.27.969 0l7.83 7.908c.268.271.268.709 0 .979l-7.83 7.908c-.268.271-.701.27-.969 0-.268-.269-.268-.707 0-.979L13.25 10z"/>
</symbol>

<symbol viewBox="0 0 20 20" id="large-arrow-down">
<path d="M17.418 6.109c.272-.268.709-.268.979 0s.271.701 0 .969l-7.908 7.83c-.27.268-.707.268-.979 0l-7.908-7.83c-.27-.268-.27-.701 0-.969.271-.268.709-.268.979 0L10 13.25l7.418-7.141z"/>
</symbol>

{% if config.SWAGGER_UI_LANGUAGES %}
if (window.SwaggerTranslator) {
window.SwaggerTranslator.translate();
}
{% endif %}

$('pre code').each(function(i, e) {
hljs.highlightBlock(e)
});
},
onFailure: function(data) {
log("Unable to Load SwaggerUI");
},
jsonEditor: {{ config.SWAGGER_UI_JSONEDITOR | default(False) | string | lower }},
<symbol viewBox="0 0 24 24" id="jump-to">
<path d="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z"/>
</symbol>

<symbol viewBox="0 0 24 24" id="expand">
<path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>
</symbol>

</defs>
</svg>

<div id="swagger-ui"></div>

{% include 'swagger-ui-libs.html' %}
<script type="text/javascript">
window.onload = function() {
const ui = window.ui = new SwaggerUIBundle({
url: "{{ specs_url }}",
validatorUrl: "{{ config.SWAGGER_VALIDATOR_URL }}" || null,
dom_id: "#swagger-ui",
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset.slice(1) // No Topbar
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
displayOperationId: {{ config.SWAGGER_UI_OPERATION_ID|default(False)|tojson }},
displayRequestDuration: {{ config.SWAGGER_UI_REQUEST_DURATION|default(False)|tojson }},
docExpansion: "{{ config.SWAGGER_UI_DOC_EXPANSION | default('none') }}"
});
window.swaggerUi.load();
})

function log() {
if ('console' in window) {
console.log.apply(console, arguments);
}
}
});
{% if config.SWAGGER_UI_OAUTH_CLIENT_ID -%}
ui.initOAuth({
clientId: "{{ config.SWAGGER_UI_OAUTH_CLIENT_ID }}",
realm: "{{ config.SWAGGER_UI_OAUTH_REALM }}",
appName: "{{ config.SWAGGER_UI_OAUTH_APP_NAME }}"
})
{%- endif %}
}
</script>
</head>

<body class="swagger-section">
<div class="swagger-ui-wrap"><div id="auth_container"></div></div>
<div id="message-bar" class="swagger-ui-wrap">&nbsp;</div>
<div id="swagger-ui-container" class="swagger-ui-wrap"></div>
</body>
</html>
16 changes: 16 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "flask-restplus",
"version": "0.10.1",
"description": "Fully featured framework for fast, easy and documented API development with Flask",
"repository": "noirbizarre/flask-restplus",
"keywords": [
"swagger",
"flask"
],
"author": "Axel Haustant",
"license": "ISC",
"dependencies": {
"swagger-ui-dist": "^3.4.0",
"typeface-droid-sans": "0.0.40"
}
}
6 changes: 5 additions & 1 deletion tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,11 @@ def assets(ctx):
'''Fetch web assets'''
header(assets.__doc__)
with ctx.cd(ROOT):
ctx.run('bower install')
ctx.run('npm install')
ctx.run('cp node_modules/swagger-ui-dist/{swagger-ui*.{css,js}{,.map},favicon*.png} flask_restplus/static')
# Until next release we need to install droid sans separately
ctx.run('cp node_modules/typeface-droid-sans/index.css flask_restplus/static/droid-sans.css')
ctx.run('cp -R node_modules/typeface-droid-sans/files flask_restplus/static/')


@task
Expand Down
37 changes: 12 additions & 25 deletions tests/test_apidoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,46 +63,33 @@ def test_apidoc_doc_expansion_parameter(self, app, client):
response = client.get(url_for('doc'))
assert 'docExpansion: "full"' in str(response.data)

def test_apidoc_doc_jsoneditor_parameter(self, app, client):
def test_apidoc_doc_display_operation_id(self, app, client):
restplus.Api(app)

response = client.get(url_for('doc'))
assert 'jsonEditor: false' in str(response.data)
assert 'displayOperationId: false' in str(response.data)

app.config['SWAGGER_UI_JSONEDITOR'] = False
app.config['SWAGGER_UI_OPERATION_ID'] = False
response = client.get(url_for('doc'))
assert 'jsonEditor: false' in str(response.data)
assert 'displayOperationId: false' in str(response.data)

app.config['SWAGGER_UI_JSONEDITOR'] = True
app.config['SWAGGER_UI_OPERATION_ID'] = True
response = client.get(url_for('doc'))
assert 'jsonEditor: true' in str(response.data)
assert 'displayOperationId: true' in str(response.data)

def test_apidoc_doc_language_parameter(self, app, client):
def test_apidoc_doc_display_request_duration(self, app, client):
restplus.Api(app)

response = client.get(url_for('doc'))
assert 'lang/translator.js' not in str(response.data)
assert 'lang/en.js' not in str(response.data)
assert 'lang/fr.js' not in str(response.data)
assert 'window.SwaggerTranslator' not in str(response.data)
assert 'displayRequestDuration: false' in str(response.data)

app.config['SWAGGER_UI_LANGUAGES'] = ['en', 'fr']
app.config['SWAGGER_UI_REQUEST_DURATION'] = False
response = client.get(url_for('doc'))
assert 'lang/translator.js' in str(response.data)
assert 'lang/en.js' in str(response.data)
assert 'lang/fr.js' in str(response.data)
assert 'window.SwaggerTranslator' in str(response.data)
assert 'displayRequestDuration: false' in str(response.data)

def test_apidoc_doc_minified(self, app, client):
restplus.Api(app)

app.config['DEBUG'] = True
response = client.get(url_for('doc'))
assert 'swagger-ui.js' in str(response.data)

app.config['DEBUG'] = False
app.config['SWAGGER_UI_REQUEST_DURATION'] = True
response = client.get(url_for('doc'))
assert 'swagger-ui.min.js' in str(response.data)
assert 'displayRequestDuration: true' in str(response.data)

def test_custom_apidoc_url(self, app, client):
restplus.Api(app, version='1.0', doc='/doc/')
Expand Down

0 comments on commit 962a100

Please sign in to comment.