commit
7683a28816
|
|
@ -11,6 +11,7 @@ coverage:
|
||||||
default:
|
default:
|
||||||
enabled: yes
|
enabled: yes
|
||||||
target: auto
|
target: auto
|
||||||
|
threshold: 0%
|
||||||
if_no_uploads: error
|
if_no_uploads: error
|
||||||
if_ci_failed: error
|
if_ci_failed: error
|
||||||
|
|
||||||
|
|
@ -18,7 +19,7 @@ coverage:
|
||||||
default:
|
default:
|
||||||
enabled: yes
|
enabled: yes
|
||||||
target: 80%
|
target: 80%
|
||||||
threshold: 60%
|
threshold: 0%
|
||||||
if_no_uploads: error
|
if_no_uploads: error
|
||||||
if_ci_failed: error
|
if_ci_failed: error
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
* text=auto
|
||||||
|
*.sh text eol=lf
|
||||||
|
|
@ -40,3 +40,8 @@ after_success:
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- master
|
- master
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
email:
|
||||||
|
on_success: always
|
||||||
|
on_failure: always
|
||||||
|
|
|
||||||
|
|
@ -359,7 +359,7 @@ https://drf-yasg.readthedocs.io/en/latest/
|
||||||
:alt: Codecov
|
:alt: Codecov
|
||||||
|
|
||||||
.. |pypi-version| image:: https://img.shields.io/pypi/v/drf-yasg.svg
|
.. |pypi-version| image:: https://img.shields.io/pypi/v/drf-yasg.svg
|
||||||
:target: https://pypi.org/project/drf-yasg/
|
:target: https://pypi.python.org/pypi/drf-yasg/
|
||||||
:alt: PyPI
|
:alt: PyPI
|
||||||
|
|
||||||
.. |rtd-badge| image:: https://img.shields.io/readthedocs/drf-yasg.svg
|
.. |rtd-badge| image:: https://img.shields.io/readthedocs/drf-yasg.svg
|
||||||
|
|
|
||||||
|
|
@ -3,20 +3,31 @@ Changelog
|
||||||
#########
|
#########
|
||||||
|
|
||||||
|
|
||||||
|
*********
|
||||||
|
**1.0.5**
|
||||||
|
*********
|
||||||
|
|
||||||
|
- **FIXED:** fixed a crash caused by having read-only Serializers nested by reference
|
||||||
|
- **FIXED:** removed erroneous backslashes in paths when routes are generated using Django 2
|
||||||
|
`path() <https://docs.djangoproject.com/en/2.0/ref/urls/#django.urls.path>`_
|
||||||
|
- **IMPROVED:** updated ``swagger-ui`` to version 3.7.0
|
||||||
|
- **IMPROVED:** ``FileField`` is now generated as an URL or file name in response Schemas
|
||||||
|
(:pr:`21`, thanks to :ghuser:`h-hirokawa`)
|
||||||
|
|
||||||
*********
|
*********
|
||||||
**1.0.4**
|
**1.0.4**
|
||||||
*********
|
*********
|
||||||
|
|
||||||
- **FIX:** fixed improper generation of YAML references
|
- **FIXED:** fixed improper generation of YAML references
|
||||||
- **FEATURE:** added ``query_serializer`` parameter to
|
- **ADDED:** added ``query_serializer`` parameter to
|
||||||
:func:`@swagger_auto_schema <.swagger_auto_schema>` (:issue:`16`, :pr:`17`)
|
:func:`@swagger_auto_schema <.swagger_auto_schema>` (:issue:`16`, :pr:`17`)
|
||||||
|
|
||||||
*********
|
*********
|
||||||
**1.0.3**
|
**1.0.3**
|
||||||
*********
|
*********
|
||||||
|
|
||||||
- **FIX:** fixed bug that caused schema views returned from cache to fail (:issue:`14`)
|
- **FIXED:** fixed bug that caused schema views returned from cache to fail (:issue:`14`)
|
||||||
- **FIX:** disabled automatic generation of response schemas for form operations to avoid confusing errors caused by
|
- **FIXED:** disabled automatic generation of response schemas for form operations to avoid confusing errors caused by
|
||||||
attempting to shove file parameters into Schema objects
|
attempting to shove file parameters into Schema objects
|
||||||
|
|
||||||
*********
|
*********
|
||||||
|
|
|
||||||
55
docs/conf.py
55
docs/conf.py
|
|
@ -4,6 +4,7 @@
|
||||||
# drf-yasg documentation build configuration file, created by
|
# drf-yasg documentation build configuration file, created by
|
||||||
# sphinx-quickstart on Sun Dec 10 15:20:34 2017.
|
# sphinx-quickstart on Sun Dec 10 15:20:34 2017.
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import sphinx_rtd_theme
|
import sphinx_rtd_theme
|
||||||
|
|
@ -168,6 +169,7 @@ nitpick_ignore = [
|
||||||
|
|
||||||
('py:class', 'ruamel.yaml.dumper.SafeDumper'),
|
('py:class', 'ruamel.yaml.dumper.SafeDumper'),
|
||||||
('py:class', 'rest_framework.renderers.BaseRenderer'),
|
('py:class', 'rest_framework.renderers.BaseRenderer'),
|
||||||
|
('py:class', 'rest_framework.schemas.generators.EndpointEnumerator'),
|
||||||
('py:class', 'rest_framework.views.APIView'),
|
('py:class', 'rest_framework.views.APIView'),
|
||||||
|
|
||||||
('py:class', 'OpenAPICodecYaml'),
|
('py:class', 'OpenAPICodecYaml'),
|
||||||
|
|
@ -215,40 +217,59 @@ drf_yasg.views.SchemaView = drf_yasg.views.get_schema_view(None)
|
||||||
|
|
||||||
# custom interpreted role for linking to GitHub issues and pull requests
|
# custom interpreted role for linking to GitHub issues and pull requests
|
||||||
# use as :issue:`14` or :pr:`17`
|
# use as :issue:`14` or :pr:`17`
|
||||||
gh_issue_uri = "https://github.com/axnsan12/drf-yasg/issues/%d"
|
gh_issue_uri = "https://github.com/axnsan12/drf-yasg/issues/{}"
|
||||||
gh_pr_uri = "https://github.com/axnsan12/drf-yasg/pull/%d"
|
gh_pr_uri = "https://github.com/axnsan12/drf-yasg/pull/{}"
|
||||||
|
gh_user_uri = "https://github.com/{}"
|
||||||
|
|
||||||
|
|
||||||
|
def sphinx_err(inliner, lineno, rawtext, msg):
|
||||||
|
msg = inliner.reporter.error(msg, line=lineno)
|
||||||
|
prb = inliner.problematic(rawtext, rawtext, msg)
|
||||||
|
return [prb], [msg]
|
||||||
|
|
||||||
|
|
||||||
|
def sphinx_ref(options, rawtext, text, ref):
|
||||||
|
set_classes(options)
|
||||||
|
node = nodes.reference(rawtext, text, refuri=ref, **options)
|
||||||
|
return [node], []
|
||||||
|
|
||||||
|
|
||||||
|
def role_github_user(name, rawtext, text, lineno, inliner, options=None, content=None):
|
||||||
|
options = options or {}
|
||||||
|
content = content or []
|
||||||
|
|
||||||
|
if not re.match(r"^[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,38}$", text):
|
||||||
|
return sphinx_err(inliner, lineno, rawtext, '"%s" is not a valid GitHub username.' % text)
|
||||||
|
|
||||||
|
ref = gh_user_uri.format(text)
|
||||||
|
text = '@' + utils.unescape(text)
|
||||||
|
return sphinx_ref(options, rawtext, text, ref)
|
||||||
|
|
||||||
|
|
||||||
def role_github_pull_request_or_issue(name, rawtext, text, lineno, inliner, options=None, content=None):
|
def role_github_pull_request_or_issue(name, rawtext, text, lineno, inliner, options=None, content=None):
|
||||||
options = options or {}
|
options = options or {}
|
||||||
content = content or []
|
content = content or []
|
||||||
try:
|
try:
|
||||||
ghid = int(text)
|
if int(text) <= 0:
|
||||||
if ghid <= 0:
|
|
||||||
raise ValueError
|
raise ValueError
|
||||||
except ValueError:
|
except ValueError:
|
||||||
msg = inliner.reporter.error(
|
return sphinx_err(
|
||||||
'GitHub pull request or issue number must be a number greater than or equal to 1; '
|
inliner, lineno, rawtext,
|
||||||
'"%s" is invalid.' % text, line=lineno
|
'GitHub pull request or issue number must be a number greater than or equal to 1; "%s" is invalid.' % text
|
||||||
)
|
)
|
||||||
prb = inliner.problematic(rawtext, rawtext, msg)
|
|
||||||
return [prb], [msg]
|
|
||||||
# Base URL mainly used by inliner.rfc_reference, so this is correct:
|
|
||||||
|
|
||||||
if name == 'pr':
|
if name == 'pr':
|
||||||
ref = gh_pr_uri
|
ref = gh_pr_uri
|
||||||
elif name == 'issue':
|
elif name == 'issue':
|
||||||
ref = gh_issue_uri
|
ref = gh_issue_uri
|
||||||
else:
|
else:
|
||||||
msg = inliner.reporter.error('unknown tag name for GitHub reference - "%s"' % name, line=lineno)
|
return sphinx_err(inliner, lineno, rawtext, 'unknown role name for GitHub reference - "%s"' % name)
|
||||||
prb = inliner.problematic(rawtext, rawtext, msg)
|
|
||||||
return [prb], [msg]
|
|
||||||
|
|
||||||
ref = ref % ghid
|
ref = ref.format(text)
|
||||||
set_classes(options)
|
text = '#' + utils.unescape(text)
|
||||||
node = nodes.reference(rawtext, '#' + utils.unescape(text), refuri=ref, **options)
|
return sphinx_ref(options, rawtext, text, ref)
|
||||||
return [node], []
|
|
||||||
|
|
||||||
|
|
||||||
roles.register_local_role('pr', role_github_pull_request_or_issue)
|
roles.register_local_role('pr', role_github_pull_request_or_issue)
|
||||||
roles.register_local_role('issue', role_github_pull_request_or_issue)
|
roles.register_local_role('issue', role_github_pull_request_or_issue)
|
||||||
|
roles.register_local_role('ghuser', role_github_user)
|
||||||
|
|
|
||||||
|
|
@ -158,7 +158,9 @@ Where you can use the :func:`@swagger_auto_schema <.swagger_auto_schema>` decora
|
||||||
test_param = openapi.Parameter('test', openapi.IN_QUERY, description="test manual param", type=openapi.TYPE_BOOLEAN)
|
test_param = openapi.Parameter('test', openapi.IN_QUERY, description="test manual param", type=openapi.TYPE_BOOLEAN)
|
||||||
user_response = openapi.Response('response description', UserSerializer)
|
user_response = openapi.Response('response description', UserSerializer)
|
||||||
|
|
||||||
|
# 'method' can be used to customize a single HTTP method of a view
|
||||||
@swagger_auto_schema(method='get', manual_parameters=[test_param], responses={200: user_response})
|
@swagger_auto_schema(method='get', manual_parameters=[test_param], responses={200: user_response})
|
||||||
|
# 'methods' can be used to apply the same modification to multiple methods
|
||||||
@swagger_auto_schema(methods=['put', 'post'], request_body=UserSerializer)
|
@swagger_auto_schema(methods=['put', 'post'], request_body=UserSerializer)
|
||||||
@api_view(['GET', 'PUT', 'POST'])
|
@api_view(['GET', 'PUT', 'POST'])
|
||||||
def user_detail(request, pk):
|
def user_detail(request, pk):
|
||||||
|
|
@ -187,6 +189,7 @@ Where you can use the :func:`@swagger_auto_schema <.swagger_auto_schema>` decora
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
class ArticleViewSet(viewsets.ModelViewSet):
|
class ArticleViewSet(viewsets.ModelViewSet):
|
||||||
|
# method or 'methods' can be skipped because the list_route only handles a single method (GET)
|
||||||
@swagger_auto_schema(operation_description='GET /articles/today/')
|
@swagger_auto_schema(operation_description='GET /articles/today/')
|
||||||
@list_route(methods=['get'])
|
@list_route(methods=['get'])
|
||||||
def today(self, request):
|
def today(self, request):
|
||||||
|
|
@ -206,6 +209,45 @@ Where you can use the :func:`@swagger_auto_schema <.swagger_auto_schema>` decora
|
||||||
def partial_update(self, request, *args, **kwargs):
|
def partial_update(self, request, *args, **kwargs):
|
||||||
...
|
...
|
||||||
|
|
||||||
|
.. Tip::
|
||||||
|
|
||||||
|
If you want to customize the generation of a method you are not implementing yourself, you can use
|
||||||
|
``swagger_auto_schema`` in combination with Django's ``method_decorator``:
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
@method_decorator(name='list', decorator=swagger_auto_schema(
|
||||||
|
operation_description="description from swagger_auto_schema via method_decorator"
|
||||||
|
))
|
||||||
|
class ArticleViewSet(viewsets.ModelViewSet):
|
||||||
|
...
|
||||||
|
|
||||||
|
This allows you to avoid unnecessarily overriding the method.
|
||||||
|
|
||||||
|
.. Tip::
|
||||||
|
|
||||||
|
You can go even further and directly decorate the result of ``as_view``, in the same manner you would
|
||||||
|
override an ``@api_view`` as described above:
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
decorated_login_view = \
|
||||||
|
swagger_auto_schema(
|
||||||
|
method='post',
|
||||||
|
responses={status.HTTP_200_OK: LoginResponseSerializer}
|
||||||
|
)(LoginView.as_view())
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
...
|
||||||
|
url(r'^login/$', decorated_login_view, name='login')
|
||||||
|
]
|
||||||
|
|
||||||
|
This can allow you to avoid skipping an unnecessary *subclass* altogether.
|
||||||
|
|
||||||
|
.. Warning::
|
||||||
|
|
||||||
|
However, do note that both of the methods above can lead to unexpected (and maybe surprising) results by
|
||||||
|
replacing/decorating methods on the base class itself.
|
||||||
|
|
||||||
*************************
|
*************************
|
||||||
Subclassing and extending
|
Subclassing and extending
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,375 @@
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"argparse": {
|
||||||
|
"version": "1.0.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz",
|
||||||
|
"integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=",
|
||||||
|
"requires": {
|
||||||
|
"sprintf-js": "1.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autolinker": {
|
||||||
|
"version": "0.15.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/autolinker/-/autolinker-0.15.3.tgz",
|
||||||
|
"integrity": "sha1-NCQX2PLzRhsUzwkIjV7fh5HcmDI="
|
||||||
|
},
|
||||||
|
"builtin-status-codes": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
|
||||||
|
"integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug="
|
||||||
|
},
|
||||||
|
"call-me-maybe": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms="
|
||||||
|
},
|
||||||
|
"clipboard": {
|
||||||
|
"version": "1.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/clipboard/-/clipboard-1.7.1.tgz",
|
||||||
|
"integrity": "sha1-Ng1taUbpmnof7zleQrqStem1oWs=",
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"good-listener": "1.2.2",
|
||||||
|
"select": "1.1.2",
|
||||||
|
"tiny-emitter": "2.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"commander": {
|
||||||
|
"version": "2.12.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.12.2.tgz",
|
||||||
|
"integrity": "sha512-BFnaq5ZOGcDN7FlrtBT4xxkgIToalIIxwjxLWVJ8bGTpe1LroqMiqQXdA7ygc7CRvaYS+9zfPGFnJqFSayx+AA==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"core-js": {
|
||||||
|
"version": "2.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.3.tgz",
|
||||||
|
"integrity": "sha1-isw4NFgk8W2DZbfJtCWRaOjtYD4="
|
||||||
|
},
|
||||||
|
"core-util-is": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
|
||||||
|
},
|
||||||
|
"debug": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"delegate": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"dropkickjs": {
|
||||||
|
"version": "2.1.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/dropkickjs/-/dropkickjs-2.1.10.tgz",
|
||||||
|
"integrity": "sha1-8TyUAhQdoJ50rfTmN5jXkiBEOPI="
|
||||||
|
},
|
||||||
|
"es6-promise": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-OaU1hHjgJf+b0NzsxCg7NdIYERD6Hy/PEmFLTjw+b65scuisG3Kt4QoTvJ66BBkPZ581gr0kpoVzKnxniM8nng=="
|
||||||
|
},
|
||||||
|
"esprima": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw=="
|
||||||
|
},
|
||||||
|
"foreach": {
|
||||||
|
"version": "2.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz",
|
||||||
|
"integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k="
|
||||||
|
},
|
||||||
|
"format-util": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/format-util/-/format-util-1.0.3.tgz",
|
||||||
|
"integrity": "sha1-Ay3KShFiYqEsQ/TD7IVmQWxbLZU="
|
||||||
|
},
|
||||||
|
"good-listener": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
|
||||||
|
"integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"delegate": "3.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"hint.css": {
|
||||||
|
"version": "2.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/hint.css/-/hint.css-2.5.0.tgz",
|
||||||
|
"integrity": "sha1-OMrjZn5C2R392+UDEAqzSTL2/WU="
|
||||||
|
},
|
||||||
|
"https-browserify": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM="
|
||||||
|
},
|
||||||
|
"inherits": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||||
|
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||||
|
},
|
||||||
|
"isarray": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
||||||
|
},
|
||||||
|
"js-yaml": {
|
||||||
|
"version": "3.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz",
|
||||||
|
"integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==",
|
||||||
|
"requires": {
|
||||||
|
"argparse": "1.0.9",
|
||||||
|
"esprima": "4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"json-pointer": {
|
||||||
|
"version": "0.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-pointer/-/json-pointer-0.6.0.tgz",
|
||||||
|
"integrity": "sha1-jlAFUKaqxUZKRzN32leqbMIoKNc=",
|
||||||
|
"requires": {
|
||||||
|
"foreach": "2.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"json-schema-ref-parser": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-schema-ref-parser/-/json-schema-ref-parser-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-stQTMhec2R/p2L9dH4XXRlpNCP0mY8QrLd/9Kl+8SHJQmwHtE1nDfXH4wbsSM+GkJMl8t92yZbI0OIol432CIQ==",
|
||||||
|
"requires": {
|
||||||
|
"call-me-maybe": "1.0.1",
|
||||||
|
"debug": "3.1.0",
|
||||||
|
"es6-promise": "4.1.1",
|
||||||
|
"js-yaml": "3.10.0",
|
||||||
|
"ono": "4.0.2",
|
||||||
|
"z-schema": "3.19.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lodash.get": {
|
||||||
|
"version": "4.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
|
||||||
|
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
|
||||||
|
},
|
||||||
|
"lodash.isequal": {
|
||||||
|
"version": "4.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
|
||||||
|
"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
|
||||||
|
},
|
||||||
|
"lunr": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lunr/-/lunr-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-XJJ2ySyRrDWpJBtQGNRnI9kuL18="
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||||
|
},
|
||||||
|
"ono": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ono/-/ono-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-EFXJFoeF+KkZW4lwmcPMKHp2ZU7o6CM+ccX2nPbEJKiJIdyqbIcS1v6pmNgeNJ6x4/vEYn0/8oz66qXSPnnmSQ==",
|
||||||
|
"requires": {
|
||||||
|
"format-util": "1.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"openapi-sampler": {
|
||||||
|
"version": "0.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/openapi-sampler/-/openapi-sampler-0.4.3.tgz",
|
||||||
|
"integrity": "sha512-Ml6o1gt++ZQ4JKL344YRo/fX05yuM6C+l/mGVX2yjhu1BRKyrRK4Z46uBTKSVaag1xINBFwYG7dZdz/10AmPzA=="
|
||||||
|
},
|
||||||
|
"perfect-scrollbar": {
|
||||||
|
"version": "0.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/perfect-scrollbar/-/perfect-scrollbar-0.8.1.tgz",
|
||||||
|
"integrity": "sha512-RNC5tX/JMRYR+qVdJTEAWnRxw0Yf9lvbO8lTuAOvgDODkiA8lveTSkvrNMhmaGKEyimJpJl+myb/syVS9YyPuw=="
|
||||||
|
},
|
||||||
|
"prismjs": {
|
||||||
|
"version": "1.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.9.0.tgz",
|
||||||
|
"integrity": "sha1-+j4tntw8OIfB8fMJXUHx+bQgDw8=",
|
||||||
|
"requires": {
|
||||||
|
"clipboard": "1.7.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"process-nextick-args": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
|
||||||
|
"integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M="
|
||||||
|
},
|
||||||
|
"readable-stream": {
|
||||||
|
"version": "2.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
|
||||||
|
"integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
|
||||||
|
"requires": {
|
||||||
|
"core-util-is": "1.0.2",
|
||||||
|
"inherits": "2.0.3",
|
||||||
|
"isarray": "1.0.0",
|
||||||
|
"process-nextick-args": "1.0.7",
|
||||||
|
"safe-buffer": "5.1.1",
|
||||||
|
"string_decoder": "1.0.3",
|
||||||
|
"util-deprecate": "1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"redoc": {
|
||||||
|
"version": "1.19.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/redoc/-/redoc-1.19.3.tgz",
|
||||||
|
"integrity": "sha1-DfPx+6S92G/+nGIAEzBxUjytVec=",
|
||||||
|
"requires": {
|
||||||
|
"core-js": "2.5.3",
|
||||||
|
"dropkickjs": "2.1.10",
|
||||||
|
"hint.css": "2.5.0",
|
||||||
|
"https-browserify": "1.0.0",
|
||||||
|
"json-pointer": "0.6.0",
|
||||||
|
"json-schema-ref-parser": "3.3.1",
|
||||||
|
"lunr": "1.0.0",
|
||||||
|
"mark.js": "github:julmot/mark.js#714c9523feca999267f1758da8cfd92d077105d0",
|
||||||
|
"openapi-sampler": "0.4.3",
|
||||||
|
"perfect-scrollbar": "0.8.1",
|
||||||
|
"prismjs": "1.9.0",
|
||||||
|
"remarkable": "1.7.1",
|
||||||
|
"scrollparent": "2.0.1",
|
||||||
|
"slugify": "1.2.6",
|
||||||
|
"stream-http": "2.7.2",
|
||||||
|
"ts-helpers": "1.1.2",
|
||||||
|
"zone.js": "0.8.18"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"mark.js": {
|
||||||
|
"version": "github:julmot/mark.js#714c9523feca999267f1758da8cfd92d077105d0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"remarkable": {
|
||||||
|
"version": "1.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/remarkable/-/remarkable-1.7.1.tgz",
|
||||||
|
"integrity": "sha1-qspJchALZqZCpjoQIcpLrBvjv/Y=",
|
||||||
|
"requires": {
|
||||||
|
"argparse": "0.1.16",
|
||||||
|
"autolinker": "0.15.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"argparse": {
|
||||||
|
"version": "0.1.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz",
|
||||||
|
"integrity": "sha1-z9AeD7uj1srtBJ+9dY1A9lGW9Xw=",
|
||||||
|
"requires": {
|
||||||
|
"underscore": "1.7.0",
|
||||||
|
"underscore.string": "2.4.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"safe-buffer": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
|
||||||
|
},
|
||||||
|
"scrollparent": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/scrollparent/-/scrollparent-2.0.1.tgz",
|
||||||
|
"integrity": "sha1-cV1bnMV3YPsivczDvvtb/gaxoxc="
|
||||||
|
},
|
||||||
|
"select": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
|
||||||
|
"integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"slugify": {
|
||||||
|
"version": "1.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/slugify/-/slugify-1.2.6.tgz",
|
||||||
|
"integrity": "sha512-796YAGnzEnLKQHAFf7H2q1nsjY/9qywSnF9ZkMUbs9he4aZaXO/zFUow0LZ95sBAiQjOX1EmGl23gTYaswiNaQ=="
|
||||||
|
},
|
||||||
|
"sprintf-js": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||||
|
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
|
||||||
|
},
|
||||||
|
"stream-http": {
|
||||||
|
"version": "2.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.7.2.tgz",
|
||||||
|
"integrity": "sha512-c0yTD2rbQzXtSsFSVhtpvY/vS6u066PcXOX9kBB3mSO76RiUQzL340uJkGBWnlBg4/HZzqiUXtaVA7wcRcJgEw==",
|
||||||
|
"requires": {
|
||||||
|
"builtin-status-codes": "3.0.0",
|
||||||
|
"inherits": "2.0.3",
|
||||||
|
"readable-stream": "2.3.3",
|
||||||
|
"to-arraybuffer": "1.0.1",
|
||||||
|
"xtend": "4.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"string_decoder": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
|
||||||
|
"requires": {
|
||||||
|
"safe-buffer": "5.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"swagger-ui-dist": {
|
"swagger-ui-dist": {
|
||||||
"version": "3.6.1",
|
"version": "3.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.7.0.tgz",
|
||||||
"integrity": "sha1-uzQgV/h2COTs2DlGMDSJxjYicgY="
|
"integrity": "sha1-hkLAGUNf1SOE09KzVaHMovEDcoM="
|
||||||
|
},
|
||||||
|
"tiny-emitter": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-2NM0auVBGft5tee/OxP4PI3d8WItkDM+fPnaRAVo6xTDI2knbz9eC5ArWGqtGlYqiH3RU5yMpdyTTO7MguC4ow==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"to-arraybuffer": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M="
|
||||||
|
},
|
||||||
|
"ts-helpers": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ts-helpers/-/ts-helpers-1.1.2.tgz",
|
||||||
|
"integrity": "sha1-/Gm+nx87rtAfsaDvjUz+dIgU2DU="
|
||||||
|
},
|
||||||
|
"underscore": {
|
||||||
|
"version": "1.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz",
|
||||||
|
"integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk="
|
||||||
|
},
|
||||||
|
"underscore.string": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz",
|
||||||
|
"integrity": "sha1-jN2PusTi0uoefi6Al8QvRCKA+Fs="
|
||||||
|
},
|
||||||
|
"util-deprecate": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||||
|
},
|
||||||
|
"validator": {
|
||||||
|
"version": "9.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/validator/-/validator-9.2.0.tgz",
|
||||||
|
"integrity": "sha512-6Ij4Eo0KM4LkR0d0IegOwluG5453uqT5QyF5SV5Ezvm8/zmkKI/L4eoraafZGlZPC9guLkwKzgypcw8VGWWnGA=="
|
||||||
|
},
|
||||||
|
"xtend": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
|
||||||
|
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
|
||||||
|
},
|
||||||
|
"z-schema": {
|
||||||
|
"version": "3.19.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/z-schema/-/z-schema-3.19.0.tgz",
|
||||||
|
"integrity": "sha512-V94f3ODuluBS4kQLLjNhwoMek0dyIXCsvNu/A17dAyJ6sMhT5KkJQwSn07R0naByLIXJWMDk+ruMfI/3G3hS4Q==",
|
||||||
|
"requires": {
|
||||||
|
"commander": "2.12.2",
|
||||||
|
"lodash.get": "4.4.2",
|
||||||
|
"lodash.isequal": "4.5.0",
|
||||||
|
"validator": "9.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zone.js": {
|
||||||
|
"version": "0.8.18",
|
||||||
|
"resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.18.tgz",
|
||||||
|
"integrity": "sha512-knKOBQM0oea3/x9pdyDuDi7RhxDlJhOIkeixXSiTKWLgs4LpK37iBc+1HaHwzlciHUKT172CymJFKo8Xgh+44Q=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "drf-yasg",
|
"name": "drf-yasg",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"swagger-ui-dist": "^3.6.1"
|
"redoc": "^1.19.3",
|
||||||
|
"swagger-ui-dist": "^3.7.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,4 +3,4 @@ sphinx_rtd_theme==0.2.4
|
||||||
Pillow==4.3.0
|
Pillow==4.3.0
|
||||||
readme_renderer==17.2
|
readme_renderer==17.2
|
||||||
|
|
||||||
Django==1.11.8
|
Django==2.0
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,62 @@
|
||||||
|
import re
|
||||||
from collections import defaultdict, OrderedDict
|
from collections import defaultdict, OrderedDict
|
||||||
|
|
||||||
import django.db.models
|
import django.db.models
|
||||||
import uritemplate
|
import uritemplate
|
||||||
from coreapi.compat import force_text
|
from coreapi.compat import force_text
|
||||||
from rest_framework.schemas.generators import SchemaGenerator
|
from rest_framework.schemas.generators import SchemaGenerator, EndpointEnumerator as _EndpointEnumerator
|
||||||
from rest_framework.schemas.inspectors import get_pk_description
|
from rest_framework.schemas.inspectors import get_pk_description
|
||||||
|
|
||||||
from . import openapi
|
from . import openapi
|
||||||
from .inspectors import SwaggerAutoSchema
|
from .inspectors import SwaggerAutoSchema
|
||||||
from .openapi import ReferenceResolver
|
from .openapi import ReferenceResolver
|
||||||
|
|
||||||
|
PATH_PARAMETER_RE = re.compile(r'{(?P<parameter>\w+)}')
|
||||||
|
|
||||||
|
|
||||||
|
class EndpointEnumerator(_EndpointEnumerator):
|
||||||
|
def get_path_from_regex(self, path_regex):
|
||||||
|
return self.unescape_path(super(EndpointEnumerator, self).get_path_from_regex(path_regex))
|
||||||
|
|
||||||
|
def unescape(self, s):
|
||||||
|
"""Unescape all backslash escapes from `s`.
|
||||||
|
|
||||||
|
:param str s: string with backslash escapes
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
# unlike .replace('\\', ''), this corectly transforms a double backslash into a single backslash
|
||||||
|
return re.sub(r'\\(.)', r'\1', s)
|
||||||
|
|
||||||
|
def unescape_path(self, path):
|
||||||
|
"""Remove backslashes from all path components outside {parameters}. This is needed because
|
||||||
|
Django>=2.0 ``path()``/``RoutePattern`` aggresively escapes all non-parameter path components.
|
||||||
|
|
||||||
|
**NOTE:** this might destructively affect some url regex patterns that contain metacharacters (e.g. \w, \d)
|
||||||
|
outside path parameter groups; if you are in this category, God help you
|
||||||
|
|
||||||
|
:param str path: path possibly containing
|
||||||
|
:return: the unescaped path
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
clean_path = ''
|
||||||
|
while path:
|
||||||
|
match = PATH_PARAMETER_RE.search(path)
|
||||||
|
if not match:
|
||||||
|
clean_path += self.unescape(path)
|
||||||
|
break
|
||||||
|
clean_path += self.unescape(path[:match.start()])
|
||||||
|
clean_path += match.group()
|
||||||
|
path = path[match.end():]
|
||||||
|
|
||||||
|
return clean_path
|
||||||
|
|
||||||
|
|
||||||
class OpenAPISchemaGenerator(object):
|
class OpenAPISchemaGenerator(object):
|
||||||
"""
|
"""
|
||||||
This class iterates over all registered API endpoints and returns an appropriate OpenAPI 2.0 compliant schema.
|
This class iterates over all registered API endpoints and returns an appropriate OpenAPI 2.0 compliant schema.
|
||||||
Method implementations shamelessly stolen and adapted from rest_framework SchemaGenerator.
|
Method implementations shamelessly stolen and adapted from rest_framework SchemaGenerator.
|
||||||
"""
|
"""
|
||||||
|
endpoint_enumerator_class = EndpointEnumerator
|
||||||
|
|
||||||
def __init__(self, info, version, url=None, patterns=None, urlconf=None):
|
def __init__(self, info, version, url=None, patterns=None, urlconf=None):
|
||||||
"""
|
"""
|
||||||
|
|
@ -79,8 +120,8 @@ class OpenAPISchemaGenerator(object):
|
||||||
:return: {path: (view_class, list[(http_method, view_instance)])
|
:return: {path: (view_class, list[(http_method, view_instance)])
|
||||||
:rtype: dict
|
:rtype: dict
|
||||||
"""
|
"""
|
||||||
inspector = self._gen.endpoint_inspector_cls(self._gen.patterns, self._gen.urlconf)
|
enumerator = self.endpoint_enumerator_class(self._gen.patterns, self._gen.urlconf)
|
||||||
endpoints = inspector.get_api_endpoints()
|
endpoints = enumerator.get_api_endpoints()
|
||||||
|
|
||||||
view_paths = defaultdict(list)
|
view_paths = defaultdict(list)
|
||||||
view_cls = {}
|
view_cls = {}
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -4,6 +4,7 @@ from django.core.validators import RegexValidator
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from rest_framework.mixins import RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin
|
from rest_framework.mixins import RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin
|
||||||
|
from rest_framework.settings import api_settings
|
||||||
|
|
||||||
from . import openapi
|
from . import openapi
|
||||||
from .errors import SwaggerGenerationError
|
from .errors import SwaggerGenerationError
|
||||||
|
|
@ -176,12 +177,15 @@ def serializer_field_to_swagger(field, swagger_object_type, definitions=None, **
|
||||||
description = description if swagger_object_type != openapi.Items else None # Items has no description either
|
description = description if swagger_object_type != openapi.Items else None # Items has no description either
|
||||||
|
|
||||||
def SwaggerType(**instance_kwargs):
|
def SwaggerType(**instance_kwargs):
|
||||||
if swagger_object_type == openapi.Parameter:
|
if swagger_object_type == openapi.Parameter and 'required' not in instance_kwargs:
|
||||||
instance_kwargs['required'] = field.required
|
instance_kwargs['required'] = field.required
|
||||||
if swagger_object_type != openapi.Items:
|
if swagger_object_type != openapi.Items and 'default' not in instance_kwargs:
|
||||||
default = getattr(field, 'default', serializers.empty)
|
default = getattr(field, 'default', serializers.empty)
|
||||||
if default is not serializers.empty:
|
if default is not serializers.empty:
|
||||||
instance_kwargs['default'] = default
|
instance_kwargs['default'] = default
|
||||||
|
if swagger_object_type == openapi.Schema and 'read_only' not in instance_kwargs:
|
||||||
|
if field.read_only:
|
||||||
|
instance_kwargs['read_only'] = True
|
||||||
instance_kwargs.update(kwargs)
|
instance_kwargs.update(kwargs)
|
||||||
return swagger_object_type(title=title, description=description, **instance_kwargs)
|
return swagger_object_type(title=title, description=description, **instance_kwargs)
|
||||||
|
|
||||||
|
|
@ -213,8 +217,6 @@ def serializer_field_to_swagger(field, swagger_object_type, definitions=None, **
|
||||||
required = []
|
required = []
|
||||||
for key, value in serializer.fields.items():
|
for key, value in serializer.fields.items():
|
||||||
properties[key] = serializer_field_to_swagger(value, ChildSwaggerType, definitions)
|
properties[key] = serializer_field_to_swagger(value, ChildSwaggerType, definitions)
|
||||||
if value.read_only:
|
|
||||||
properties[key].read_only = value.read_only
|
|
||||||
if value.required:
|
if value.required:
|
||||||
required.append(key)
|
required.append(key)
|
||||||
|
|
||||||
|
|
@ -286,13 +288,20 @@ def serializer_field_to_swagger(field, swagger_object_type, definitions=None, **
|
||||||
elif isinstance(field, serializers.FileField):
|
elif isinstance(field, serializers.FileField):
|
||||||
# swagger 2.0 does not support specifics about file fields, so ImageFile gets no special treatment
|
# swagger 2.0 does not support specifics about file fields, so ImageFile gets no special treatment
|
||||||
# OpenAPI 3.0 does support it, so a future implementation could handle this better
|
# OpenAPI 3.0 does support it, so a future implementation could handle this better
|
||||||
err = SwaggerGenerationError("parameter of type file is supported only in a formData Parameter")
|
err = SwaggerGenerationError("FileField is supported only in a formData Parameter or response Schema")
|
||||||
if swagger_object_type != openapi.Parameter:
|
if swagger_object_type == openapi.Schema:
|
||||||
raise err # pragma: no cover
|
# FileField.to_representation returns URL or file name
|
||||||
|
result = SwaggerType(type=openapi.TYPE_STRING, read_only=True)
|
||||||
|
if getattr(field, 'use_url', api_settings.UPLOADED_FILES_USE_URL):
|
||||||
|
result.format = openapi.FORMAT_URI
|
||||||
|
return result
|
||||||
|
elif swagger_object_type == openapi.Parameter:
|
||||||
param = SwaggerType(type=openapi.TYPE_FILE)
|
param = SwaggerType(type=openapi.TYPE_FILE)
|
||||||
if param['in'] != openapi.IN_FORM:
|
if param['in'] != openapi.IN_FORM:
|
||||||
raise err # pragma: no cover
|
raise err # pragma: no cover
|
||||||
return param
|
return param
|
||||||
|
else:
|
||||||
|
raise err # pragma: no cover
|
||||||
elif isinstance(field, serializers.DictField) and swagger_object_type == openapi.Schema:
|
elif isinstance(field, serializers.DictField) and swagger_object_type == openapi.Schema:
|
||||||
child_schema = serializer_field_to_swagger(field.child, ChildSwaggerType, definitions)
|
child_schema = serializer_field_to_swagger(field.child, ChildSwaggerType, definitions)
|
||||||
return SwaggerType(
|
return SwaggerType(
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,12 @@ class ArticleSerializer(serializers.ModelSerializer):
|
||||||
child=serializers.URLField(help_text="but i needed to test these 2 fields somehow"),
|
child=serializers.URLField(help_text="but i needed to test these 2 fields somehow"),
|
||||||
)
|
)
|
||||||
uuid = serializers.UUIDField(help_text="should articles have UUIDs?")
|
uuid = serializers.UUIDField(help_text="should articles have UUIDs?")
|
||||||
|
cover_name = serializers.FileField(use_url=False, source='cover', read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Article
|
model = Article
|
||||||
fields = ('title', 'body', 'slug', 'date_created', 'date_modified', 'references', 'uuid')
|
fields = ('title', 'body', 'slug', 'date_created', 'date_modified',
|
||||||
|
'references', 'uuid', 'cover', 'cover_name')
|
||||||
read_only_fields = ('date_created', 'date_modified')
|
read_only_fields = ('date_created', 'date_modified')
|
||||||
lookup_field = 'slug'
|
lookup_field = 'slug'
|
||||||
extra_kwargs = {'body': {'help_text': 'body serializer help_text'}}
|
extra_kwargs = {'body': {'help_text': 'body serializer help_text'}}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from rest_framework import viewsets
|
from rest_framework import viewsets
|
||||||
from rest_framework.decorators import detail_route, list_route
|
from rest_framework.decorators import detail_route, list_route
|
||||||
|
|
@ -19,6 +20,9 @@ class NoPagingAutoSchema(SwaggerAutoSchema):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(name='list', decorator=swagger_auto_schema(
|
||||||
|
operation_description="description from swagger_auto_schema via method_decorator"
|
||||||
|
))
|
||||||
class ArticleViewSet(viewsets.ModelViewSet):
|
class ArticleViewSet(viewsets.ModelViewSet):
|
||||||
"""
|
"""
|
||||||
ArticleViewSet class docstring
|
ArticleViewSet class docstring
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,17 @@
|
||||||
from django.conf.urls import url
|
import django
|
||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
|
if django.VERSION[:2] >= (2, 0):
|
||||||
|
from django.urls import path
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'$', views.SnippetList.as_view()),
|
path('', views.SnippetList.as_view()),
|
||||||
url(r'^(?P<pk>[0-9]+)/$', views.SnippetDetail.as_view()),
|
path('<int:pk>/', views.SnippetDetail.as_view()),
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
from django.conf.urls import url
|
||||||
|
urlpatterns = [
|
||||||
|
url('^$', views.SnippetList.as_view()),
|
||||||
|
url(r'^(?P<pk>\d+)/$', views.SnippetDetail.as_view()),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -4,5 +4,5 @@ from users import views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^$', views.UserList.as_view()),
|
url(r'^$', views.UserList.as_view()),
|
||||||
url(r'^(?P<pk>[0-9]+)/$', views.user_detail),
|
url(r'^(?P<pk>\d+)/$', views.user_detail),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ paths:
|
||||||
/articles/:
|
/articles/:
|
||||||
get:
|
get:
|
||||||
operationId: articles_list
|
operationId: articles_list
|
||||||
description: ArticleViewSet class docstring
|
description: description from swagger_auto_schema via method_decorator
|
||||||
parameters:
|
parameters:
|
||||||
- name: title
|
- name: title
|
||||||
in: query
|
in: query
|
||||||
|
|
@ -493,6 +493,13 @@ definitions:
|
||||||
description: should articles have UUIDs?
|
description: should articles have UUIDs?
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
|
cover:
|
||||||
|
type: string
|
||||||
|
format: uri
|
||||||
|
readOnly: true
|
||||||
|
cover_name:
|
||||||
|
type: string
|
||||||
|
readOnly: true
|
||||||
Project:
|
Project:
|
||||||
required:
|
required:
|
||||||
- project_name
|
- project_name
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ def test_appropriate_status_codes(swagger_dict):
|
||||||
|
|
||||||
def test_operation_docstrings(swagger_dict):
|
def test_operation_docstrings(swagger_dict):
|
||||||
articles_list = swagger_dict['paths']['/articles/']
|
articles_list = swagger_dict['paths']['/articles/']
|
||||||
assert articles_list['get']['description'] == "ArticleViewSet class docstring"
|
assert articles_list['get']['description'] == "description from swagger_auto_schema via method_decorator"
|
||||||
assert articles_list['post']['description'] == "ArticleViewSet class docstring"
|
assert articles_list['post']['description'] == "ArticleViewSet class docstring"
|
||||||
|
|
||||||
articles_detail = swagger_dict['paths']['/articles/{slug}/']
|
articles_detail = swagger_dict['paths']['/articles/{slug}/']
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
npm update
|
||||||
|
cp node_modules/redoc/dist/redoc.min.js src/drf_yasg/static/drf-yasg/redoc/redoc.min.js
|
||||||
|
cp -r node_modules/swagger-ui-dist src/drf_yasg/static/drf-yasg/
|
||||||
|
rm -f src/drf_yasg/static/drf-yasg/swagger-ui-dist/package.json src/drf_yasg/static/drf-yasg/swagger-ui-dist/.npmignore
|
||||||
Loading…
Reference in New Issue