Flask-RESTPlus does not generate Swagger UI correctly
Swagger UI not generating interactive docs for resource
Code
Example code for images endpoint
import io
from flask import request, abort, send_file
from flask_restplus import Namespace, Resource, reqparse
from celery.result import AsyncResult
from scrapy_api.models import ImageTask, Image
from scrapy_api import errors
__all__ = ['image_namespace']
image_namespace = Namespace('/image/', description='Images endpoint')
put_arguments = reqparse.RequestParser()
put_arguments.add_argument('url', type=str, help='URL of web page.')
post_arguments = reqparse.RequestParser()
post_arguments.add_argument('task_id', type=str, help='Task id.')
get_arguments = reqparse.RequestParser()
get_arguments.add_argument('task_id', type=str, help='Task id.')
get_arguments.add_argument('img_id', type=int, help='Image id.')
@image_namespace.errorhandler(ImageTask.DoesNotExist)
def image_task_does_not_exist(error):
return errors.make_error_response(errors.TaskDoesNotExist())
@image_namespace.route('/')
class ImageAPI(Resource):
'''
Images endpoint
'''
def get_image(self, task_id, id):
task = ImageTask.objects(task_id=task_id, images__id=id).first()
image = next((img for img in task.images if img.id == id), None)
return image
@image_namespace.expect(put_arguments)
def put(self):
'''
Submit task to get images from page by url.
'''
json_data = image_namespace.payload
image = ImagePUTInput.load(json_data).data
image.save()
return ImagePUTOutput.dump(image)
@image_namespace.expect(post_arguments)
def post(self):
'''
Get status of submitted task.
'''
json_data = image_namespace.payload
task_id = json_data['task_id']
image = ImageTask.objects.get(task_id=task_id)
celery_task = AsyncResult(image.task_id)
image.task_status = celery_task.state
image.save()
return ImagePOSTOutput.dump(image)
@image_namespace.expect(get_arguments)
@image_namespace.produces(['image/jpeg'])
def get(self):
'''
Get image from task result.
'''
task_id = request.args.get('task_id')
img_id = int(request.args.get('img_id'))
image = self.get_image(task_id, img_id)
if not image:
abort(404)
return send_file(io.BytesIO(image.img),
mimetype='image/jpeg',
as_attachment=True,
attachment_filename='{}-{}-{}.jpg'.format(
'Image', task_id, id))
Repro Steps (if applicable)
- Add namespace to api
- Open Swagger UI in browser
Expected Behavior
Generates Swagger documentation with interactive UI
Actual Behavior
Endpoints appear in Swagger UI but detailed information about them is not displayed.
Error Messages/Stack Trace
Some errors related to React are appearing in browser console:
TypeError: e.parentNode is null
DOMLazyTree.js:67
React 128
replaceChildWithTree
dangerouslyReplaceNodeWithMarkup
_replaceNodeWithMarkup
_updateRenderedComponent
_performComponentUpdate
updateComponent
receiveComponent
receiveComponent
updateChildren
_reconcilerUpdateChildren
_updateChildren
updateChildren
_updateDOMChildren
updateComponent
receiveComponent
receiveComponent
_updateRenderedComponent
_performComponentUpdate
updateComponent
receiveComponent
receiveComponent
updateChildren
_reconcilerUpdateChildren
_updateChildren
updateChildren
_updateDOMChildren
updateComponent
receiveComponent
receiveComponent
_updateRenderedComponent
_performComponentUpdate
updateComponent
receiveComponent
receiveComponent
updateChildren
_reconcilerUpdateChildren
_updateChildren
updateChildren
_updateDOMChildren
updateComponent
receiveComponent
receiveComponent
_updateRenderedComponent
_performComponentUpdate
updateComponent
receiveComponent
receiveComponent
_updateRenderedComponent
_performComponentUpdate
updateComponent
receiveComponent
receiveComponent
_updateRenderedComponent
_performComponentUpdate
updateComponent
receiveComponent
receiveComponent
updateChildren
_reconcilerUpdateChildren
_updateChildren
updateChildren
_updateDOMChildren
updateComponent
receiveComponent
receiveComponent
_updateRenderedComponent
_performComponentUpdate
updateComponent
receiveComponent
receiveComponent
updateChildren
_reconcilerUpdateChildren
_updateChildren
updateChildren
_updateDOMChildren
updateComponent
receiveComponent
receiveComponent
_updateRenderedComponent
_performComponentUpdate
updateComponent
receiveComponent
receiveComponent
updateChildren
_reconcilerUpdateChildren
_updateChildren
updateChildren
_updateDOMChildren
updateComponent
receiveComponent
receiveComponent
_updateRenderedComponent
_performComponentUpdate
updateComponent
receiveComponent
receiveComponent
updateChildren
_reconcilerUpdateChildren
_updateChildren
updateChildren
_updateDOMChildren
updateComponent
receiveComponent
receiveComponent
_updateRenderedComponent
_performComponentUpdate
updateComponent
receiveComponent
receiveComponent
_updateRenderedComponent
_performComponentUpdate
updateComponent
receiveComponent
receiveComponent
_updateRenderedComponent
_performComponentUpdate
updateComponent
receiveComponent
receiveComponent
_updateRenderedComponent
_performComponentUpdate
updateComponent
receiveComponent
receiveComponent
_updateRenderedComponent
_performComponentUpdate
updateComponent
receiveComponent
TypeError: l is null
OperationContainer.jsx:64:26
TypeError: e.parentNode is null
DOMLazyTree.js:67
Environment
- Python version: 3.6.6
- Flask version: 1.1.0
- Flask-RESTPlus version: 0.13.0
- Browser: Mozilla Firefox 69.0 (64-bit)
Additional Context

