diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ceceaa0a..24eafbb2 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -8,6 +8,7 @@ Current - Ensure that exceptions raised in error handler, including programming errors, are logged (:issue:`705`, :pr:`706`) - Import the ABCs from 'collections.abc' instead of 'collections' by default as it is deprecated since Python3.7, and in 3.8 it will stop working. Python2.7 is still supported though. +- Fix illegal characters in JSON references to model names (:issue:`651`) 0.13.0 (2019-08-12) ------------------- @@ -20,6 +21,7 @@ Current - Per route Swagger documentation for multiple routes on a ``Resource`` - Fix Swagger `duplicate mapping key` problem from conflicts between response codes given as string or integer (:issue`661`) + 0.12.1 (2018-09-28) ------------------- diff --git a/flask_restplus/swagger.py b/flask_restplus/swagger.py index 64511ef0..2c61af3d 100644 --- a/flask_restplus/swagger.py +++ b/flask_restplus/swagger.py @@ -21,6 +21,10 @@ from .utils import merge, not_none, not_none_sorted from ._http import HTTPStatus +try: + from urllib.parse import quote +except ImportError: + from urllib import quote #: Maps Flask/Werkzeug rooting types to Swagger ones PATH_TYPES = { @@ -51,7 +55,7 @@ def ref(model): '''Return a reference to model in definitions''' name = model.name if isinstance(model, ModelBase) else model - return {'$ref': '#/definitions/{0}'.format(name)} + return {'$ref': '#/definitions/{0}'.format(quote(name, safe=''))} def _v(value): diff --git a/tests/test_swagger.py b/tests/test_swagger.py index 9e583f55..d781334b 100644 --- a/tests/test_swagger.py +++ b/tests/test_swagger.py @@ -1669,6 +1669,33 @@ def get(self): } } + def test_model_with_non_uri_chars_in_name(self, api, client): + # name will be encoded as 'Person%2F%2F%3Flots%7B%7D%20of%20%26illegals%40%60' + name = 'Person//?lots{} of &illegals@`' + fields = api.model(name, { + }) + + @api.route('/model-bad-uri/') + class ModelBadUri(restplus.Resource): + @api.doc(model=fields) + def get(self): + return {} + + @api.response(201, "", model=name) + def post(self): + return {} + + data = client.get_specs() + + assert 'definitions' in data + assert name in data['definitions'] + + path = data['paths']['/model-bad-uri/'] + assert path['get']['responses']['200']['schema']['$ref'] == \ + '#/definitions/Person%2F%2F%3Flots%7B%7D%20of%20%26illegals%40%60' + assert path['post']['responses']['201']['schema']['$ref'] == \ + '#/definitions/Person%2F%2F%3Flots%7B%7D%20of%20%26illegals%40%60' + def test_marchal_decorator_with_code(self, api, client): fields = api.model('Person', { 'name': restplus.fields.String,