Compare commits

..

30 Commits

Author SHA1 Message Date
Cristi Vîjdea 27e38101a4 Add customization blocks to ui templates
Closes #178.
2018-09-10 02:41:02 +03:00
Cristi Vîjdea 4bac38e67b Update ReDoc settings 2018-09-10 01:39:57 +03:00
Cristi Vîjdea 10abf46597 Merge pull request #204 from axnsan12/release-1.10.1
Lotsa fixes. Sorry for the long time no see 😄
2018-09-10 01:00:26 +03:00
Cristi Vîjdea 42841e3092 Add 1.10.1 changelog 2018-09-10 00:42:45 +03:00
Cristi Vîjdea 0654aefe58 Update swagger-ui to 3.18.2 and ReDoc to 2.0.0-alpha.37 2018-09-10 00:13:39 +03:00
Cristi Vîjdea 41b1ca4483 Do not bail on response when request body is formData 2018-09-10 00:02:39 +03:00
Cristi Vîjdea b109d4c847 Fix bad test for no_body 2018-09-09 23:14:09 +03:00
Cristi Vîjdea 591c12f1a7 Fix request_body=no_body handling
Fixes #188, #199.
2018-09-09 23:14:09 +03:00
Cristi Vîjdea 271918a678 Fix empty swagger_auto_schema return value 2018-09-09 23:14:09 +03:00
Cristi Vîjdea c5c40e31c4 Log regex compilation failure in find_regex
Closes #193.
2018-09-09 22:33:13 +03:00
Cristi Vîjdea 3ac6c9ec3a Remove dependency on future
Sole use was raise_from. 

six.raise_from loses exception detail on Python 2, so god help the programmers of the past.

Fixes #196.
2018-09-09 22:29:11 +03:00
Cristi Vîjdea 5fa35d5b81 Check for None before calling to_representation
Django REST Framework also does this in serializers.

https://github.com/encode/django-rest-framework/issues/2299
https://github.com/encode/django-rest-framework/blob/4d57d46bf89f54f276909219ee06f0a2bf112f9f/rest_framework/serializers.py#L518