I've checked code on previous version (0.12.1) and it works perfectly fine.
could you describe how you installed flask-restplus?
Asking because we don't have a version 0.13.3 anywhere. Last release release on PyPi was 0.13.0. Current master is on 0.13.1-dev
Just wondering if it's a typo or you're on someone else's fork?
@j5awry I'm sorry. I made a typo. It's 0.13.0. I've updated issue.
For simpler bug reproduction we can use this code:
from flask_restplus import Namespace, Resource
ping_namespace = Namespace(
'/ping/', description='Endpoint for checking service availability.')
@ping_namespace.route('')
class PingAPI(Resource):
'''
Ping endpoint.
'''
def get(self):
return {'message': 'OK'}
This should make Swagger UI with GET section inside ping namespace. But in current version (0.13.0) it's not displayed.
I'll need to know more about your environment. My guess is there are missing assets from Swagger. I set up my environment using the task in flask-restplus' codeline to get all the Swagger base assets:
https://github.com/noirbizarre/flask-restplus/blob/master/tasks.py#L180
def assets(ctx):
'''Fetch web assets'''
header(assets.__doc__)
with ctx.cd(ROOT):
ctx.run('npm install')
ctx.run('mkdir -p flask_restplus/static')
ctx.run('cp node_modules/swagger-ui-dist/{swagger-ui*.{css,js}{,.map},favicon*.png,oauth2-redirect.html} 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/')
With that in mind, and knowing my local running environment (from the flask-restplus codeline from Git, after running inv assets), I did the following, and was unable to reproduce
- create fresh virtualenv of python 3.6.6
- pip install flask-restplus
- run our todo.py demo: https://github.com/noirbizarre/flask-restplus/blob/master/examples/todo.py
pip freeze contents:
aniso8601==8.0.0
attrs==19.2.0
Click==7.0
Flask==1.1.1
flask-restplus==0.13.0
invoke==1.3.0
itsdangerous==1.1.0
Jinja2==2.10.1
jsonschema==3.0.2
MarkupSafe==1.1.1
pyrsistent==0.15.4
pytz==2019.2
six==1.12.0
Werkzeug==0.16.0
(invoke was added so i could quickly run the demo without trying)
Considering it's React errors, I'd assume it's missing some Swagger assets somewhere.
Did you tested this on 0.13.0 version?
I've installed Flask-RESTPlus using pipenv and it automatically fetched latest version:
$ pipenv install flask-restplus
Here is the part of code where I add namespaces:
def register_blueprints(app):
'''
Register blueprints for app.
'''
blueprint = Blueprint('api', __name__, url_prefix='/api')
api = Api(blueprint,
title=app.config.app_name,
version=app.config.app_version,
description=app.config.app_descr)
# Error handlers
@blueprint.app_errorhandler(404)
def path_not_found(error):
return errors.make_error_response(errors.Error404())
@api.errorhandler
def default_error_handler(error):
return errors.make_error_response(error)
# Adding namespaces
api.add_namespace(ping_namespace, '/ping')
api.add_namespace(document_namespace, '/document')
api.add_namespace(image_namespace, '/image')
app.register_blueprint(blueprint)
Result of pip freeze:
amqp==2.5.1
aniso8601==8.0.0
appdirs==1.4.3
astroid==2.3.1
atomicwrites==1.3.0
attrs==19.2.0
beautifulsoup4==4.8.0
billiard==3.6.1.0
blinker==1.4
bs4==0.0.1
celery==4.3.0
certifi==2019.9.11
chardet==3.0.4
Click==7.0
cssselect==1.1.0
dynaconf==2.1.1
fake-useragent==0.1.11
fancycompleter==0.8
Flask==1.1.1
flask-mongoengine==0.9.5
flask-restplus==0.13.0
Flask-WTF==0.14.2
gunicorn==19.9.0
idna==2.8
importlib-metadata==0.23
isort==4.3.21
itsdangerous==1.1.0
Jinja2==2.10.1
jsonschema==3.0.2
kombu==4.6.5
lazy-object-proxy==1.4.2
lxml==4.4.1
MarkupSafe==1.1.1
mccabe==0.6.1
mongoengine==0.18.2
more-itertools==7.2.0
packaging==19.2
parse==1.12.1
pdbpp==0.10.0
pluggy==0.13.0
py==1.8.0
pyee==6.0.0
Pygments==2.4.2
pylint==2.4.2
pymongo==3.9.0
pyparsing==2.4.2
pyppeteer==0.0.25
pyquery==1.4.0
pyrsistent==0.15.4
pytest==5.2.0
python-box==3.4.5
python-dotenv==0.10.3
pytz==2019.2
PyYAML==5.1.2
redis==3.3.8
requests==2.22.0
requests-html==0.10.0
rope==0.14.0
six==1.12.0
soupsieve==1.9.4
toml==0.10.0
tqdm==4.36.1
typed-ast==1.4.0
urllib3==1.25.6
vine==1.3.0
w3lib==1.21.0
wcwidth==0.1.7
websockets==8.0.2
Werkzeug==0.16.0
wmctrl==0.3
wrapt==1.11.2
WTForms==2.2.1
yapf==0.28.0
zipp==0.6.0
i tested on 0.13.0. As stated, if you're seeing errors in the browser related to React, then it's probably an issue with missing assets. You'll need to NPM install the following depenedencies to get the Swagger UI running
"dependencies": {
"swagger-ui-dist": "^3.4.0",
"typeface-droid-sans": "0.0.40"
}
These can be found in https://github.com/noirbizarre/flask-restplus/blob/master/package.json
@j5awry I've tried to copy and run example you've mentioned earlier and it works ok. But my code don't work with current version
@deepaerial I have tried to reproduce this issue using your example code and I'm unable to do so. Could you please try creating a clean Python environment, reinstalling the minimum dependencies to get the example you provided working and try to reproduce?
@SteadBytes I've completely removed virtual environment and installed project again. Unfortuanatelly, nothing changed. Here is repo.
Installed version from Pipfile.lock:
"flask-restplus": {
"hashes": [
"sha256:a15d251923a8feb09a5d805c2f4d188555910a42c64d58f7dd281b8cac095f1b",
"sha256:a66e442d0bca08f389fc3d07b4d808fc89961285d12fb8013f7cf15516fa9f5c"
],
"index": "pypi",
"version": "==0.13.0"
I have the same problem using Flask-RESTPlus version: 0.12.1 and 0.13.0