From 10974159b552cafdcf1ad33ba8d7fbc6ad66cd31 Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Fri, 2 Feb 2018 15:04:34 -0500 Subject: [PATCH] add support for marking Swagger paths deprecated --- awx/api/swagger.py | 16 ++++++++++++- .../tests/docs/test_swagger_generation.py | 23 +++++++++++-------- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/awx/api/swagger.py b/awx/api/swagger.py index 9e504d4297..b67f2d4a26 100644 --- a/awx/api/swagger.py +++ b/awx/api/swagger.py @@ -1,3 +1,4 @@ +import json import warnings from coreapi.document import Object, Link @@ -25,6 +26,8 @@ class AutoSchema(DRFAuthSchema): 'generated for {} {}.' .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 if hasattr(self.view, 'swagger_topic'): link.__dict__['topic'] = str(self.view.swagger_topic).title() @@ -62,6 +65,9 @@ class SwaggerSchemaView(APIView): urlconf=None ) 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 # a single node based on their root path (/api). Instead, we want to @@ -75,6 +81,11 @@ class SwaggerSchemaView(APIView): if topic: schema._data.setdefault(topic, Object()) 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): topic = getattr(node, 'topic', None) if topic: @@ -86,4 +97,7 @@ class SwaggerSchemaView(APIView): 'The schema generator did not return a schema Document' ) - return Response(schema) + return Response( + schema, + headers={'X-Deprecated-Paths': json.dumps(_deprecated)} + ) diff --git a/awx/main/tests/docs/test_swagger_generation.py b/awx/main/tests/docs/test_swagger_generation.py index dd49f2f8cb..3319a3c19d 100644 --- a/awx/main/tests/docs/test_swagger_generation.py +++ b/awx/main/tests/docs/test_swagger_generation.py @@ -42,27 +42,27 @@ class TestSwaggerGeneration(): url = drf_reverse('api:swagger_view') + '?format=openapi' response = get(url, user=admin) 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 {}) self.__class__.JSON = data - def _lookup_display_name(self, method, path): - return path - def test_transform_metadata(self, release): """ This test takes the JSON output from the swagger endpoint and applies various transformations to it. """ - self.__class__.JSON['info']['version'] = release - self.__class__.JSON['host'] = None - self.__class__.JSON['schemes'] = ['https'] - self.__class__.JSON['produces'] = ['application/json'] - self.__class__.JSON['consumes'] = ['application/json'] + JSON = self.__class__.JSON + JSON['info']['version'] = release + JSON['host'] = None + JSON['schemes'] = ['https'] + JSON['produces'] = ['application/json'] + JSON['consumes'] = ['application/json'] # Inject a top-level description into the OpenAPI document if os.path.exists(description_file): 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 if os.path.exists(config_file): @@ -72,9 +72,10 @@ class TestSwaggerGeneration(): tag = {'name': category['name']} if 'description' in category: tag['description'] = category['description'] - self.__class__.JSON.setdefault('tags', []).append(tag) + JSON.setdefault('tags', []).append(tag) revised_paths = {} + deprecated_paths = JSON.pop('deprecated_paths', []) for path, node in self.__class__.JSON['paths'].items(): # change {version} in paths to the actual default API version (e.g., v2) revised_paths[path.replace( @@ -82,6 +83,8 @@ class TestSwaggerGeneration(): settings.REST_FRAMEWORK['DEFAULT_VERSION'] )] = node for method in node: + if path in deprecated_paths: + node[method]['deprecated'] = True if 'description' in node[method]: # Pop off the first line and use that as the summary lines = node[method]['description'].splitlines()