Fixes #203.
2018-09-09 22:22:58 +03:00
Terence D. Honles f9b215deab Fix missing backslashes found via pytest 3.8 (#202)
See: https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals

Changed in version 3.6: Unrecognized escape sequences produce a DeprecationWarning

Closes #202.
2018-09-09 22:17:04 +03:00
Nour Chawich 0837873f55 Add missing overrides to swagger_auto_schema (#194)
Add missing `operation_summary` and `deprecated` to `swagger_auto_schema` decorator data. The previous PR missed adding these to extra overrides.

- Added tests
- Updated reference.yaml

Refs #149, #173.
Closes #194, #198.
2018-09-09 22:15:50 +03:00
Julian Bez 02b8848912 Fix error when using typing classes (#195)
Closes #195.
2018-09-09 22:11:29 +03:00
Cristi Vîjdea 6e39a58b2d Defer settings imports to avoid ImportErrors 2018-09-09 21:56:26 +03:00
Cristi Vîjdea e146a9712e Remove minor version from PyCharm python interpreter name
This allows for conflictless development on any supproted python version
2018-09-09 21:56:26 +03:00
Cristi Vîjdea ec7529399e Fix swagger-ui style when session auth is disabled 2018-09-09 21:56:26 +03:00
Cristi Vîjdea 824baf7e9d Update PyCharm interpreter to python 3.7 2018-08-14 09:54:05 +03:00
Cristi Vîjdea dd50eb5e12 Remove unused requirement 2018-08-14 09:53:47 +03:00
Cristi Vîjdea 9b56e1a4c8 Avoid invalid usage of readOnly 2018-08-09 04:27:08 +03:00
Cristi Vîjdea 608abaf0e1 Fuck this test 2018-08-09 04:02:35 +03:00
Cristi Vîjdea 64e13a94da Remove tox-battery from CI requirements 2018-08-09 03:54:35 +03:00
Cristi Vîjdea 5051f91c97 Don't cache .tox on travis 2018-08-09 03:52:18 +03:00
Cristi Vîjdea d1ae81a27e Fix relative imports 2018-08-09 03:52:09 +03:00
Cristi Vîjdea 1ca401959c Fix tests 2018-08-09 03:40:35 +03:00
Cristi Vîjdea cd4bd4a18e Add SPEC_URL setting 2018-08-09 03:32:04 +03:00
Cristi Vîjdea 8a69f08c29 Update ReDoc to 2.0.0-alpha.34 2018-08-09 03:15:08 +03:00
Cristi Vîjdea e533b0827a Add ReDoc 1.x.x renderer 2018-08-09 03:14:08 +03:00
Cristi Vîjdea dc78dfdad8 Shrink tox envlist 2018-08-08 01:53:24 +03:00
42 changed files with 469 additions and 302 deletions
+7 -3
View File
@@ -16,10 +16,14 @@
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/testproj" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/testproj" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/.pytest_cache" />
<excludeFolder url="file://$MODULE_DIR$/dist" />
<excludeFolder url="file://$MODULE_DIR$/docs/_build" /> <excludeFolder url="file://$MODULE_DIR$/docs/_build" />
<excludeFolder url="file://$MODULE_DIR$/htmlcov" />
<excludeFolder url="file://$MODULE_DIR$/src/drf_yasg.egg-info" />
<excludeFolder url="file://$MODULE_DIR$/venv" /> <excludeFolder url="file://$MODULE_DIR$/venv" />
</content> </content>
<orderEntry type="jdk" jdkName="Python 3.6 (drf-yasg)" jdkType="Python SDK" /> <orderEntry type="jdk" jdkName="Python 3 (drf-yasg)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
</component> </component>
<component name="TemplatesService"> <component name="TemplatesService">
@@ -31,7 +35,7 @@
</option> </option>
</component> </component>
<component name="TestRunnerService"> <component name="TestRunnerService">
<option name="projectConfiguration" value="py.test" /> <option name="projectConfiguration" value="pytest" />
<option name="PROJECT_TEST_RUNNER" value="py.test" /> <option name="PROJECT_TEST_RUNNER" value="pytest" />
</component> </component>
</module> </module>
+1 -1
View File
@@ -69,7 +69,7 @@
<textMaps /> <textMaps />
</LinkMapSettings> </LinkMapSettings>
</component> </component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6 (drf-yasg)" project-jdk-type="Python SDK" /> <component name="ProjectRootManager" version="2" project-jdk-name="Python 3 (drf-yasg)" project-jdk-type="Python SDK" />
<component name="PythonCompatibilityInspectionAdvertiser"> <component name="PythonCompatibilityInspectionAdvertiser">
<option name="version" value="3" /> <option name="version" value="3" />
</component> </component>
+1 -7
View File
@@ -6,13 +6,7 @@ python:
- '3.5' - '3.5'
- '3.6' - '3.6'
cache: cache: pip
directories:
- $HOME/.cache/pip
- .tox
before_cache:
- rm -f $HOME/.cache/pip/log/debug.log
- rm -fr .tox/log/ .tox/*/log/ .tox/*/tmp/ .tox/dist .tox/*/lib/*/site-packages/drf_yasg*
jobs: jobs:
include: include:
+20
View File
@@ -3,6 +3,26 @@ Changelog
######### #########
**********
**1.10.1**
**********
*Release date: Sep 10, 2018*
- **ADDED:** added the ``SPEC_URL`` setting for controlling the download link in ``swagger-ui`` and ``ReDoc``
- **ADDED:** updated ``ReDoc`` settings (added ``NATIVE_SCROLLBARS`` and ``REQUIRED_PROPS_FIRST``)
- **ADDED:** added ``extra_styles`` and ``extra_scripts`` blocks to ui templates (:issue:`178`)
- **IMPROVED:** updated ``swagger-ui`` to version 3.18.2
- **IMPROVED:** updated ``ReDoc`` to version 2.0.0-alpha.37
- **FIXED:** stopped generating invalid OpenAPI by improper placement of ``readOnly`` Schemas
- **FIXED:** fixed broken CSS when ``USE_SESSION_AUTH=False``
- **FIXED:** fixed implementation of ``operation_summary`` and ``deprecated`` (:pr:`194`, :pr:`198`)
- **FIXED:** fixed a bug related to nested ``typing`` hints (:pr:`195`)
- **FIXED:** removed dependency on ``future`` (:issue:`196`)
- **FIXED:** removed exceptions loged for fields with ``default=None`` (:issue:`203`)
- **FIXED:** fixed ``request_body=no_body`` handling and related tests (:issue:`188`, :issue:`199`)
********** **********
**1.10.0** **1.10.0**
********** **********
+14 -3
View File
@@ -2,6 +2,17 @@
Customizing the web UI Customizing the web UI
###################### ######################
There is currently no pluggable way of customizing the web UI apart from the settings available in The web UI can be customized using the settings available in :ref:`swagger-ui-settings` and :ref:`redoc-ui-settings`.
:ref:`swagger-ui-settings` and :ref:`redoc-ui-settings`. If you really need to, you can override one of the
``drf-yasg/swagger-ui.html`` or ``drf-yasg/redoc.html`` templates that are used for rendering. You can also extend one of the ``drf-yasg/swagger-ui.html`` or ``drf-yasg/redoc.html`` templates that are used for
rendering. The customizable blocks are currently limited to:
{% block extra_styles %}
additional stylesheets
{% block extra_scripts %}
additional scripts
{% block user_context_message %}
*(swagger-ui session auth only)*
logged in user message
+56 -13
View File
@@ -27,16 +27,27 @@ Example:
} }
REDOC_SETTINGS = { REDOC_SETTINGS = {
'LAZY_RENDERING': True, 'LAZY_RENDERING': False,
... ...
} }
.. _url-settings:
All settings which configure URLs (``LOGIN_URL``, ``SPEC_URL``, ``VALIDATOR_URL``, etc.) can accept several forms of
input:
* A view name: `urls.reverse()` will be used to reverse-resolve the name
* A 2-tuple of ``(view_name, kwargs)```: `urls.reverse()` will be used to reverse-resolve the name using the given
`kwargs`; `kwargs` must be a dict
* A 3-tuple of ``(view_name, args, kwargs)```: `urls.reverse()` will be used to reverse-resolve the name using the given
`args` and `kwargs`; `args`, `kwargs` must be a tuple/list and a dict respectively
* A URL, which will be used as-is
The possible settings and their default values are as follows: The possible settings and their default values are as follows:
******************** ****************
``SWAGGER_SETTINGS`` SWAGGER_SETTINGS
******************** ****************
.. _default-class-settings: .. _default-class-settings:
@@ -192,6 +203,15 @@ Swagger UI settings
Swagger UI configuration settings. |br| Swagger UI configuration settings. |br|
See https://github.com/swagger-api/swagger-ui/blob/112bca906553a937ac67adc2e500bdeed96d067b/docs/usage/configuration.md#parameters. See https://github.com/swagger-api/swagger-ui/blob/112bca906553a937ac67adc2e500bdeed96d067b/docs/usage/configuration.md#parameters.
SPEC_URL
--------
URL pointing to a swagger document for use by swagger-ui. The default behaviour is to append ``?format=openapi`` to the
URL which serves the UI; see :ref:`note on URL settings <url-settings>` above.
**Default**: :python:`None` |br|
*Maps to parameter*: ``url``
VALIDATOR_URL VALIDATOR_URL
------------- -------------
@@ -312,9 +332,9 @@ This does not filter the operations from the display.
**Default**: :python:`['get','put','post','delete','options','head','patch','trace']` |br| **Default**: :python:`['get','put','post','delete','options','head','patch','trace']` |br|
*Maps to parameter*: ``supportedSubmitMethods`` *Maps to parameter*: ``supportedSubmitMethods``
****************** **************
``REDOC_SETTINGS`` REDOC_SETTINGS
****************** **************
.. _redoc-ui-settings: .. _redoc-ui-settings:
@@ -324,29 +344,52 @@ ReDoc UI settings
ReDoc UI configuration settings. |br| ReDoc UI configuration settings. |br|
See https://github.com/Rebilly/ReDoc#configuration. See https://github.com/Rebilly/ReDoc#configuration.
SPEC_URL
--------
URL pointing to a swagger document for use by ReDoc. The default behaviour is to append ``?format=openapi`` to the
URL which serves the UI; see :ref:`note on URL settings <url-settings>` above.
**Default**: :python:`None` |br|
*Maps to attribute*: ``spec-url``
LAZY_RENDERING LAZY_RENDERING
-------------- --------------
**Default**: :python:`True` |br| **NOTE:** this feature might be removed in future versions of ReDoc (see https://github.com/Rebilly/ReDoc/issues/475)
*Maps to attribute*: ``lazy-rendering``
**Default**: :python:`False` |br|
*Maps to attribute*: ``lazyRendering``
HIDE_HOSTNAME HIDE_HOSTNAME
------------- -------------
**Default**: :python:`False` |br| **Default**: :python:`False` |br|
*Maps to attribute*: ``hide-hostname`` *Maps to attribute*: ``hideHostname``
EXPAND_RESPONSES EXPAND_RESPONSES
---------------- ----------------
**Default**: :python:`'all'` |br| **Default**: :python:`'all'` |br|
*Maps to attribute*: ``expand-responses`` *Maps to attribute*: ``expandResponses``
PATH_IN_MIDDLE PATH_IN_MIDDLE
-------------- --------------
**Default**: :python:`False` |br| **Default**: :python:`False` |br|
*Maps to attribute*: ``path-in-middle-panel`` *Maps to attribute*: ``pathInMiddlePanel``
NATIVE_SCROLLBARS
-----------------
**Default**: :python:`False` |br|
*Maps to attribute*: ``nativeScrollbars``
REQUIRED_PROPS_FIRST
--------------------
**Default**: :python:`False` |br|
*Maps to attribute*: ``requiredPropsFirst``
.. _FORCE_SCRIPT_NAME: https://docs.djangoproject.com/en/2.0/ref/settings/#force-script-name .. _FORCE_SCRIPT_NAME: https://docs.djangoproject.com/en/2.0/ref/settings/#force-script-name
+61 -61
View File
@@ -22,9 +22,9 @@
"integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw=="
}, },
"buffer": { "buffer": {
"version": "5.2.0", "version": "5.2.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.0.tgz", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz",
"integrity": "sha512-nUJyfChH7PMJy75eRDCCKtszSEFokUNXC1hNVSe+o+VdcgvDPLs20k3v8UXI8ruRYAJiYtyRea8mYyqPxoHWDw==", "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==",
"requires": { "requires": {
"base64-js": "^1.0.2", "base64-js": "^1.0.2",
"ieee754": "^1.1.4" "ieee754": "^1.1.4"
@@ -96,9 +96,9 @@
"integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=" "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg="
}, },
"dompurify": { "dompurify": {
"version": "1.0.7", "version": "1.0.8",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-1.0.7.tgz", "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-1.0.8.tgz",
"integrity": "sha512-1xK0JEda/jvIm3SgqHXKvRCh3AbEKCyBbUAGpNCMVIljBD145cPvBR66JSj3O4SdscFUx5NXsDkJpz6vDT8KLg==" "integrity": "sha512-vetRFbN1SXSPfP3ClIiYnxTrXquSqakBEOoB5JESn0SVcSYzpu6ougjakpKnskGctYdlNpwf+riUHSkG7d4XUw=="
}, },
"encoding": { "encoding": {
"version": "0.1.12", "version": "0.1.12",
@@ -176,9 +176,9 @@
"integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==" "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw=="
}, },
"iconv-lite": { "iconv-lite": {
"version": "0.4.23", "version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"requires": { "requires": {
"safer-buffer": ">= 2.1.2 < 3" "safer-buffer": ">= 2.1.2 < 3"
} }
@@ -225,14 +225,14 @@
} }
}, },
"json-schema-ref-parser": { "json-schema-ref-parser": {
"version": "5.1.2", "version": "5.1.3",
"resolved": "https://registry.npmjs.org/json-schema-ref-parser/-/json-schema-ref-parser-5.1.2.tgz", "resolved": "https://registry.npmjs.org/json-schema-ref-parser/-/json-schema-ref-parser-5.1.3.tgz",
"integrity": "sha512-gP0mSqqkG99xNeA4F6bf2pXQYv5fFqe9SybbKO9qSMmyzzfFFIqd16s9Y65mRWKzZ0muTjyEtcSE/hLZLvIjZw==", "integrity": "sha512-CpDFlBwz/6la78hZxyB9FECVKGYjIIl3Ms3KLqFj99W7IIb7D00/RDgc++IGB4BBALl0QRhh5m4q5WNSopvLtQ==",
"requires": { "requires": {
"call-me-maybe": "^1.0.1", "call-me-maybe": "^1.0.1",
"debug": "^3.1.0", "debug": "^3.1.0",
"js-yaml": "^3.12.0", "js-yaml": "^3.12.0",
"ono": "^4.0.5" "ono": "^4.0.6"
} }
}, },
"loose-envify": { "loose-envify": {
@@ -244,9 +244,9 @@
} }
}, },
"lunr": { "lunr": {
"version": "2.3.1", "version": "2.3.3",
"resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.1.tgz", "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.3.tgz",
"integrity": "sha1-ETYWorYC3cEJMqe/ik5uV+v+zfI=" "integrity": "sha512-rlAEsgU9Bnavca2w1WJ6+6cdeHMXNyadcersyk3ZpuhgWb5HBNj8l4WwJz9PjksAhYDlpQffCVXPctOn+wCIVA=="
}, },
"mark.js": { "mark.js": {
"version": "8.11.1", "version": "8.11.1",
@@ -259,9 +259,9 @@
"integrity": "sha512-49i2QYhfULqaXzNZpxC808PisuCTGT2fgG0zrzdCI9N3rIfAWfW0nggvbXr6zvpynZdOG5+9xNxdzP0kwZnERw==" "integrity": "sha512-49i2QYhfULqaXzNZpxC808PisuCTGT2fgG0zrzdCI9N3rIfAWfW0nggvbXr6zvpynZdOG5+9xNxdzP0kwZnERw=="
}, },
"memoize-one": { "memoize-one": {
"version": "4.0.0", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-4.0.0.tgz", "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-4.0.2.tgz",
"integrity": "sha512-wdpOJ4XBejprGn/xhd1i2XR8Dv1A25FJeIvR7syQhQlz9eXsv+06llcvcmBxlWVGv4C73QBsWA8kxvZozzNwiQ==" "integrity": "sha512-ucx2DmXTeZTsS4GPPUZCbULAN7kdPT1G+H49Y34JjbQ5ESc6OGhVxKvb1iKhr9v19ZB9OtnHwNnhUnNR/7Wteg=="
}, },
"min-document": { "min-document": {
"version": "2.19.0", "version": "2.19.0",
@@ -272,9 +272,9 @@
} }
}, },
"mobx-react": { "mobx-react": {
"version": "5.2.3", "version": "5.2.6",
"resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-5.2.3.tgz", "resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-5.2.6.tgz",
"integrity": "sha512-OuSlF2nJEa1PGookZcZnINbvEK4iWNNYiqUh6aebk2AkWxj3sG8OafDOQMcMYApQALTHRsrBIjOx/K8TFxcz7w==", "integrity": "sha512-AGo3m/ICzN2SEbtNi8ZdF1ShDcAOnEKonZFc3mm8uuF2obDXg6rXqV5mtkr9ZHPYizGp51kT/vRg0TlGzok+CA==",
"requires": { "requires": {
"hoist-non-react-statics": "^2.5.0", "hoist-non-react-statics": "^2.5.0",
"react-lifecycles-compat": "^3.0.2" "react-lifecycles-compat": "^3.0.2"
@@ -300,17 +300,17 @@
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
}, },
"ono": { "ono": {
"version": "4.0.5", "version": "4.0.7",
"resolved": "https://registry.npmjs.org/ono/-/ono-4.0.5.tgz", "resolved": "https://registry.npmjs.org/ono/-/ono-4.0.7.tgz",
"integrity": "sha512-ZVNuV9kJbr/2tWs83I2snrYo+WIS0DISF/xUfX9p9b6GyDD6F5N9PzHjW+p/dep6IGwSYylf1HCub5I/nM0R5Q==", "integrity": "sha512-FJiGEETwfSVyOwVTwQZD7XN69FRekvgtlobtvPwtilc7PxIHg3gKUykdNP7E9mC/VTF2cxqKZxUZfNKA3MuQLA==",
"requires": { "requires": {
"format-util": "^1.0.3" "format-util": "^1.0.3"
} }
}, },
"openapi-sampler": { "openapi-sampler": {
"version": "1.0.0-beta.13", "version": "1.0.0-beta.14",
"resolved": "https://registry.npmjs.org/openapi-sampler/-/openapi-sampler-1.0.0-beta.13.tgz", "resolved": "https://registry.npmjs.org/openapi-sampler/-/openapi-sampler-1.0.0-beta.14.tgz",
"integrity": "sha512-NReKJh92NlUmY9CDo86L1Skkx3DMDeWmiLoqMxQSX7xBpoSx8WJ1JdTb7nEwz6M7S+WDPrQsEfS5BaIW27nMrQ==", "integrity": "sha512-NNmH9YAN5AaCE4w6MQXdCrmsOJJQTswHVSp075+h+iiG+OTonpZE8HzwocozovD2imx4lamkuxGLs4E4bO4Z+g==",
"requires": { "requires": {
"json-pointer": "^0.6.0" "json-pointer": "^0.6.0"
} }
@@ -361,17 +361,17 @@
} }
}, },
"react-dropdown": { "react-dropdown": {
"version": "1.5.0", "version": "1.6.2",
"resolved": "https://registry.npmjs.org/react-dropdown/-/react-dropdown-1.5.0.tgz", "resolved": "https://registry.npmjs.org/react-dropdown/-/react-dropdown-1.6.2.tgz",
"integrity": "sha512-rRv3a7NiP++yC1rzdjzkviC5ujq759i4SRa0M3C0Cr7loYT4Z3+JhSPekv1/04JiZNXX46cV3/g6A9kS7rkI4Q==", "integrity": "sha512-6RpKAFEVZLr53y91qnxC9rAWcoDvq8A9YstmeSV2AQgJxNFRcsreN1mJNRUctSvIwm0Hph07ouMvDc2GvfQBsA==",
"requires": { "requires": {
"classnames": "^2.2.3" "classnames": "^2.2.3"
} }
}, },
"react-hot-loader": { "react-hot-loader": {
"version": "4.3.4", "version": "4.3.6",
"resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.3.4.tgz", "resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.3.6.tgz",
"integrity": "sha512-LlKjtHq+RhDq9xm6crXojbkzrEvli5F4/RaeJ//XtDWrwwsAHDjEqKfZZiPCxv7gWV2cxE3YE8TXeE9BDzLqOA==", "integrity": "sha512-iuBeBkLz7tdkKmKduNww9o5JY4ZH1XI0TWwkWToHIqfOSh1xMCqLMSYXUasWfgZWykWWa9IkueYab+cDq2jyWg==",
"requires": { "requires": {
"fast-levenshtein": "^2.0.6", "fast-levenshtein": "^2.0.6",
"global": "^4.3.0", "global": "^4.3.0",
@@ -382,9 +382,9 @@
} }
}, },
"react-is": { "react-is": {
"version": "16.4.2", "version": "16.5.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.4.2.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.5.0.tgz",
"integrity": "sha512-rI3cGFj/obHbBz156PvErrS5xc6f1eWyTwyV4mo0vF2lGgXgS+mm7EKD5buLJq6jNgIagQescGSVG2YzgXt8Yg==" "integrity": "sha512-kpkCGLsChXTEQJVmowQqHpCjHKJFwB4SIChYaaaiAkq8OtE2aBg5pQe8/xnFlGmz9KmMx1H4oQRUyxP7qC9v5A=="
}, },
"react-lifecycles-compat": { "react-lifecycles-compat": {
"version": "3.0.4", "version": "3.0.4",
@@ -392,41 +392,41 @@
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
}, },
"react-tabs": { "react-tabs": {
"version": "2.2.2", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/react-tabs/-/react-tabs-2.2.2.tgz", "resolved": "https://registry.npmjs.org/react-tabs/-/react-tabs-2.3.0.tgz",
"integrity": "sha512-jZGAAoq1yknr/XF60/2kH5X3UJdR1X8ItcsLZ1mJnHfoniKcCr3shT2TK5wiS2sO0LOmqwX2BtJQX3snKd50sg==", "integrity": "sha512-pYaefgVy76/36AMEP+B8YuVVzDHa3C5UFZ3REU78zolk0qMxEhKvUFofvDCXyLZwf0RZjxIfiwok1BEb18nHyA==",
"requires": { "requires": {
"classnames": "^2.2.0", "classnames": "^2.2.0",
"prop-types": "^15.5.0" "prop-types": "^15.5.0"
} }
}, },
"redoc": { "redoc": {
"version": "2.0.0-alpha.32", "version": "2.0.0-alpha.37",
"resolved": "https://registry.npmjs.org/redoc/-/redoc-2.0.0-alpha.32.tgz", "resolved": "https://registry.npmjs.org/redoc/-/redoc-2.0.0-alpha.37.tgz",
"integrity": "sha512-hqQlQ8lnXszLOJGyrRCLtCH+yPJfS22FjbqJ9X6/KM3U46UXk7rv3jZG/quV0IIRhcyehRm6h8a6Eu18KRupng==", "integrity": "sha512-yuT8sTT+oNzg8G9PfsUhu8Jhd9rWYJCTSQnbgaq9KzAFcJ2O0XfvlstkGXDK1zmtSSBBe5cy9Z/1XcQGDiGYug==",
"requires": { "requires": {
"classnames": "^2.2.6", "classnames": "^2.2.6",
"decko": "^1.2.0", "decko": "^1.2.0",
"dompurify": "^1.0.6", "dompurify": "^1.0.7",
"eventemitter3": "^3.0.0", "eventemitter3": "^3.0.0",
"json-pointer": "^0.6.0", "json-pointer": "^0.6.0",
"json-schema-ref-parser": "^5.1.1", "json-schema-ref-parser": "^5.1.2",
"lunr": "^2.3.0", "lunr": "^2.3.2",
"mark.js": "^8.11.1", "mark.js": "^8.11.1",
"marked": "0.3.18", "marked": "0.3.18",
"memoize-one": "^4.0.0", "memoize-one": "^4.0.0",
"mobx-react": "^5.2.3", "mobx-react": "^5.2.5",
"openapi-sampler": "1.0.0-beta.13", "openapi-sampler": "1.0.0-beta.14",
"perfect-scrollbar": "^1.4.0", "perfect-scrollbar": "^1.4.0",
"polished": "^1.9.3", "polished": "^1.9.3",
"prismjs": "^1.15.0", "prismjs": "^1.15.0",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"react-dropdown": "^1.3.0", "react-dropdown": "^1.6.1",
"react-hot-loader": "^4.3.3", "react-hot-loader": "^4.3.4",
"react-tabs": "^2.0.0", "react-tabs": "^2.0.0",
"slugify": "^1.2.1", "slugify": "^1.3.1",
"stickyfill": "^1.1.1", "stickyfill": "^1.1.1",
"styled-components": "^3.3.3", "styled-components": "^3.4.2",
"tslib": "^1.9.3" "tslib": "^1.9.3"
} }
}, },
@@ -452,9 +452,9 @@
"integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
}, },
"slugify": { "slugify": {
"version": "1.3.0", "version": "1.3.1",
"resolved": "https://registry.npmjs.org/slugify/-/slugify-1.3.0.tgz", "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.3.1.tgz",
"integrity": "sha512-vvz+9ANt7CtdTHwJpfrsHOnGkgxky+CUPnvtzDZBZYFo/H/CdZkd5lJL7z7RqtH/x9QW/ItYYfHlcGf38CBK1w==" "integrity": "sha512-6BwyhjF5tG5P8s+0DPNyJmBSBePG6iMyhjvIW5zGdA3tFik9PtK+yNkZgTeiroCRGZYgkHftFA62tGVK1EI9Kw=="
}, },
"sprintf-js": { "sprintf-js": {
"version": "1.0.3", "version": "1.0.3",
@@ -467,9 +467,9 @@
"integrity": "sha1-OUE/7p0CXHSn5ZzuyyN4TMDxfwI=" "integrity": "sha1-OUE/7p0CXHSn5ZzuyyN4TMDxfwI="
}, },
"styled-components": { "styled-components": {
"version": "3.4.0", "version": "3.4.5",
"resolved": "https://registry.npmjs.org/styled-components/-/styled-components-3.4.0.tgz", "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-3.4.5.tgz",
"integrity": "sha1-0lb7RmfmwRWn7WGrePEgKDtD3QA=", "integrity": "sha512-/4c4/72+QAZ8LO+VIzTcitdjMcJleOln1laK9HZr166O+OmGpKZYt3+hRn0YhYPytwDGfx9J5c7WhU7Oys+nSw==",
"requires": { "requires": {
"buffer": "^5.0.3", "buffer": "^5.0.3",
"css-to-react-native": "^2.0.3", "css-to-react-native": "^2.0.3",
@@ -501,9 +501,9 @@
} }
}, },
"swagger-ui-dist": { "swagger-ui-dist": {
"version": "3.18.0", "version": "3.18.2",
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.18.0.tgz", "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.18.2.tgz",
"integrity": "sha512-AwFwmd9pf4XJb/IwLvpZ6Bl6wDhjidwjgBiqGv3/kXHp1hbVWi5ZKGSwKjdJ9att6MDJFhgp0+Dvd/Zqb7uySA==" "integrity": "sha512-pWAEiKkgWUJvjmLW9AojudnutJ+NTn5g6OdNLj1iIJWwCkoy40K3Upwa24DqFbmIE4vLX4XplND61hp2L+s5vg=="
}, },
"tiny-emitter": { "tiny-emitter": {
"version": "2.0.2", "version": "2.0.2",
+2 -2
View File
@@ -1,8 +1,8 @@
{ {
"name": "drf-yasg", "name": "drf-yasg",
"dependencies": { "dependencies": {
"redoc": "^2.0.0-alpha.32", "redoc": "^2.0.0-alpha.37",
"swagger-ui-dist": "^3.18.0" "swagger-ui-dist": "^3.18.2"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
-2
View File
@@ -1,9 +1,7 @@
coreapi>=2.3.3 coreapi>=2.3.3
coreschema>=0.0.4 coreschema>=0.0.4
openapi_codec>=1.3.2
ruamel.yaml>=0.15.34 ruamel.yaml>=0.15.34
inflection>=0.3.1 inflection>=0.3.1
future>=0.16.0
six>=1.10.0 six>=1.10.0
uritemplate>=3.0.0 uritemplate>=3.0.0
+2
View File
@@ -2,3 +2,5 @@
-r tox.txt -r tox.txt
-r test.txt -r test.txt
-r lint.txt -r lint.txt
tox-battery>=0.5
-1
View File
@@ -1,5 +1,4 @@
# requirements for building and running tox # requirements for building and running tox
tox>=3.1.2 tox>=3.1.2
tox-battery>=0.5
-r setup.txt -r setup.txt
+5 -1
View File
@@ -40,6 +40,7 @@ SWAGGER_DEFAULTS = {
'SECURITY_REQUIREMENTS': None, 'SECURITY_REQUIREMENTS': None,
'LOGIN_URL': getattr(settings, 'LOGIN_URL', None), 'LOGIN_URL': getattr(settings, 'LOGIN_URL', None),
'LOGOUT_URL': getattr(settings, 'LOGOUT_URL', None), 'LOGOUT_URL': getattr(settings, 'LOGOUT_URL', None),
'SPEC_URL': None,
'VALIDATOR_URL': '', 'VALIDATOR_URL': '',
'OPERATIONS_SORTER': None, 'OPERATIONS_SORTER': None,
@@ -65,10 +66,13 @@ SWAGGER_DEFAULTS = {
} }
REDOC_DEFAULTS = { REDOC_DEFAULTS = {
'LAZY_RENDERING': True, 'SPEC_URL': None,
'LAZY_RENDERING': False,
'HIDE_HOSTNAME': False, 'HIDE_HOSTNAME': False,
'EXPAND_RESPONSES': 'all', 'EXPAND_RESPONSES': 'all',
'PATH_IN_MIDDLE': False, 'PATH_IN_MIDDLE': False,
'NATIVE_SCROLLBARS': False,
'REQUIRED_PROPS_FIRST': False,
} }
IMPORT_STRINGS = [ IMPORT_STRINGS = [
+1 -1
View File
@@ -1,4 +1,4 @@
from future.utils import raise_from from six import raise_from
import copy import copy
import json import json
+1 -1
View File
@@ -134,7 +134,7 @@ class EndpointEnumerator(_EndpointEnumerator):
"""Remove backslashe escapes from all path components outside {parameters}. This is needed because """Remove backslashe escapes from all path components outside {parameters}. This is needed because
``simplify_regex`` does not handle this correctly - note however that this implementation is ``simplify_regex`` does not handle this correctly - note however that this implementation is
**NOTE:** this might destructively affect some url regex patterns that contain metacharacters (e.g. \w, \d) **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 outside path parameter groups; if you are in this category, God help you
:param str path: path possibly containing :param str path: path possibly containing
+2 -2
View File
@@ -258,7 +258,7 @@ class SerializerInspector(FieldInspector):
return NotHandled return NotHandled
def get_request_parameters(self, serializer, in_): def get_request_parameters(self, serializer, in_):
"""Convert a DRF serializer into a list of :class:`.Parameter`\ s. """Convert a DRF serializer into a list of :class:`.Parameter`\\ s.
Should return :data:`.NotHandled` if this inspector does not know how to handle the given `serializer`. Should return :data:`.NotHandled` if this inspector does not know how to handle the given `serializer`.
@@ -372,7 +372,7 @@ class ViewInspector(BaseInspector):
) )
def serializer_to_parameters(self, serializer, in_): def serializer_to_parameters(self, serializer, in_):
"""Convert a serializer to a possibly empty list of :class:`.Parameter`\ s. """Convert a serializer to a possibly empty list of :class:`.Parameter`\\ s.
:param serializers.BaseSerializer serializer: the ``Serializer`` instance :param serializers.BaseSerializer serializer: the ``Serializer`` instance
:param str in_: the location of the parameters, one of the `openapi.IN_*` constants :param str in_: the location of the parameters, one of the `openapi.IN_*` constants
+14 -4
View File
@@ -5,6 +5,7 @@ import operator
import uuid import uuid
from collections import OrderedDict from collections import OrderedDict
from decimal import Decimal from decimal import Decimal
from inspect import isclass
from django.core import validators from django.core import validators
from django.db import models from django.db import models
@@ -118,6 +119,8 @@ class InlineSerializerInspector(SerializerInspector):
definitions = self.components.with_scope(openapi.SCHEMA_DEFINITIONS) definitions = self.components.with_scope(openapi.SCHEMA_DEFINITIONS)
actual_schema = definitions.setdefault(ref_name, make_schema_definition) actual_schema = definitions.setdefault(ref_name, make_schema_definition)
actual_schema._remove_read_only()
actual_serializer = get_serializer_class(getattr(actual_schema, '_serializer', None)) actual_serializer = get_serializer_class(getattr(actual_schema, '_serializer', None))
this_serializer = get_serializer_class(field) this_serializer = get_serializer_class(field)
if actual_serializer and actual_serializer != this_serializer: # pragma: no cover if actual_serializer and actual_serializer != this_serializer: # pragma: no cover
@@ -194,7 +197,7 @@ def get_related_model(model, source):
class RelatedFieldInspector(FieldInspector): class RelatedFieldInspector(FieldInspector):
"""Provides conversions for ``RelatedField``\ s.""" """Provides conversions for ``RelatedField``\\ s."""
def field_to_swagger_object(self, field, swagger_object_type, use_references, **kwargs): def field_to_swagger_object(self, field, swagger_object_type, use_references, **kwargs):
SwaggerType, ChildSwaggerType = self._get_partial_types(field, swagger_object_type, use_references, **kwargs) SwaggerType, ChildSwaggerType = self._get_partial_types(field, swagger_object_type, use_references, **kwargs)
@@ -259,7 +262,12 @@ def find_regex(regex_field):
regex_validator = validator regex_validator = validator
# regex_validator.regex should be a compiled re object... # regex_validator.regex should be a compiled re object...
pattern = getattr(getattr(regex_validator, 'regex', None), 'pattern', None) try:
pattern = getattr(getattr(regex_validator, 'regex', None), 'pattern', None)
except Exception: # pragma: no cover
logger.warning('failed to compile regex validator of ' + str(regex_field), exc_info=True)
return None
if pattern: if pattern:
# attempt some basic cleanup to remove regex constructs not supported by JavaScript # attempt some basic cleanup to remove regex constructs not supported by JavaScript
# -- swagger uses javascript-style regexes - see https://github.com/swagger-api/swagger-editor/issues/1601 # -- swagger uses javascript-style regexes - see https://github.com/swagger-api/swagger-editor/issues/1601
@@ -509,7 +517,9 @@ class SerializerMethodFieldInspector(FieldInspector):
# look for Python 3.5+ style type hinting of the return value # look for Python 3.5+ style type hinting of the return value
hint_class = inspect.signature(method).return_annotation hint_class = inspect.signature(method).return_annotation
if not issubclass(hint_class, inspect._empty): if not isclass(hint_class) and hasattr(hint_class, '__args__'):
hint_class = hint_class.__args__[0]
if isclass(hint_class) and not issubclass(hint_class, inspect._empty):
type_info = get_basic_type_info_from_hint(hint_class) type_info = get_basic_type_info_from_hint(hint_class)
if type_info is not None: if type_info is not None:
@@ -568,7 +578,7 @@ class ChoiceFieldInspector(FieldInspector):
class FileFieldInspector(FieldInspector): class FileFieldInspector(FieldInspector):
"""Provides conversions for ``FileField``\ s.""" """Provides conversions for ``FileField``\\ s."""
def field_to_swagger_object(self, field, swagger_object_type, use_references, **kwargs): def field_to_swagger_object(self, field, swagger_object_type, use_references, **kwargs):
SwaggerType, ChildSwaggerType = self._get_partial_types(field, swagger_object_type, use_references, **kwargs) SwaggerType, ChildSwaggerType = self._get_partial_types(field, swagger_object_type, use_references, **kwargs)
+2 -3
View File
@@ -3,14 +3,13 @@ from collections import OrderedDict
import coreschema import coreschema
from rest_framework.pagination import CursorPagination, LimitOffsetPagination, PageNumberPagination from rest_framework.pagination import CursorPagination, LimitOffsetPagination, PageNumberPagination
from drf_yasg.utils import force_real_str
from .. import openapi from .. import openapi
from ..utils import force_real_str
from .base import FilterInspector, PaginatorInspector from .base import FilterInspector, PaginatorInspector
class CoreAPICompatInspector(PaginatorInspector, FilterInspector): class CoreAPICompatInspector(PaginatorInspector, FilterInspector):
"""Converts ``coreapi.Field``\ s to :class:`.openapi.Parameter`\ s for filters and paginators that implement a """Converts ``coreapi.Field``\\ s to :class:`.openapi.Parameter`\\ s for filters and paginators that implement a
``get_schema_fields`` method. ``get_schema_fields`` method.
""" """
+12 -7
View File
@@ -63,7 +63,7 @@ class SwaggerAutoSchema(ViewInspector):
- a list of primitive Parameters parsed as form data - a list of primitive Parameters parsed as form data
:param list[str] consumes: a list of accepted MIME types as returned by :meth:`.get_consumes` :param list[str] consumes: a list of accepted MIME types as returned by :meth:`.get_consumes`
:return: a (potentially empty) list of :class:`.Parameter`\ s either ``in: body`` or ``in: formData`` :return: a (potentially empty) list of :class:`.Parameter`\\ s either ``in: body`` or ``in: formData``
:rtype: list[openapi.Parameter] :rtype: list[openapi.Parameter]
""" """
serializer = self.get_request_serializer() serializer = self.get_request_serializer()
@@ -106,7 +106,7 @@ class SwaggerAutoSchema(ViewInspector):
if body_override is not None: if body_override is not None:
if body_override is no_body: if body_override is no_body:
return None return no_body
if self.method not in self.body_methods: if self.method not in self.body_methods:
raise SwaggerGenerationError("request_body can only be applied to (" + ','.join(self.body_methods) + raise SwaggerGenerationError("request_body can only be applied to (" + ','.join(self.body_methods) +
"); are you looking for query_serializer or manual_parameters?") "); are you looking for query_serializer or manual_parameters?")
@@ -126,10 +126,13 @@ class SwaggerAutoSchema(ViewInspector):
if body_override is None and self.method in self.implicit_body_methods: if body_override is None and self.method in self.implicit_body_methods:
return self.get_view_serializer() return self.get_view_serializer()
if body_override is no_body:
return None
return body_override return body_override
def get_request_form_parameters(self, serializer): def get_request_form_parameters(self, serializer):
"""Given a Serializer, return a list of ``in: formData`` :class:`.Parameter`\ s. """Given a Serializer, return a list of ``in: formData`` :class:`.Parameter`\\ s.
:param serializer: the view's request serializer as returned by :meth:`.get_request_serializer` :param serializer: the view's request serializer as returned by :meth:`.get_request_serializer`
:rtype: list[openapi.Parameter] :rtype: list[openapi.Parameter]
@@ -192,7 +195,11 @@ class SwaggerAutoSchema(ViewInspector):
:return: response serializer, :class:`.Schema`, :class:`.SchemaRef`, ``None`` :return: response serializer, :class:`.Schema`, :class:`.SchemaRef`, ``None``
""" """
return self._get_request_body_override() or self.get_view_serializer() body_override = self._get_request_body_override()
if body_override and body_override is not no_body:
return body_override
return self.get_view_serializer()
def get_default_responses(self): def get_default_responses(self):
"""Get the default responses determined for this view from the request serializer and request method. """Get the default responses determined for this view from the request serializer and request method.
@@ -207,8 +214,6 @@ class SwaggerAutoSchema(ViewInspector):
default_schema = self.get_default_response_serializer() default_schema = self.get_default_response_serializer()
default_schema = default_schema or '' default_schema = default_schema or ''
if any(is_form_media_type(encoding) for encoding in self.get_consumes()):
default_schema = ''
if default_schema and not isinstance(default_schema, openapi.Schema): if default_schema and not isinstance(default_schema, openapi.Schema):
default_schema = self.serializer_to_schema(default_schema) or '' default_schema = self.serializer_to_schema(default_schema) or ''
@@ -223,7 +228,7 @@ class SwaggerAutoSchema(ViewInspector):
def get_response_serializers(self): def get_response_serializers(self):
"""Return the response codes that this view is expected to return, and the serializer for each response body. """Return the response codes that this view is expected to return, and the serializer for each response body.
The return value should be a dict where the keys are possible status codes, and values are either strings, The return value should be a dict where the keys are possible status codes, and values are either strings,
``Serializer``\ s, :class:`.Schema`, :class:`.SchemaRef` or :class:`.Response` objects. See ``Serializer``\\ s, :class:`.Schema`, :class:`.SchemaRef` or :class:`.Response` objects. See
:func:`@swagger_auto_schema <.swagger_auto_schema>` for more details. :func:`@swagger_auto_schema <.swagger_auto_schema>` for more details.
:return: the response serializers :return: the response serializers
+16 -7
View File
@@ -387,8 +387,6 @@ class Parameter(SwaggerDict):
:param default: default value if the parameter is not provided; must conform to parameter type :param default: default value if the parameter is not provided; must conform to parameter type
""" """
super(Parameter, self).__init__(**extra) super(Parameter, self).__init__(**extra)
if (not schema and not type) or (schema and type):
raise AssertionError("either schema or type are required for Parameter object!")
self.name = name self.name = name
self.in_ = in_ self.in_ = in_
self.description = description self.description = description
@@ -401,6 +399,10 @@ class Parameter(SwaggerDict):
self.items = items self.items = items
self.default = default self.default = default
self._insert_extras__() self._insert_extras__()
if (not schema and not type) or (schema and type):
raise AssertionError("either schema or type are required for Parameter object (not both)!")
if schema and isinstance(schema, Schema):
schema._remove_read_only()
if self['in'] == IN_PATH: if self['in'] == IN_PATH:
# path parameters must always be required # path parameters must always be required
assert required is not False, "path parameter cannot be optional" assert required is not False, "path parameter cannot be optional"
@@ -432,9 +434,9 @@ class Schema(SwaggerDict):
:param bool,.Schema,.SchemaRef additional_properties: allow wildcard properties not listed in `properties` :param bool,.Schema,.SchemaRef additional_properties: allow wildcard properties not listed in `properties`
:param list[str] required: list of requried property names :param list[str] required: list of requried property names
:param .Schema,.SchemaRef items: type of array items, only valid if `type` is ``array`` :param .Schema,.SchemaRef items: type of array items, only valid if `type` is ``array``
:param default: only valid when insider another ``Schema``\ 's ``properties``; :param default: only valid when insider another ``Schema``\\ 's ``properties``;
the default value of this property if it is not provided, must conform to the type of this Schema the default value of this property if it is not provided, must conform to the type of this Schema
:param read_only: only valid when insider another ``Schema``\ 's ``properties``; :param read_only: only valid when insider another ``Schema``\\ 's ``properties``;
declares the property as read only - it must only be sent as part of responses, never in requests declares the property as read only - it must only be sent as part of responses, never in requests
""" """
super(Schema, self).__init__(**extra) super(Schema, self).__init__(**extra)
@@ -465,6 +467,11 @@ class Schema(SwaggerDict):
if pattern and type != TYPE_STRING: if pattern and type != TYPE_STRING:
raise AssertionError("pattern can only be used when type is string") raise AssertionError("pattern can only be used when type is string")
def _remove_read_only(self):
# readOnly is only valid for Schemas inside another Schema's properties;
# when placing Schema elsewhere we must take care to remove the readOnly flag
self.pop('readOnly', '')
class _Ref(SwaggerDict): class _Ref(SwaggerDict):
ref_name_re = re.compile(r"#/(?P<scope>.+)/(?P<name>[^/]+)$") ref_name_re = re.compile(r"#/(?P<scope>.+)/(?P<name>[^/]+)$")
@@ -497,12 +504,12 @@ class _Ref(SwaggerDict):
ref_match = self.ref_name_re.match(self.ref) ref_match = self.ref_name_re.match(self.ref)
return resolver.get(ref_match.group('name'), scope=ref_match.group('scope')) return resolver.get(ref_match.group('name'), scope=ref_match.group('scope'))
def __setitem__(self, key, value, **kwargs): def __setitem__(self, key, value):
if key == "$ref": if key == "$ref":
return super(_Ref, self).__setitem__(key, value, **kwargs) return super(_Ref, self).__setitem__(key, value)
raise NotImplementedError("only $ref can be set on Reference objects (not %s)" % key) raise NotImplementedError("only $ref can be set on Reference objects (not %s)" % key)
def __delitem__(self, key, **kwargs): def __delitem__(self, key):
raise NotImplementedError("cannot delete property of Reference object") raise NotImplementedError("cannot delete property of Reference object")
@@ -560,6 +567,8 @@ class Response(SwaggerDict):
self.schema = schema self.schema = schema
self.examples = examples self.examples = examples
self._insert_extras__() self._insert_extras__()
if schema and isinstance(schema, Schema):
schema._remove_read_only()
class ReferenceResolver(object): class ReferenceResolver(object):
+70 -39
View File
@@ -1,11 +1,15 @@
import warnings
import six
from django.shortcuts import render, resolve_url from django.shortcuts import render, resolve_url
from rest_framework.renderers import BaseRenderer, JSONRenderer, TemplateHTMLRenderer from rest_framework.renderers import BaseRenderer, JSONRenderer, TemplateHTMLRenderer
from rest_framework.utils import json from rest_framework.utils import json
from drf_yasg.openapi import Swagger
from .app_settings import redoc_settings, swagger_settings from .app_settings import redoc_settings, swagger_settings
from .codecs import VALIDATORS, OpenAPICodecJson, OpenAPICodecYaml from .codecs import VALIDATORS, OpenAPICodecJson, OpenAPICodecYaml
from .openapi import Swagger
from .utils import filter_none
class _SpecRenderer(BaseRenderer): class _SpecRenderer(BaseRenderer):
@@ -74,23 +78,52 @@ class _UIRenderer(BaseRenderer):
def set_context(self, renderer_context, swagger): def set_context(self, renderer_context, swagger):
renderer_context['title'] = swagger.info.title renderer_context['title'] = swagger.info.title
renderer_context['version'] = swagger.info.version renderer_context['version'] = swagger.info.version
renderer_context['swagger_settings'] = json.dumps(self.get_swagger_ui_settings())
renderer_context['redoc_settings'] = json.dumps(self.get_redoc_settings())
renderer_context['oauth2_config'] = json.dumps(self.get_oauth2_config()) renderer_context['oauth2_config'] = json.dumps(self.get_oauth2_config())
renderer_context['USE_SESSION_AUTH'] = swagger_settings.USE_SESSION_AUTH renderer_context['USE_SESSION_AUTH'] = swagger_settings.USE_SESSION_AUTH
renderer_context.update(self.get_auth_urls()) renderer_context.update(self.get_auth_urls())
def get_auth_urls(self): def resolve_url(self, to):
urls = {} if to is None:
if swagger_settings.LOGIN_URL is not None: return None
urls['LOGIN_URL'] = resolve_url(swagger_settings.LOGIN_URL)
if swagger_settings.LOGOUT_URL is not None:
urls['LOGOUT_URL'] = resolve_url(swagger_settings.LOGOUT_URL)
return urls args, kwargs = None, None
if not isinstance(to, six.string_types):
if len(to) > 2:
to, args, kwargs = to
elif len(to) == 2:
to, kwargs = to
args = args or ()
kwargs = kwargs or {}
return resolve_url(to, *args, **kwargs)
def get_auth_urls(self):
urls = {
'LOGIN_URL': self.resolve_url(swagger_settings.LOGIN_URL),
'LOGOUT_URL': self.resolve_url(swagger_settings.LOGOUT_URL),
}
return filter_none(urls)
def get_oauth2_config(self):
data = swagger_settings.OAUTH2_CONFIG
assert isinstance(data, dict), "OAUTH2_CONFIG must be a dict"
return data
class SwaggerUIRenderer(_UIRenderer):
"""Renders a swagger-ui web interface for schema browisng."""
template = 'drf-yasg/swagger-ui.html'
format = 'swagger'
def set_context(self, renderer_context, swagger):
super(SwaggerUIRenderer, self).set_context(renderer_context, swagger)
renderer_context['swagger_settings'] = json.dumps(self.get_swagger_ui_settings())
def get_swagger_ui_settings(self): def get_swagger_ui_settings(self):
data = { data = {
'url': self.resolve_url(swagger_settings.SPEC_URL),
'operationsSorter': swagger_settings.OPERATIONS_SORTER, 'operationsSorter': swagger_settings.OPERATIONS_SORTER,
'tagsSorter': swagger_settings.TAGS_SORTER, 'tagsSorter': swagger_settings.TAGS_SORTER,
'docExpansion': swagger_settings.DOC_EXPANSION, 'docExpansion': swagger_settings.DOC_EXPANSION,
@@ -103,39 +136,37 @@ class _UIRenderer(BaseRenderer):
'oauth2RedirectUrl': swagger_settings.OAUTH2_REDIRECT_URL, 'oauth2RedirectUrl': swagger_settings.OAUTH2_REDIRECT_URL,
'supportedSubmitMethods': swagger_settings.SUPPORTED_SUBMIT_METHODS, 'supportedSubmitMethods': swagger_settings.SUPPORTED_SUBMIT_METHODS,
} }
data = {k: v for k, v in data.items() if v is not None}
data = filter_none(data)
if swagger_settings.VALIDATOR_URL != '': if swagger_settings.VALIDATOR_URL != '':
data['validatorUrl'] = swagger_settings.VALIDATOR_URL data['validatorUrl'] = self.resolve_url(swagger_settings.VALIDATOR_URL)
return data return data
def get_redoc_settings(self):
data = {
'lazyRendering': redoc_settings.LAZY_RENDERING,
'hideHostname': redoc_settings.HIDE_HOSTNAME,
'expandResponses': redoc_settings.EXPAND_RESPONSES,
'pathInMiddle': redoc_settings.PATH_IN_MIDDLE,
}
return data
def get_oauth2_config(self):
data = swagger_settings.OAUTH2_CONFIG
assert isinstance(data, dict), "OAUTH2_CONFIG must be a dict"
return data
class SwaggerUIRenderer(_UIRenderer):
"""Renders a swagger-ui web interface for schema browisng.
Also requires :class:`.OpenAPIRenderer` as an available renderer on the same view.
"""
template = 'drf-yasg/swagger-ui.html'
format = 'swagger'
class ReDocRenderer(_UIRenderer): class ReDocRenderer(_UIRenderer):
"""Renders a ReDoc web interface for schema browisng. """Renders a ReDoc web interface for schema browisng."""
Also requires :class:`.OpenAPIRenderer` as an available renderer on the same view.
"""
template = 'drf-yasg/redoc.html' template = 'drf-yasg/redoc.html'
format = 'redoc' format = 'redoc'
def set_context(self, renderer_context, swagger):
super(ReDocRenderer, self).set_context(renderer_context, swagger)
renderer_context['redoc_settings'] = json.dumps(self.get_redoc_settings())
def get_redoc_settings(self):
data = {
'url': self.resolve_url(redoc_settings.SPEC_URL),
'lazyRendering': redoc_settings.LAZY_RENDERING,
'hideHostname': redoc_settings.HIDE_HOSTNAME,
'expandResponses': redoc_settings.EXPAND_RESPONSES,
'pathInMiddlePanel': redoc_settings.PATH_IN_MIDDLE,
'nativeScrollbars': redoc_settings.NATIVE_SCROLLBARS,
'requiredPropsFirst': redoc_settings.REQUIRED_PROPS_FIRST,
}
return filter_none(data)
class ReDocOldRenderer(ReDocRenderer):
"""Renders a ReDoc 1.x.x web interface for schema browisng."""
template = 'drf-yasg/redoc-old.html'
+15 -8
View File
@@ -3,19 +3,26 @@
var currentPath = window.location.protocol + "//" + window.location.host + window.location.pathname; var currentPath = window.location.protocol + "//" + window.location.host + window.location.pathname;
var specURL = currentPath + '?format=openapi'; var specURL = currentPath + '?format=openapi';
var redoc = document.createElement("redoc"); var redoc = document.createElement("redoc");
redoc.setAttribute("spec-url", specURL);
var redocSettings = JSON.parse(document.getElementById('redoc-settings').innerHTML); var redocSettings = JSON.parse(document.getElementById('redoc-settings').innerHTML);
if (redocSettings.lazyRendering) { if (redocSettings.url) {
redoc.setAttribute("lazy-rendering", ''); specURL = redocSettings.url;
delete redocSettings.url;
} }
if (redocSettings.pathInMiddle) { redoc.setAttribute("spec-url", specURL);
redoc.setAttribute("path-in-middle-panel", '');
function camelToKebab(str) {
return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
} }
if (redocSettings.hideHostname) {
redoc.setAttribute("hide-hostname", ''); for (var p in redocSettings) {
if (redocSettings.hasOwnProperty(p)) {
if (redocSettings[p] !== null && redocSettings[p] !== undefined && redocSettings[p] !== false) {
redoc.setAttribute(camelToKebab(p), redocSettings[p].toString());
}
}
} }
redoc.setAttribute("expand-responses", redocSettings.expandResponses);
document.body.appendChild(redoc); document.body.appendChild(redoc);
function hideEmptyVersion() { function hideEmptyVersion() {
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-4
View File
@@ -19,10 +19,6 @@ body.swagger-body {
background: #fafafa; background: #fafafa;
} }
#django-session-auth {
margin-right: 8px;
}
.hidden { .hidden {
display: none; display: none;
} }
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
@@ -6,6 +6,11 @@ function patchSwaggerUi() {
var authWrapper = document.querySelector('.auth-wrapper'); var authWrapper = document.querySelector('.auth-wrapper');
var authorizeButton = document.querySelector('.auth-wrapper .authorize'); var authorizeButton = document.querySelector('.auth-wrapper .authorize');
var djangoSessionAuth = document.querySelector('#django-session-auth'); var djangoSessionAuth = document.querySelector('#django-session-auth');
if (!djangoSessionAuth) {
console.log("WARNING: session auth disabled");
return;
}
if (document.querySelector('.auth-wrapper #django-session-auth')) { if (document.querySelector('.auth-wrapper #django-session-auth')) {
console.log("WARNING: session auth already patched; skipping patchSwaggerUi()"); console.log("WARNING: session auth already patched; skipping patchSwaggerUi()");
return; return;
@@ -57,7 +62,7 @@ function initSwaggerUi() {
} }
} }
console.log(swaggerSettings); console.log('swaggerSettings', swaggerSettings);
for (var p in swaggerSettings) { for (var p in swaggerSettings) {
if (swaggerSettings.hasOwnProperty(p)) { if (swaggerSettings.hasOwnProperty(p)) {
swaggerConfig[p] = swaggerSettings[p]; swaggerConfig[p] = swaggerSettings[p];
@@ -66,7 +71,7 @@ function initSwaggerUi() {
window.ui = SwaggerUIBundle(swaggerConfig); window.ui = SwaggerUIBundle(swaggerConfig);
var oauth2Config = JSON.parse(document.getElementById('oauth2-config').innerHTML); var oauth2Config = JSON.parse(document.getElementById('oauth2-config').innerHTML);
console.log(oauth2Config); console.log('oauth2Config', oauth2Config);
window.ui.initOAuth(oauth2Config); window.ui.initOAuth(oauth2Config);
} }
@@ -0,0 +1,18 @@
{% load static %}
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="{% static 'drf-yasg/style.css' %}"/>
</head>
<body>
<script id="redoc-settings" type="application/json">{{ redoc_settings | safe }}</script>
<script src="{% static 'drf-yasg/insQ.min.js' %}"></script>
<script src="{% static 'drf-yasg/redoc-init.js' %}"> </script>
<script src="{% static 'drf-yasg/redoc-old/redoc.min.js' %}"></script>
</body>
</html>
+10 -4
View File
@@ -7,12 +7,18 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="{% static 'drf-yasg/style.css' %}"/> <link rel="stylesheet" type="text/css" href="{% static 'drf-yasg/style.css' %}"/>
{% block extra_styles %}
{# -- Add any additional CSS scripts here -- #}
{% endblock %}
</head> </head>
<body> <body>
<script id="redoc-settings" type="application/json">{{ redoc_settings | safe }}</script> <script id="redoc-settings" type="application/json">{{ redoc_settings | safe }}</script>
<script src="{% static 'drf-yasg/insQ.min.js' %}"></script> <script src="{% static 'drf-yasg/insQ.min.js' %}"></script>
<script src="{% static 'drf-yasg/redoc-init.js' %}"> </script> <script src="{% static 'drf-yasg/redoc-init.js' %}"></script>
<script src="{% static 'drf-yasg/redoc/redoc.min.js' %}"></script> <script src="{% static 'drf-yasg/redoc/redoc.min.js' %}"></script>
{% block extra_scripts %}
{# -- Add any additional scripts here -- #}
{% endblock %}
</body> </body>
</html> </html>
+24 -53
View File
@@ -11,53 +11,14 @@
<link rel="stylesheet" type="text/css" href="{% static 'drf-yasg/swagger-ui-dist/swagger-ui.css' %}"> <link rel="stylesheet" type="text/css" href="{% static 'drf-yasg/swagger-ui-dist/swagger-ui.css' %}">
<link rel="icon" type="image/png" href="{% static 'drf-yasg/swagger-ui-dist/favicon-32x32.png' %}" sizes="32x32"/> <link rel="icon" type="image/png" href="{% static 'drf-yasg/swagger-ui-dist/favicon-32x32.png' %}" sizes="32x32"/>
<link rel="icon" type="image/png" href="{% static 'drf-yasg/swagger-ui-dist/favicon-16x16.png' %}" sizes="16x16"/> <link rel="icon" type="image/png" href="{% static 'drf-yasg/swagger-ui-dist/favicon-16x16.png' %}" sizes="16x16"/>
{% block extra_styles %}
{# -- Add any additional CSS scripts here -- #}
{% endblock %}
</head> </head>
<body class="swagger-body"> <body class="swagger-body">
<svg class="swagger-defs" xmlns="http://www.w3.org/2000/svg">
<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"></path>
</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"></path>
</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"></path>
</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"></path>
</symbol>
<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"></path>
</symbol>
<symbol viewBox="0 0 24 24" id="expand">
<path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"></path>
</symbol>
</defs>
</svg>
<div id="swagger-ui"></div> <div id="swagger-ui"></div>
<div id="spec-error" class="hidden alert alert-danger"></div>
<script id="swagger-settings" type="application/json">{{ swagger_settings | safe }}</script> <script id="swagger-settings" type="application/json">{{ swagger_settings | safe }}</script>
<script id="oauth2-config" type="application/json">{{ oauth2_config | safe }}</script> <script id="oauth2-config" type="application/json">{{ oauth2_config | safe }}</script>
@@ -66,33 +27,43 @@
<script src="{% static 'drf-yasg/swagger-ui-dist/swagger-ui-standalone-preset.js' %}"></script> <script src="{% static 'drf-yasg/swagger-ui-dist/swagger-ui-standalone-preset.js' %}"></script>
<script src="{% static 'drf-yasg/insQ.min.js' %}"></script> <script src="{% static 'drf-yasg/insQ.min.js' %}"></script>
<script src="{% static 'drf-yasg/swagger-ui-init.js' %}"></script> <script src="{% static 'drf-yasg/swagger-ui-init.js' %}"></script>
{% block extra_scripts %}
{# -- Add any additional scripts here -- #}
{% endblock %}
<a id="oauth2-redirect-url" href="{% static 'drf-yasg/swagger-ui-dist/oauth2-redirect.html' %}" class="hidden"></a> <a id="oauth2-redirect-url" href="{% static 'drf-yasg/swagger-ui-dist/oauth2-redirect.html' %}" class="hidden"></a>
<div id="django-session-auth" class="hidden"> {% if USE_SESSION_AUTH %}
{% if USE_SESSION_AUTH %} <div id="django-session-auth" class="hidden">
{% csrf_token %} {% csrf_token %}
{% if request.user.is_authenticated %}
<div class="hello"> {% block user_context_message %}
<span class="django-session">Django</span> <span class="label label-primary">{{ request.user }}</span> {% if request.user.is_authenticated %}
</div> <div class="hello">
{% endif %} <span class="django-session">Django</span> <span class="label label-primary">{{ request.user }}</span>
</div>
{% endif %}
{% endblock %}
{% if request.user.is_authenticated %} {% if request.user.is_authenticated %}
<div class='btn authorize'> <div class='btn authorize'>
<a id="auth" class="header__btn" href="{{ LOGOUT_URL }}?next={{ request.path }}" data-sw-translate> <a id="auth" class="header__btn" href="{{ LOGOUT_URL }}?next={{ request.path }}" data-sw-translate>
Django Logout {% block django_logout_message %}
Django Logout
{% endblock %}
</a> </a>
</div> </div>
{% else %} {% else %}
<div class='btn authorize'> <div class='btn authorize'>
<a id="auth" class="header__btn" href="{{ LOGIN_URL }}?next={{ request.path }}" data-sw-translate> <a id="auth" class="header__btn" href="{{ LOGIN_URL }}?next={{ request.path }}" data-sw-translate>
Django Login {% block django_login_message %}
Django Login
{% endblock %}
</a> </a>
</div> </div>
{% endif %} {% endif %}
{% endif %} </div>
</div> {% endif %}
</body> </body>
</html> </html>
+7 -5
View File
@@ -11,7 +11,7 @@ from rest_framework.settings import api_settings as rest_framework_settings
from rest_framework.utils import encoders, json from rest_framework.utils import encoders, json
from rest_framework.views import APIView from rest_framework.views import APIView
from drf_yasg.app_settings import swagger_settings from .app_settings import swagger_settings
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -50,7 +50,7 @@ def swagger_auto_schema(method=None, methods=None, auto_schema=unset, request_bo
the `manual_parameters` argument. the `manual_parameters` argument.
If a ``Serializer`` class or instance is given, it will be automatically converted into a :class:`.Schema` If a ``Serializer`` class or instance is given, it will be automatically converted into a :class:`.Schema`
used as a ``body`` :class:`.Parameter`, or into a list of ``form`` :class:`.Parameter`\ s, as appropriate. used as a ``body`` :class:`.Parameter`, or into a list of ``form`` :class:`.Parameter`\\ s, as appropriate.
:param .Serializer query_serializer: if you use a ``Serializer`` to parse query parameters, you can pass it here :param .Serializer query_serializer: if you use a ``Serializer`` to parse query parameters, you can pass it here
and have :class:`.Parameter` objects be generated automatically from it. and have :class:`.Parameter` objects be generated automatically from it.
@@ -63,7 +63,7 @@ def swagger_auto_schema(method=None, methods=None, auto_schema=unset, request_bo
:param list[.Parameter] manual_parameters: a list of manual parameters to override the automatically generated ones :param list[.Parameter] manual_parameters: a list of manual parameters to override the automatically generated ones
:class:`.Parameter`\ s are identified by their (``name``, ``in``) combination, and any parameters given :class:`.Parameter`\\ s are identified by their (``name``, ``in``) combination, and any parameters given
here will fully override automatically generated parameters if they collide. here will fully override automatically generated parameters if they collide.
It is an error to supply ``form`` parameters when the request does not consume form-data. It is an error to supply ``form`` parameters when the request does not consume form-data.
@@ -107,6 +107,8 @@ def swagger_auto_schema(method=None, methods=None, auto_schema=unset, request_bo
'query_serializer': query_serializer, 'query_serializer': query_serializer,
'manual_parameters': manual_parameters, 'manual_parameters': manual_parameters,
'operation_id': operation_id, 'operation_id': operation_id,
'operation_summary': operation_summary,
'deprecated': deprecated,
'operation_description': operation_description, 'operation_description': operation_description,
'security': security, 'security': security,
'responses': responses, 'responses': responses,
@@ -120,7 +122,7 @@ def swagger_auto_schema(method=None, methods=None, auto_schema=unset, request_bo
data.update(extra_overrides) data.update(extra_overrides)
if not data: # pragma: no cover if not data: # pragma: no cover
# no overrides to set, no use in doing more work # no overrides to set, no use in doing more work
return return view_method
# if the method is an @action, it will have a bind_to_methods attribute, or a mapping attribute for drf>3.8 # if the method is an @action, it will have a bind_to_methods attribute, or a mapping attribute for drf>3.8
bind_to_methods = getattr(view_method, 'bind_to_methods', []) bind_to_methods = getattr(view_method, 'bind_to_methods', [])
@@ -402,7 +404,7 @@ def get_field_default(field):
"called; 'default' will not be set on schema", field, exc_info=True) "called; 'default' will not be set on schema", field, exc_info=True)
default = serializers.empty default = serializers.empty
if default is not serializers.empty: if default is not serializers.empty and default is not None:
try: try:
default = field.to_representation(default) default = field.to_representation(default)
# JSON roundtrip ensures that the value is valid JSON; # JSON roundtrip ensures that the value is valid JSON;
+10 -5
View File
@@ -11,12 +11,15 @@ from rest_framework.settings import api_settings
from rest_framework.views import APIView from rest_framework.views import APIView
from .app_settings import swagger_settings from .app_settings import swagger_settings
from .renderers import OpenAPIRenderer, ReDocRenderer, SwaggerJSONRenderer, SwaggerUIRenderer, SwaggerYAMLRenderer from .renderers import (
OpenAPIRenderer, ReDocOldRenderer, ReDocRenderer, SwaggerJSONRenderer, SwaggerUIRenderer, SwaggerYAMLRenderer
)
SPEC_RENDERERS = (SwaggerYAMLRenderer, SwaggerJSONRenderer, OpenAPIRenderer) SPEC_RENDERERS = (SwaggerYAMLRenderer, SwaggerJSONRenderer, OpenAPIRenderer)
UI_RENDERERS = { UI_RENDERERS = {
'swagger': (SwaggerUIRenderer, ReDocRenderer), 'swagger': (SwaggerUIRenderer, ReDocRenderer),
'redoc': (ReDocRenderer, SwaggerUIRenderer), 'redoc': (ReDocRenderer, SwaggerUIRenderer),
'redoc-old': (ReDocOldRenderer, ReDocRenderer, SwaggerUIRenderer),
} }
@@ -45,9 +48,7 @@ def deferred_never_cache(view_func):
def get_schema_view(info=None, url=None, patterns=None, urlconf=None, public=False, validators=None, def get_schema_view(info=None, url=None, patterns=None, urlconf=None, public=False, validators=None,
generator_class=swagger_settings.DEFAULT_GENERATOR_CLASS, generator_class=None, authentication_classes=None, permission_classes=None):
authentication_classes=api_settings.DEFAULT_AUTHENTICATION_CLASSES,
permission_classes=api_settings.DEFAULT_PERMISSION_CLASSES):
"""Create a SchemaView class with default renderers and generators. """Create a SchemaView class with default renderers and generators.
:param .Info info: information about the API; if omitted, defaults to :ref:`DEFAULT_INFO <default-swagger-settings>` :param .Info info: information about the API; if omitted, defaults to :ref:`DEFAULT_INFO <default-swagger-settings>`
@@ -63,9 +64,13 @@ def get_schema_view(info=None, url=None, patterns=None, urlconf=None, public=Fal
:rtype: type[.SchemaView] :rtype: type[.SchemaView]
""" """
_public = public _public = public
_generator_class = generator_class _generator_class = generator_class or swagger_settings.DEFAULT_GENERATOR_CLASS
_auth_classes = authentication_classes _auth_classes = authentication_classes
if _auth_classes is None:
_auth_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
_perm_classes = permission_classes _perm_classes = permission_classes
if _perm_classes is None:
_perm_classes = api_settings.DEFAULT_PERMISSION_CLASSES
info = info or swagger_settings.DEFAULT_INFO info = info or swagger_settings.DEFAULT_INFO
validators = validators or [] validators = validators or []
_spec_renderers = tuple(renderer.with_validators(validators) for renderer in SPEC_RENDERERS) _spec_renderers = tuple(renderer.with_validators(validators) for renderer in SPEC_RENDERERS)
+4 -2
View File
@@ -15,7 +15,7 @@ from articles.models import Article
from drf_yasg import openapi from drf_yasg import openapi
from drf_yasg.app_settings import swagger_settings from drf_yasg.app_settings import swagger_settings
from drf_yasg.inspectors import CoreAPICompatInspector, FieldInspector, NotHandled, SwaggerAutoSchema from drf_yasg.inspectors import CoreAPICompatInspector, FieldInspector, NotHandled, SwaggerAutoSchema
from drf_yasg.utils import swagger_auto_schema from drf_yasg.utils import no_body, swagger_auto_schema
class DjangoFilterDescriptionInspector(CoreAPICompatInspector): class DjangoFilterDescriptionInspector(CoreAPICompatInspector):
@@ -133,11 +133,13 @@ class ArticleViewSet(viewsets.ModelViewSet):
""" """
pass pass
@swagger_auto_schema(request_body=no_body, operation_id='no_body_test')
def update(self, request, *args, **kwargs): def update(self, request, *args, **kwargs):
"""update method docstring""" """update method docstring"""
return super(ArticleViewSet, self).update(request, *args, **kwargs) return super(ArticleViewSet, self).update(request, *args, **kwargs)
@swagger_auto_schema(operation_description="partial_update description override", responses={404: 'slug not found'}) @swagger_auto_schema(operation_description="partial_update description override", responses={404: 'slug not found'},
operation_summary='partial_update summary', deprecated=True)
def partial_update(self, request, *args, **kwargs): def partial_update(self, request, *args, **kwargs):
"""partial_update method docstring""" """partial_update method docstring"""
return super(ArticleViewSet, self).partial_update(request, *args, **kwargs) return super(ArticleViewSet, self).partial_update(request, *args, **kwargs)
+4
View File
@@ -108,6 +108,10 @@ SWAGGER_SETTINGS = {
} }
} }
REDOC_SETTINGS = {
'SPEC_URL': ('schema-json', {'format': '.json'}),
}
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/1.11/topics/i18n/ # https://docs.djangoproject.com/en/1.11/topics/i18n/
+1
View File
@@ -51,6 +51,7 @@ urlpatterns = [
url(r'^swagger(?P<format>.json|.yaml)$', SchemaView.without_ui(cache_timeout=0), name='schema-json'), url(r'^swagger(?P<format>.json|.yaml)$', SchemaView.without_ui(cache_timeout=0), name='schema-json'),
url(r'^swagger/$', SchemaView.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'), url(r'^swagger/$', SchemaView.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
url(r'^redoc/$', SchemaView.with_ui('redoc', cache_timeout=0), name='schema-redoc'), url(r'^redoc/$', SchemaView.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
url(r'^redoc-old/$', SchemaView.with_ui('redoc-old', cache_timeout=0), name='schema-redoc-old'),
url(r'^cached/swagger(?P<format>.json|.yaml)$', SchemaView.without_ui(cache_timeout=None), name='cschema-json'), url(r'^cached/swagger(?P<format>.json|.yaml)$', SchemaView.without_ui(cache_timeout=None), name='cschema-json'),
url(r'^cached/swagger/$', SchemaView.with_ui('swagger', cache_timeout=None), name='cschema-swagger-ui'), url(r'^cached/swagger/$', SchemaView.with_ui('swagger', cache_timeout=None), name='cschema-swagger-ui'),
+2 -2
View File
@@ -6,7 +6,7 @@ from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from drf_yasg import openapi from drf_yasg import openapi
from drf_yasg.utils import no_body, swagger_auto_schema from drf_yasg.utils import swagger_auto_schema
from users.serializers import UserListQuerySerializer, UserSerializerrr from users.serializers import UserListQuerySerializer, UserSerializerrr
@@ -36,7 +36,7 @@ class UserList(APIView):
serializer.save() serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.data, status=status.HTTP_201_CREATED)
@swagger_auto_schema(request_body=no_body, operation_id="users_dummy", operation_description="dummy operation") @swagger_auto_schema(operation_id="users_dummy", operation_description="dummy operation")
def patch(self, request): def patch(self, request):
pass pass
+34 -8
View File
@@ -134,14 +134,9 @@ paths:
tags: tags:
- articles - articles
put: put:
operationId: articles_update operationId: no_body_test
description: update method docstring description: update method docstring
parameters: parameters: []
- name: data
in: body
required: true
schema:
$ref: '#/definitions/Article'
responses: responses:
'200': '200':
description: '' description: ''
@@ -151,6 +146,7 @@ paths:
- articles - articles
patch: patch:
operationId: articles_partial_update operationId: articles_partial_update
summary: partial_update summary
description: partial_update description override description: partial_update description override
parameters: parameters:
- name: data - name: data
@@ -167,6 +163,7 @@ paths:
description: slug not found description: slug not found
tags: tags:
- articles - articles
deprecated: true
delete: delete:
operationId: articles_delete operationId: articles_delete
description: destroy method docstring description: destroy method docstring
@@ -192,6 +189,8 @@ paths:
responses: responses:
'200': '200':
description: '' description: ''
schema:
$ref: '#/definitions/Article'
consumes: consumes:
- multipart/form-data - multipart/form-data
tags: tags:
@@ -228,6 +227,8 @@ paths:
responses: responses:
'201': '201':
description: '' description: ''
schema:
$ref: '#/definitions/ImageUpload'
consumes: consumes:
- multipart/form-data - multipart/form-data
tags: tags:
@@ -890,6 +891,32 @@ definitions:
type: string type: string
format: uuid format: uuid
readOnly: true readOnly: true
ImageUpload:
required:
- image_styles
type: object
properties:
what_am_i_doing:
description: test
type: string
pattern: ^69$
default: '69'
minLength: 1
image_styles:
description: Parameter with Items
type: array
items:
type: string
enum:
- wide
- tall
- thumb
- social
upload:
description: image serializer help_text
type: string
readOnly: true
format: uri
Identity: Identity:
title: Identity title: Identity
type: object type: object
@@ -1599,7 +1626,6 @@ definitions:
title: Foo title: Foo
type: string type: string
minLength: 1 minLength: 1
readOnly: true
MethodFieldExample: MethodFieldExample:
title: Hint example title: Hint example
type: object type: object
+2 -2
View File
@@ -25,8 +25,8 @@ def test_validator_url(swagger_settings, swagger):
swagger_settings['VALIDATOR_URL'] = None swagger_settings['VALIDATOR_URL'] = None
_check_swagger_setting(swagger, 'validatorUrl', None) _check_swagger_setting(swagger, 'validatorUrl', None)
swagger_settings['VALIDATOR_URL'] = 'not none' swagger_settings['VALIDATOR_URL'] = 'http://not.none/'
_check_swagger_setting(swagger, 'validatorUrl', 'not none') _check_swagger_setting(swagger, 'validatorUrl', 'http://not.none/')
with pytest.raises(KeyError): with pytest.raises(KeyError):
swagger_settings['VALIDATOR_URL'] = '' swagger_settings['VALIDATOR_URL'] = ''
+4 -7
View File
@@ -1,12 +1,9 @@
[tox] [tox]
# https://docs.djangoproject.com/en/dev/faq/install/#what-python-version-can-i-use-with-django # https://docs.djangoproject.com/en/dev/faq/install/#what-python-version-can-i-use-with-django
envlist = envlist =
py{27,34,35,36}-django111-drf37, py{27,34,35,36}-django111-drf{37,38},
py{27,34,35,36}-django111-drf38, py{34,35,36,37}-django20-drf{37,38},
py{34,35,36,37}-django20-drf37, py{35,36,37}-django21-drf{37,38},
py{34,35,36,37}-django20-drf38,
py{35,36,37}-django21-drf37,
py{35,36,37}-django21-drf38,
djmaster, lint, docs djmaster, lint, docs
[testenv] [testenv]
@@ -63,7 +60,7 @@ not_skip = __init__.py
atomic = true atomic = true
multi_line_output = 5 multi_line_output = 5
line_length = 120 line_length = 120
known_future_library = future,six known_future_library = six
known_standard_library = known_standard_library =
collections,copy,distutils,functools,inspect,io,json,logging,operator,os,pkg_resources,re,setuptools,sys, collections,copy,distutils,functools,inspect,io,json,logging,operator,os,pkg_resources,re,setuptools,sys,
types,warnings types,warnings
+2
View File
@@ -3,6 +3,8 @@ set -ev
npm update npm update
cp node_modules/redoc/bundles/redoc.standalone.js src/drf_yasg/static/drf-yasg/redoc/redoc.min.js cp node_modules/redoc/bundles/redoc.standalone.js src/drf_yasg/static/drf-yasg/redoc/redoc.min.js
wget https://rebilly.github.io/ReDoc/releases/v1.x.x/redoc.min.js -O src/drf_yasg/static/drf-yasg/redoc-old/redoc.min.js
cp -r node_modules/swagger-ui-dist src/drf_yasg/static/drf-yasg/ cp -r node_modules/swagger-ui-dist src/drf_yasg/static/drf-yasg/
pushd src/drf_yasg/static/drf-yasg/swagger-ui-dist/ >/dev/null pushd src/drf_yasg/static/drf-yasg/swagger-ui-dist/ >/dev/null
rm -f package.json .npmignore README.md rm -f package.json .npmignore README.md