Skip to content

Commit 3d3ce26

Browse files
committed
Support multiple versions swagger
1 parent d8cb658 commit 3d3ce26

File tree

3 files changed

+65
-11
lines changed

3 files changed

+65
-11
lines changed

flask_apispec/extension.py

+30-3
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,17 @@ def get_pet(pet_id):
3636
3737
:param Flask app: App associated with API documentation
3838
:param APISpec spec: apispec specification associated with API documentation
39+
:param bool support_multiple_version: support multiple version swaggers
40+
by register doc to 'swagger-{api_version}' url
3941
"""
4042

41-
def __init__(self, app=None):
43+
def __init__(self, app=None, support_multiple_version=False):
4244
self._deferred = []
4345
self.app = app
4446
self.view_converter = None
4547
self.resource_converter = None
4648
self.spec = None
49+
self.support_multiple_version = support_multiple_version
4750

4851
if app:
4952
self.init_app(app)
@@ -67,9 +70,15 @@ def _defer(self, callable, *args, **kwargs):
6770
if self.app:
6871
bound()
6972

73+
@property
74+
def blueprint_name(self):
75+
if self.support_multiple_version:
76+
return 'flask-apispec-' + self.spec.version
77+
return 'flask-apispec'
78+
7079
def add_swagger_routes(self):
7180
blueprint = flask.Blueprint(
72-
'flask-apispec',
81+
self.blueprint_name,
7382
__name__,
7483
static_folder='./static',
7584
template_folder='./templates',
@@ -78,19 +87,37 @@ def add_swagger_routes(self):
7887

7988
json_url = self.app.config.get('APISPEC_SWAGGER_URL', '/swagger/')
8089
if json_url:
90+
if self.support_multiple_version:
91+
json_url = self.make_url_with_suffix_version(json_url)
92+
8193
blueprint.add_url_rule(json_url, 'swagger-json', self.swagger_json)
8294

8395
ui_url = self.app.config.get('APISPEC_SWAGGER_UI_URL', '/swagger-ui/')
8496
if ui_url:
97+
if self.support_multiple_version:
98+
ui_url = self.make_url_with_suffix_version(ui_url)
8599
blueprint.add_url_rule(ui_url, 'swagger-ui', self.swagger_ui)
86100

87101
self.app.register_blueprint(blueprint)
88102

103+
def make_url_with_suffix_version(self, url):
104+
# adding version suffix
105+
if url.endswith('/'):
106+
url = url[:-1] + '-' + self.spec.version + '/'
107+
elif url.endswith('.json') or url.endswith('.html'):
108+
# support extension url
109+
url = url.replace('.json', '-' + self.spec.version + '.json') \
110+
.replace('.html', '-' + self.spec.version + '.html')
111+
else:
112+
url += '-' + self.spec.version
113+
return url
114+
89115
def swagger_json(self):
90116
return flask.jsonify(self.spec.to_dict())
91117

92118
def swagger_ui(self):
93-
return flask.render_template('swagger-ui.html')
119+
return flask.render_template('swagger-ui.html',
120+
blueprint_name=self.blueprint_name)
94121

95122
def register_existing_resources(self):
96123
for name, rule in self.app.view_functions.items():

flask_apispec/templates/swagger-ui.html

+8-8
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,19 @@
33
<head>
44
<meta charset="UTF-8">
55
<title>Swagger UI</title>
6-
<link rel="icon" type="image/png" href="{{ url_for('flask-apispec.static', filename='favicon-32x32.png') }}" sizes="32x32" />
7-
<link rel="icon" type="image/png" href="{{ url_for('flask-apispec.static', filename='favicon-16x16.png') }}" sizes="16x16" />
8-
<link href="{{ url_for('flask-apispec.static', filename='swagger-ui.css') }}" rel="stylesheet" type="text/css"/>
6+
<link rel="icon" type="image/png" href="{{ url_for(blueprint_name + '.static', filename='favicon-32x32.png') }}" sizes="32x32" />
7+
<link rel="icon" type="image/png" href="{{ url_for(blueprint_name + '.static', filename='favicon-16x16.png') }}" sizes="16x16" />
8+
<link href="{{ url_for(blueprint_name + '.static', filename='swagger-ui.css') }}" rel="stylesheet" type="text/css"/>
99
</head>
1010

1111
<body class="swagger-section">
1212
<div id="message-bar" class="swagger-ui-wrap" data-sw-translate>&nbsp;</div>
1313
<div id="swagger-ui-container" class="swagger-ui-wrap"></div>
14-
<script src="{{ url_for('flask-apispec.static', filename='swagger-ui-bundle.js') }}" type="text/javascript"></script>
15-
<script src="{{ url_for('flask-apispec.static', filename='swagger-ui-standalone-preset.js') }}" type="text/javascript"></script>
14+
<script src="{{ url_for(blueprint_name + '.static', filename='swagger-ui-bundle.js') }}" type="text/javascript"></script>
15+
<script src="{{ url_for(blueprint_name + '.static', filename='swagger-ui-standalone-preset.js') }}" type="text/javascript"></script>
1616
<script type="text/javascript">
1717
var ui = SwaggerUIBundle({
18-
url: "{{ url_for('flask-apispec.swagger-json') }}",
18+
url: "{{ url_for(blueprint_name + '.swagger-json') }}",
1919
dom_id: '#swagger-ui-container',
2020
deepLinking: true,
2121
presets: [
@@ -26,8 +26,8 @@
2626
SwaggerUIBundle.plugins.DownloadUrl
2727
],
2828
layout: "BaseLayout"
29-
})
30-
window.ui = ui
29+
});
30+
window.ui = ui;
3131
</script>
3232
</body>
3333

tests/test_extension.py

+27
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# -*- coding: utf-8 -*-
2+
import random
23

34
import pytest
45
from flask import Blueprint
@@ -98,3 +99,29 @@ def test_apispec_config(self, app):
9899
assert docs.spec.title == 'test-extension'
99100
assert docs.spec.version == '2.1'
100101
assert docs.spec.openapi_version == '2.0'
102+
103+
def test_make_url_with_suffix_version_with_version(self, app):
104+
app.config['APISPEC_VERSION'] = 'v2'
105+
docs = FlaskApiSpec(app, support_multiple_version=True)
106+
107+
assert docs.make_url_with_suffix_version('/swagger/') == '/swagger-v2/'
108+
assert docs.make_url_with_suffix_version('/swagger-ui/') == '/swagger-ui-v2/'
109+
assert docs.make_url_with_suffix_version('/swagger.json') == '/swagger-v2.json'
110+
assert docs.make_url_with_suffix_version('/swagger.html') == '/swagger-v2.html'
111+
assert docs.make_url_with_suffix_version('/swagger') == '/swagger-v2'
112+
113+
def test_urls_with_support_multiple_version(self, app, client):
114+
app.config['APISPEC_VERSION'] = 'v2'
115+
docs = FlaskApiSpec(app, support_multiple_version=True)
116+
res = client.get('/swagger-v2/')
117+
assert res.json == docs.spec.to_dict()
118+
client.get('/swagger-ui-v2/')
119+
120+
def test_support_multiple_version_by_changing_blueprint_name(self, app):
121+
version = 'v{}'.format(random.randint(1, 5))
122+
app.config['APISPEC_VERSION'] = version
123+
docs = FlaskApiSpec(app, support_multiple_version=True)
124+
assert docs.blueprint_name == 'flask-apispec-' + version
125+
126+
docs_without_support_multiple_version = FlaskApiSpec(app)
127+
assert docs_without_support_multiple_version.blueprint_name == 'flask-apispec'

0 commit comments

Comments
 (0)