add support for marking Swagger paths deprecated

This commit is contained in:
Ryan Petrello
2018-02-02 15:04:34 -05:00
parent ac7c5f8648
commit 10974159b5
2 changed files with 28 additions and 11 deletions

View File

@@ -1,3 +1,4 @@
import json
import warnings import warnings
from coreapi.document import Object, Link from coreapi.document import Object, Link
@@ -25,6 +26,8 @@ class AutoSchema(DRFAuthSchema):
'generated for {} {}.' 'generated for {} {}.'
.format(self.view.__class__.__name__, method, path)) .format(self.view.__class__.__name__, method, path))
link.__dict__['deprecated'] = getattr(self.view, 'deprecated', False)
# auto-generate a topic/tag for the serializer based on its model # auto-generate a topic/tag for the serializer based on its model
if hasattr(self.view, 'swagger_topic'): if hasattr(self.view, 'swagger_topic'):
link.__dict__['topic'] = str(self.view.swagger_topic).title() link.__dict__['topic'] = str(self.view.swagger_topic).title()
@@ -62,6 +65,9 @@ class SwaggerSchemaView(APIView):
urlconf=None urlconf=None
) )
schema = generator.get_schema(request=request) schema = generator.get_schema(request=request)
# python core-api doesn't support the deprecation yet, so track it
# ourselves and return it in a response header
_deprecated = []
# By default, DRF OpenAPI serialization places all endpoints in # By default, DRF OpenAPI serialization places all endpoints in
# a single node based on their root path (/api). Instead, we want to # a single node based on their root path (/api). Instead, we want to
@@ -75,6 +81,11 @@ class SwaggerSchemaView(APIView):
if topic: if topic:
schema._data.setdefault(topic, Object()) schema._data.setdefault(topic, Object())
schema._data[topic]._data[path] = node schema._data[topic]._data[path] = node
if isinstance(action, Object):
for link in action.links.values():
if link.deprecated:
_deprecated.append(link.url)
elif isinstance(node, Link): elif isinstance(node, Link):
topic = getattr(node, 'topic', None) topic = getattr(node, 'topic', None)
if topic: if topic:
@@ -86,4 +97,7 @@ class SwaggerSchemaView(APIView):
'The schema generator did not return a schema Document' 'The schema generator did not return a schema Document'
) )
return Response(schema) return Response(
schema,
headers={'X-Deprecated-Paths': json.dumps(_deprecated)}
)

View File

@@ -42,27 +42,27 @@ class TestSwaggerGeneration():
url = drf_reverse('api:swagger_view') + '?format=openapi' url = drf_reverse('api:swagger_view') + '?format=openapi'
response = get(url, user=admin) response = get(url, user=admin)
data = generate_swagger_object(response.data) data = generate_swagger_object(response.data)
if response.has_header('X-Deprecated-Paths'):
data['deprecated_paths'] = json.loads(response['X-Deprecated-Paths'])
data.update(response.accepted_renderer.get_customizations() or {}) data.update(response.accepted_renderer.get_customizations() or {})
self.__class__.JSON = data self.__class__.JSON = data
def _lookup_display_name(self, method, path):
return path
def test_transform_metadata(self, release): def test_transform_metadata(self, release):
""" """
This test takes the JSON output from the swagger endpoint and applies This test takes the JSON output from the swagger endpoint and applies
various transformations to it. various transformations to it.
""" """
self.__class__.JSON['info']['version'] = release JSON = self.__class__.JSON
self.__class__.JSON['host'] = None JSON['info']['version'] = release
self.__class__.JSON['schemes'] = ['https'] JSON['host'] = None
self.__class__.JSON['produces'] = ['application/json'] JSON['schemes'] = ['https']
self.__class__.JSON['consumes'] = ['application/json'] JSON['produces'] = ['application/json']
JSON['consumes'] = ['application/json']
# Inject a top-level description into the OpenAPI document # Inject a top-level description into the OpenAPI document
if os.path.exists(description_file): if os.path.exists(description_file):
with open(description_file, 'r') as f: with open(description_file, 'r') as f:
self.__class__.JSON['info']['description'] = f.read() JSON['info']['description'] = f.read()
# Write tags in the order we want them sorted # Write tags in the order we want them sorted
if os.path.exists(config_file): if os.path.exists(config_file):
@@ -72,9 +72,10 @@ class TestSwaggerGeneration():
tag = {'name': category['name']} tag = {'name': category['name']}
if 'description' in category: if 'description' in category:
tag['description'] = category['description'] tag['description'] = category['description']
self.__class__.JSON.setdefault('tags', []).append(tag) JSON.setdefault('tags', []).append(tag)
revised_paths = {} revised_paths = {}
deprecated_paths = JSON.pop('deprecated_paths', [])
for path, node in self.__class__.JSON['paths'].items(): for path, node in self.__class__.JSON['paths'].items():
# change {version} in paths to the actual default API version (e.g., v2) # change {version} in paths to the actual default API version (e.g., v2)
revised_paths[path.replace( revised_paths[path.replace(
@@ -82,6 +83,8 @@ class TestSwaggerGeneration():
settings.REST_FRAMEWORK['DEFAULT_VERSION'] settings.REST_FRAMEWORK['DEFAULT_VERSION']
)] = node )] = node
for method in node: for method in node:
if path in deprecated_paths:
node[method]['deprecated'] = True
if 'description' in node[method]: if 'description' in node[method]:
# Pop off the first line and use that as the summary # Pop off the first line and use that as the summary
lines = node[method]['description'].splitlines() lines = node[method]['description'].splitlines()