Compare commits

..

15 Commits

Author SHA1 Message Date
Cristi Vijdea d2bd838325 Add 1.13.0 changelog 2019-01-29 09:11:28 +02:00
Cristi Vijdea bacab20f0b Update swagger-ui to 3.20.5 and ReDoc to 2.0.0-rc.2 2019-01-29 09:05:26 +02:00
Cristi Vijdea df82fe59d7 Remove recommandations for unmaintained flex library
Fixes #285
2019-01-29 09:04:10 +02:00
Cristi Vijdea 7c5a0b7176 Promote deprecation warning to exception 2019-01-29 08:51:02 +02:00
Cristi Vijdea 69a1e62ed3 Add x-nullable to Optional 2019-01-29 08:46:33 +02:00
Alexander Egorov 3806d6efd5 Add support for custom and collection type hint classes (#272) 2019-01-29 08:39:29 +02:00
Dima Boger 58e6dae548 Fix typo with quotes in security doc (#300) 2019-01-28 23:14:33 +02:00
Cristi Vîjdea 8e2228fe5f Fix py37 test 2019-01-14 14:58:16 +02:00
Cristi Vîjdea 762467285c Keep dict key order for Python 3.7 2019-01-14 14:25:10 +02:00
Cristi Vîjdea 0e62fd6f2b Update copyright year 2019-01-14 14:25:10 +02:00
Daniel Hahler e266eeda60 Fi doc typo (#286) 2019-01-08 12:38:08 +02:00
Cristi Vîjdea 4b1098369c Add check to prevent build of docs with bad version 2019-01-03 20:36:12 +02:00
Cristi Vîjdea bda545e85f Inline coverage.sh 2018-12-30 16:14:05 +02:00
Cristi Vîjdea c1d3d4fe3c Add .readthedocs.yml 2018-12-29 17:44:29 +02:00
Cristi Vîjdea 7f3ffe80a9 Use call_view_method for get_serializer_class 2018-12-29 17:42:05 +02:00
25 changed files with 233 additions and 129 deletions
+15
View File
@@ -0,0 +1,15 @@
requirements_file: requirements/docs.txt
build:
image: latest
python:
version: 3.6
setup_py_install: false
pip_install: true # need this for correct pyproject.toml handling
extra_requirements:
- validation
# extra formats in addition to the default HTML web docs
formats:
- pdf
+5 -5
View File
@@ -16,10 +16,9 @@ jobs:
dist: xenial
sudo: required
- python: '3.6'
env: TOXENV=djmaster
- # readthedocs uses python 3.5 for building
python: '3.5'
env: TOXENV=docs
- python: '3.6'
env: TOXENV=djmaster
- python: '3.6'
env: TOXENV=lint
@@ -59,8 +58,9 @@ script:
after_success:
- |
if [[ -z "$TOXENV" && -z "$PYPI_DEPLOY" ]]; then
chmod +x coverage.sh
./coverage.sh
coverage combine || true
coverage report
codecov
fi
branches:
+1 -1
View File
@@ -10,7 +10,7 @@ License
BSD 3-Clause License
********************
Copyright (c) 2018, Cristian V. <cristi@cvjd.me> |br|\ All rights reserved.
Copyright (c) 2017 - 2019, Cristian V. <cristi@cvjd.me> |br|\ All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
+3 -4
View File
@@ -47,8 +47,7 @@ Features
`redoc <https://github.com/Rebilly/ReDoc>`_ for viewing the generated documentation
- schema view is cacheable out of the box
- generated Swagger schema can be automatically validated by
`swagger-spec-validator <https://github.com/Yelp/swagger_spec_validator>`_ or
`flex <https://github.com/pipermerriam/flex>`_
`swagger-spec-validator <https://github.com/Yelp/swagger_spec_validator>`_
- supports Django REST Framework API versioning with ``URLPathVersioning`` and ``NamespaceVersioning``; other DRF
or custom versioning schemes are not currently supported
@@ -166,7 +165,7 @@ a. ``get_schema_view`` parameters
- ``patterns`` - passed to SchemaGenerator
- ``urlconf`` - passed to SchemaGenerator
- ``public`` - if False, includes only endpoints the current user has access to
- ``validators`` - a list of validator names to apply on the generated schema; allowed values are ``flex``, ``ssv``
- ``validators`` - a list of validator names to apply on the generated schema; only ``ssv`` is currently supported
- ``generator_class`` - schema generator class to use; should be a subclass of ``OpenAPISchemaGenerator``
- ``authentication_classes`` - authentication classes for the schema view itself
- ``permission_classes`` - permission classes for the schema view itself
@@ -213,7 +212,7 @@ caching the schema view in-memory, with some sane defaults:
Given the numerous methods to manually customize the generated schema, it makes sense to validate the result to ensure
it still conforms to OpenAPI 2.0. To this end, validation is provided at the generation point using python swagger
libraries, and can be activated by passing :python:`validators=['flex', 'ssv']` to ``get_schema_view``; if the generated
libraries, and can be activated by passing :python:`validators=['ssv']` to ``get_schema_view``; if the generated
schema is not valid, a :python:`SwaggerValidationError` is raised by the handling codec.
**Warning:** This internal validation can slow down your server.
-6
View File
@@ -1,6 +0,0 @@
#!/usr/bin/env bash
set -e
coverage combine || true
coverage report
codecov
+12
View File
@@ -3,6 +3,18 @@ Changelog
#########
**********
**1.13.0**
**********
*Release date: Jan 29, 2019*
- **IMPROVED:** type hint inspection is now supported for collections and``Optional`` (:pr:`272`)
- **IMPROVED:** updated ``swagger-ui`` to version 3.20.5
- **IMPROVED:** updated ``ReDoc`` to version 2.0.0-rc.2
- **DEPRECATED:** quietly dropped support for the ``flex`` validator; it will still work if the library is installed,
but the setup.py requirement was removed and the validator will be silently skipped if not installed (:issue:`285`)
**********
**1.12.1**
**********
+5
View File
@@ -48,6 +48,11 @@ author = 'Cristi V.'
# The full version, including alpha/beta/rc tags.
release = get_distribution('drf_yasg').version
if 'noscm' in release:
raise AssertionError('Invalid package version string: %s. \n'
'The documentation must be built with drf_yasg installed from a distribution package, '
'which must have been built with a proper version number (i.e. from a full source checkout).'
% (release,))
# The short X.Y.Z version.
version = '.'.join(release.split('.')[:3])
+1 -1
View File
@@ -81,7 +81,7 @@ A very simple working configuration was provided by :ghuser:`Vigrond`, originall
'type': 'oauth2',
'authorizationUrl': '/yourapp/o/authorize',
'tokenUrl': '/yourapp/o/token/',
'flow": "accessCode',
'flow': 'accessCode',
'scopes': {
'read:groups': 'read groups',
}
+41 -40
View File
@@ -4,9 +4,9 @@
"lockfileVersion": 1,
"dependencies": {
"@babel/runtime": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.2.0.tgz",
"integrity": "sha512-oouEibCbHMVdZSDlJBO6bZmID/zA/G/Qx3H1d3rSNPTD+L8UNKvCat7aKWSJ74zYbm5zWGh0GQN0hKj8zYFTCg==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.3.1.tgz",
"integrity": "sha512-7jGW8ppV0ant637pIqAcFfQDDH1orEPGJb8aXfUozuCU3QqX7rX4DA8iwrbPrR1hcH0FTTHz47yQnk+bl5xHQA==",
"requires": {
"regenerator-runtime": "^0.12.0"
}
@@ -62,9 +62,9 @@
"integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg="
},
"dompurify": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-1.0.8.tgz",
"integrity": "sha512-vetRFbN1SXSPfP3ClIiYnxTrXquSqakBEOoB5JESn0SVcSYzpu6ougjakpKnskGctYdlNpwf+riUHSkG7d4XUw=="
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-1.0.9.tgz",
"integrity": "sha512-lt9f3A3RO1OCNaWdA+s/k7YVn0Typ5MbAKmX94PLCZbs8wLNccX3Bj4xXA7GLKOoDb/MeVoAoeIJarZD1JUnjg=="
},
"emojis-list": {
"version": "2.1.0",
@@ -115,11 +115,11 @@
}
},
"hoist-non-react-statics": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.2.1.tgz",
"integrity": "sha512-TFsu3TV3YLY+zFTZDrN8L2DTFanObwmBLpWvJs1qfUuEQ5bTAdFcwfx2T/bsCXfM9QHSLvjfP+nihEl0yvozxw==",
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz",
"integrity": "sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==",
"requires": {
"react-is": "^16.3.2"
"react-is": "^16.7.0"
}
},
"js-tokens": {
@@ -128,9 +128,9 @@
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"js-yaml": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz",
"integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==",
"version": "3.12.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.1.tgz",
"integrity": "sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==",
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
@@ -145,27 +145,27 @@
}
},
"json-schema-ref-parser": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/json-schema-ref-parser/-/json-schema-ref-parser-6.0.2.tgz",
"integrity": "sha512-EENU7mrmuBAdjSsAEJD8mMvodZyDhLBEfuSUBSIMuXqjs+cfMbFaxS8f6+ky675jetRzGzCdhzAU3y2VEtquvQ==",
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/json-schema-ref-parser/-/json-schema-ref-parser-6.0.3.tgz",
"integrity": "sha512-Ds/0541IPed88JSiMb3CBeUsxfL5Eosc0r97z0QMSXiBJTYKZLhOAGZd8zFVfpkKaRb4zDAnumyFYxnHLmbQmw==",
"requires": {
"call-me-maybe": "^1.0.1",
"js-yaml": "^3.12.0",
"ono": "^4.0.10"
"js-yaml": "^3.12.1",
"ono": "^4.0.11"
}
},
"json5": {
"version": "1.0.1",
"resolved": "http://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
"requires": {
"minimist": "^1.2.0"
}
},
"loader-utils": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.0.tgz",
"integrity": "sha512-KkQxP+pVgJC6ypy8ePypyhsV/hZeyVlkqiqrxe4pDgCwClbzmr3dGy8LbeSVhmfzYmCpOovdrVs/9chsQXCrLQ==",
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz",
"integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==",
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^2.0.0",
@@ -196,8 +196,9 @@
"integrity": "sha1-GA8fnr74sOY45BZq1S24eb6y/8U="
},
"marked": {
"version": "git+https://github.com/markedjs/marked.git#fb48827236ed3a43e611d2adb3c070ca3f55ed8e",
"from": "git+https://github.com/markedjs/marked.git#fb48827"
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/marked/-/marked-0.6.0.tgz",
"integrity": "sha512-HduzIW2xApSXKXJSpCipSxKyvMbwRRa/TwMbepmlZziKdH8548WSoDP4SxzulEKjlo8BE39l+2fwJZuRKOln6g=="
},
"memoize-one": {
"version": "4.0.3",
@@ -214,7 +215,7 @@
},
"minimist": {
"version": "1.2.0",
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
},
"mobx-react": {
@@ -253,11 +254,11 @@
"integrity": "sha512-/2Sk/khljhdrsamjJYS5NjrH+GKEHEwh7zFSiYyxROyYKagkE4kSn2zDQDRTOMo8mpT2jikxx6yI1dG7lNP/hw=="
},
"polished": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/polished/-/polished-2.3.1.tgz",
"integrity": "sha512-0mGyvVrHVRN92wfohriBWmMF4JLEnGgpZbpwPrNDhpB8NrX6lYI8GGWXEfrmrF+ZXg52Jkwd+D0rxViOvXM9RQ==",
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/polished/-/polished-2.3.3.tgz",
"integrity": "sha512-59V4fDbdxtH4I1m9TWxFsoGJbC8nnOpUYo5uFmvMfKp9Qh+6suo4VMUle1TGIIUZIGxfkW+Rs485zPk0wcwR2Q==",
"requires": {
"@babel/runtime": "^7.0.0"
"@babel/runtime": "^7.2.0"
}
},
"prismjs": {
@@ -283,9 +284,9 @@
}
},
"react-dropdown": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/react-dropdown/-/react-dropdown-1.6.3.tgz",
"integrity": "sha512-UAcVCsxeIkn5N7xpHr+WHDPbtUO+LsJyGiUT5PsdkVVPDJp9e42oxGICDyjcQ5rWa7DS0ctmn770JcM4gIvTiQ==",
"version": "1.6.4",
"resolved": "https://registry.npmjs.org/react-dropdown/-/react-dropdown-1.6.4.tgz",
"integrity": "sha512-zTlNRZ6vzjEPsodBNgh6Xjp9IempEx9sReH3crT2Jw4S6KW2wS/BRIH3d/grYf/iXARadDRD91//uUCs9yjoLg==",
"requires": {
"classnames": "^2.2.3"
}
@@ -333,9 +334,9 @@
}
},
"redoc": {
"version": "2.0.0-rc.0",
"resolved": "https://registry.npmjs.org/redoc/-/redoc-2.0.0-rc.0.tgz",
"integrity": "sha512-xH0xZ0mYFgWp4aUk9TAsGUKlyiT5Ph0MvhO3rK/cKUaCUZ+vwuOANA00Sf5hOMtXnjsXgJxC7i7fJ7tyX2yxWg==",
"version": "2.0.0-rc.2",
"resolved": "https://registry.npmjs.org/redoc/-/redoc-2.0.0-rc.2.tgz",
"integrity": "sha512-KsacFfNeBSBwGbWD8zzckYZ9V5TQB33xCl0I2pNQrWEEyhOZyfpp4Sf7qmOVYxj8DzdqHTP/HJiCtH84o68+mg==",
"requires": {
"classnames": "^2.2.6",
"decko": "^1.2.0",
@@ -345,7 +346,7 @@
"json-schema-ref-parser": "^6.0.1",
"lunr": "^2.3.2",
"mark.js": "^8.11.1",
"marked": "git+https://github.com/markedjs/marked.git#fb48827236ed3a43e611d2adb3c070ca3f55ed8e",
"marked": "^0.6.0",
"memoize-one": "^4.0.0",
"mobx-react": "^5.2.5",
"openapi-sampler": "1.0.0-beta.14",
@@ -389,7 +390,7 @@
},
"sprintf-js": {
"version": "1.0.3",
"resolved": "http://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
},
"stickyfill": {
@@ -398,9 +399,9 @@
"integrity": "sha1-OUE/7p0CXHSn5ZzuyyN4TMDxfwI="
},
"swagger-ui-dist": {
"version": "3.20.4",
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.20.4.tgz",
"integrity": "sha512-SR/N+N7EIncEhyA4UGiMQ12vJBSv5OKO4S1g7E7etO+9ZUV6SZ7SqKVC8RDDyMOJzdXgMXV7aGIBIiMSCbEMJw=="
"version": "3.20.5",
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.20.5.tgz",
"integrity": "sha512-DNmJQ1qVkW8xk3wI2VfupOS7IYTHNjYaYX+L4s8B8ksdtoHOx3RsRqJUMIQByojHgb3PwEt+zGdIUwxJ4N5pwg=="
},
"tiny-emitter": {
"version": "2.0.2",
+2 -2
View File
@@ -1,8 +1,8 @@
{
"name": "drf-yasg",
"dependencies": {
"redoc": "^2.0.0-rc.0",
"swagger-ui-dist": "^3.20.4"
"redoc": "^2.0.0-rc.2",
"swagger-ui-dist": "^3.20.5"
},
"repository": {
"type": "git",
-1
View File
@@ -1,3 +1,2 @@
# requirements for the validation feature
flex>=6.11.1
swagger-spec-validator>=2.1.0
+4
View File
@@ -15,8 +15,12 @@ logger = logging.getLogger(__name__)
def _validate_flex(spec):
try:
from flex.core import parse as validate_flex
from flex.exceptions import ValidationError
except ImportError:
return
try:
validate_flex(spec)
except ValidationError as ex:
+1 -1
View File
@@ -230,7 +230,7 @@ class OpenAPISchemaGenerator(object):
def get_schema(self, request=None, public=False):
"""Generate a :class:`.Swagger` object representing the API schema.
:param request: the request used for filtering accesible endpoints and finding the spec URI
:param request: the request used for filtering accessible endpoints and finding the spec URI
:type request: rest_framework.request.Request or None
:param bool public: if True, all endpoints are included regardless of access through `request`
+42 -17
View File
@@ -181,7 +181,7 @@ def get_queryset_from_view(view, serializer=None):
if queryset is not None and serializer is not None:
# make sure the view is actually using *this* serializer
assert type(serializer) == view.get_serializer_class()
assert type(serializer) == call_view_method(view, 'get_serializer_class', 'serializer_class')
return queryset
except Exception: # pragma: no cover
@@ -455,18 +455,39 @@ def decimal_return_type():
return openapi.TYPE_STRING if rest_framework_settings.COERCE_DECIMAL_TO_STRING else openapi.TYPE_NUMBER
raw_type_info = [
def get_origin_type(hint_class):
return getattr(hint_class, '__origin__', None) or hint_class
def hint_class_issubclass(hint_class, check_class):
origin_type = get_origin_type(hint_class)
return inspect.isclass(origin_type) and issubclass(origin_type, check_class)
hinting_type_info = [
(bool, (openapi.TYPE_BOOLEAN, None)),
(int, (openapi.TYPE_INTEGER, None)),
(str, (openapi.TYPE_STRING, None)),
(float, (openapi.TYPE_NUMBER, None)),
(dict, (openapi.TYPE_OBJECT, None)),
(Decimal, (decimal_return_type, openapi.FORMAT_DECIMAL)),
(uuid.UUID, (openapi.TYPE_STRING, openapi.FORMAT_UUID)),
(datetime.datetime, (openapi.TYPE_STRING, openapi.FORMAT_DATETIME)),
(datetime.date, (openapi.TYPE_STRING, openapi.FORMAT_DATE)),
# TODO - support typing.List etc
]
hinting_type_info = raw_type_info
if typing:
def inspect_collection_hint_class(hint_class):
args = hint_class.__args__
child_class = args[0] if args else str
child_type_info = get_basic_type_info_from_hint(child_class) or {'type': openapi.TYPE_STRING}
return OrderedDict([
('type', openapi.TYPE_ARRAY),
('items', openapi.Items(**child_type_info)),
])
hinting_type_info.append(((typing.Sequence, typing.AbstractSet), inspect_collection_hint_class))
def get_basic_type_info_from_hint(hint_class):
@@ -478,27 +499,31 @@ def get_basic_type_info_from_hint(hint_class):
:return: the extracted attributes as a dictionary, or ``None`` if the field type is not known
:rtype: OrderedDict
"""
if typing and get_origin_type(hint_class) == typing.Union:
# Optional is implemented as Union[T, None]
if len(hint_class.__args__) == 2 and hint_class.__args__[1] == type(None): # noqa: E721
child_type = hint_class.__args__[0]
result = get_basic_type_info_from_hint(child_type)
result['x-nullable'] = True
return result
for check_class, type_format in hinting_type_info:
if issubclass(hint_class, check_class):
swagger_type, format = type_format
if callable(swagger_type):
swagger_type = swagger_type()
# if callable(format):
# format = format(klass)
break
else: # pragma: no cover
return None
pattern = None
for check_class, info in hinting_type_info:
if hint_class_issubclass(hint_class, check_class):
if callable(info):
return info(hint_class)
result = OrderedDict([
swagger_type, format = info
if callable(swagger_type):
swagger_type = swagger_type()
return OrderedDict([
('type', swagger_type),
('format', format),
('pattern', pattern)
])
return result
return None
class SerializerMethodFieldInspector(FieldInspector):
+7 -13
View File
@@ -1,5 +1,4 @@
import logging
import warnings
from collections import OrderedDict
from rest_framework.request import is_form_media_type
@@ -22,22 +21,17 @@ class SwaggerAutoSchema(ViewInspector):
super(SwaggerAutoSchema, self).__init__(view, path, method, components, request, overrides)
self._sch = AutoSchema()
self._sch.view = view
self._summary_and_description_compat()
def _summary_and_description_compat(self):
# TODO: remove in 1.13
# TODO: remove in 1.14
base_methods = (SwaggerAutoSchema.get_summary, SwaggerAutoSchema.get_description)
self_methods = (type(self).get_summary, type(self).get_description)
if self_methods != base_methods:
warnings.warn(
"`SwaggerAutoSchema` methods `get_summary` and `get_description` are deprecated and "
"will be removed in drf-yasg 1.13. Override `get_summary_and_description` instead.",
DeprecationWarning, stacklevel=2
raise NotImplementedError(
"`SwaggerAutoSchema` methods `get_summary` and `get_description` were removed in "
"drf-yasg 1.13 and will have no effect. Override `get_summary_and_description` instead."
)
# if get_summary or get_description are overriden by a child class,
# we must call them for backwards compatibility
return self.get_summary(), self.get_description()
return self.get_summary_and_description()
def get_operation(self, operation_keys):
consumes = self.get_consumes()
@@ -50,7 +44,7 @@ class SwaggerAutoSchema(ViewInspector):
parameters = self.add_manual_parameters(parameters)
operation_id = self.get_operation_id(operation_keys)
summary, description = self._summary_and_description_compat()
summary, description = self.get_summary_and_description()
security = self.get_security()
assert security is None or isinstance(security, list), "security must be a list of security requirement objects"
deprecated = self.is_deprecated()
@@ -363,7 +357,7 @@ class SwaggerAutoSchema(ViewInspector):
description = description.strip().replace('\r', '')
if description and (summary is None):
# description from docstring ... do summary magic
# description from docstring... do summary magic
summary, description = self.split_summary_from_description(description)
return summary, description
+2 -2
View File
@@ -10,7 +10,7 @@ from django.urls import get_script_prefix
from django.utils.functional import Promise
from inflection import camelize
from .utils import filter_none, force_real_str
from .utils import dict_has_ordered_keys, filter_none, force_real_str
try:
from collections import abc as collections_abc
@@ -145,7 +145,7 @@ class SwaggerDict(OrderedDict):
result = OrderedDict()
memo[id(obj)] = result
items = obj.items()
if not isinstance(obj, OrderedDict):
if not dict_has_ordered_keys(obj):
items = sorted(items)
for attr, val in items:
result[attr] = SwaggerDict._as_odict(val, memo)
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+16 -4
View File
@@ -1,5 +1,6 @@
import inspect
import logging
import sys
from collections import OrderedDict
from django.db import models
@@ -389,8 +390,7 @@ def get_produces(renderer_classes):
def decimal_as_float(field):
"""
Returns true if ``field`` is a django-rest-framework DecimalField and its ``coerce_to_string`` attribute or the
"""Returns true if ``field`` is a django-rest-framework DecimalField and its ``coerce_to_string`` attribute or the
``COERCE_DECIMAL_TO_STRING`` setting is set to ``False``.
:rtype: bool
@@ -401,8 +401,7 @@ def decimal_as_float(field):
def get_serializer_ref_name(serializer):
"""
Get serializer's ref_name (or None for ModelSerializer if it is named 'NestedSerializer')
"""Get serializer's ref_name (or None for ModelSerializer if it is named 'NestedSerializer')
:param serializer: Serializer instance
:return: Serializer's ``ref_name`` or ``None`` for inline serializer
@@ -469,3 +468,16 @@ def get_field_default(field):
default = serializers.empty
return default
def dict_has_ordered_keys(obj):
"""Check if a given object is a dict that maintains insertion order.
:param obj: the dict object to check
:rtype: bool
"""
if sys.version_info >= (3, 7):
# the Python 3.7 language spec says that dict must maintain insertion order.
return isinstance(obj, dict)
return isinstance(obj, OrderedDict)
+1 -1
View File
@@ -57,7 +57,7 @@ def get_schema_view(info=None, url=None, patterns=None, urlconf=None, public=Fal
:param patterns: same as :class:`.OpenAPISchemaGenerator`
:param urlconf: same as :class:`.OpenAPISchemaGenerator`
:param bool public: if False, includes only the endpoints that are accesible by the user viewing the schema
:param list validators: a list of validator names to apply; allowed values are ``flex``, ``ssv``
:param list validators: a list of validator names to apply; the only allowed value is ``ssv``, for now
:param type generator_class: schema generator class to use; should be a subclass of :class:`.OpenAPISchemaGenerator`
:param tuple authentication_classes: authentication classes for the schema view itself
:param tuple permission_classes: permission classes for the schema view itself
+10 -10
View File
@@ -116,24 +116,24 @@ SWAGGER_SETTINGS = {
'type': 'basic'
},
'Bearer': {
'type': 'apiKey',
'in': 'header',
'name': 'Authorization',
'in': 'header'
},
'Query': {
'type': 'apiKey',
'name': 'auth',
'in': 'query'
},
'OAuth2 password': {
'type': 'oauth2',
'flow': 'password',
'tokenUrl': OAUTH2_TOKEN_URL,
'scopes': {
'read': 'Read everything.',
'write': 'Write everything,',
}
}
},
'tokenUrl': OAUTH2_TOKEN_URL,
'type': 'oauth2',
},
'Query': {
'in': 'query',
'name': 'auth',
'type': 'apiKey',
},
},
'OAUTH2_REDIRECT_URL': OAUTH2_REDIRECT_URL,
'OAUTH2_CONFIG': {
+6 -1
View File
@@ -57,10 +57,15 @@ def swagger_dict(swagger, codec_json):
@pytest.fixture
def validate_schema():
def validate_schema(swagger):
try:
from flex.core import parse as validate_flex
from swagger_spec_validator.validator20 import validate_spec as validate_ssv
validate_flex(copy.deepcopy(swagger))
except ImportError:
pass
from swagger_spec_validator.validator20 import validate_spec as validate_ssv
validate_ssv(copy.deepcopy(swagger))
return validate_schema
@@ -0,0 +1,39 @@
import uuid
import pytest
from drf_yasg import openapi
from drf_yasg.inspectors.field import get_basic_type_info_from_hint
try:
import typing
from typing import Dict, List, Union, Optional, Set
except ImportError:
typing = None
if typing:
@pytest.mark.parametrize('hint_class, expected_swagger_type_info', [
(int, {'type': openapi.TYPE_INTEGER, 'format': None}),
(str, {'type': openapi.TYPE_STRING, 'format': None}),
(bool, {'type': openapi.TYPE_BOOLEAN, 'format': None}),
(dict, {'type': openapi.TYPE_OBJECT, 'format': None}),
(Dict[int, int], {'type': openapi.TYPE_OBJECT, 'format': None}),
(uuid.UUID, {'type': openapi.TYPE_STRING, 'format': openapi.FORMAT_UUID}),
(List[int], {'type': openapi.TYPE_ARRAY, 'items': openapi.Items(openapi.TYPE_INTEGER)}),
(List[str], {'type': openapi.TYPE_ARRAY, 'items': openapi.Items(openapi.TYPE_STRING)}),
(List[bool], {'type': openapi.TYPE_ARRAY, 'items': openapi.Items(openapi.TYPE_BOOLEAN)}),
(Set[int], {'type': openapi.TYPE_ARRAY, 'items': openapi.Items(openapi.TYPE_INTEGER)}),
(Optional[bool], {'type': openapi.TYPE_BOOLEAN, 'format': None, 'x-nullable': True}),
(Optional[List[int]], {
'type': openapi.TYPE_ARRAY, 'items': openapi.Items(openapi.TYPE_INTEGER), 'x-nullable': True
}),
(Union[List[int], type(None)], {
'type': openapi.TYPE_ARRAY, 'items': openapi.Items(openapi.TYPE_INTEGER), 'x-nullable': True
}),
# Following cases are not 100% correct, but it should work somehow and not crash.
(Union[int, float], None),
(List, {'type': openapi.TYPE_ARRAY, 'items': openapi.Items(openapi.TYPE_STRING)}),
])
def test_get_basic_type_info_from_hint(hint_class, expected_swagger_type_info):
type_info = get_basic_type_info_from_hint(hint_class)
assert type_info == expected_swagger_type_info