Skip to content

Commit 57c22c2

Browse files
committed
add support for building swagger/OpenAPI JSON
to build, run `make swagger`; a file named `swagger.json` will be written to the current working directory
1 parent c61efc0 commit 57c22c2

35 files changed

+379
-29
lines changed

Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,12 @@ pyflakes: reports
363363
pylint: reports
364364
@(set -o pipefail && $@ | reports/$@.report)
365365

366+
swagger: reports
367+
@if [ "$(VENV_BASE)" ]; then \
368+
. $(VENV_BASE)/awx/bin/activate; \
369+
fi; \
370+
(set -o pipefail && py.test awx/main/tests/docs --release=$(RELEASE_VERSION) | tee reports/$@.report)
371+
366372
check: flake8 pep8 # pyflakes pylint
367373

368374
TEST_DIRS ?= awx/main/tests/unit awx/main/tests/functional awx/conf/tests awx/sso/tests

awx/api/generics.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
from rest_framework import views
2929

3030
# AWX
31+
from awx.api.swagger import AutoSchema
3132
from awx.api.filters import FieldLookupBackend
3233
from awx.main.models import * # noqa
3334
from awx.main.access import access_registry
@@ -93,6 +94,7 @@ def get_view_description(cls, request, html=False):
9394

9495
class APIView(views.APIView):
9596

97+
schema = AutoSchema()
9698
versioning_class = URLPathVersioning
9799

98100
def initialize_request(self, request, *args, **kwargs):
@@ -176,7 +178,7 @@ def get_view_description(self, html=False):
176178
and in the browsable API.
177179
"""
178180
func = self.settings.VIEW_DESCRIPTION_FUNCTION
179-
return func(self.__class__, self._request, html)
181+
return func(self.__class__, getattr(self, '_request', None), html)
180182

181183
def get_description_context(self):
182184
return {
@@ -197,6 +199,7 @@ def get_description_context(self):
197199
'new_in_330': getattr(self, 'new_in_330', False),
198200
'new_in_api_v2': getattr(self, 'new_in_api_v2', False),
199201
'deprecated': getattr(self, 'deprecated', False),
202+
'swagger_method': getattr(self.request, 'swagger_method', None),
200203
}
201204

202205
def get_description(self, request, html=False):

awx/api/swagger.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import warnings
2+
3+
from coreapi.document import Object, Link
4+
5+
from rest_framework import exceptions
6+
from rest_framework.permissions import AllowAny
7+
from rest_framework.renderers import CoreJSONRenderer
8+
from rest_framework.response import Response
9+
from rest_framework.schemas import SchemaGenerator, AutoSchema as DRFAuthSchema
10+
from rest_framework.views import APIView
11+
12+
from rest_framework_swagger import renderers
13+
14+
15+
class AutoSchema(DRFAuthSchema):
16+
17+
def get_link(self, path, method, base_url):
18+
link = super(AutoSchema, self).get_link(path, method, base_url)
19+
try:
20+
serializer = self.view.get_serializer()
21+
except Exception:
22+
serializer = None
23+
warnings.warn('{}.get_serializer() raised an exception during '
24+
'schema generation. Serializer fields will not be '
25+
'generated for {} {}.'
26+
.format(self.view.__class__.__name__, method, path))
27+
28+
# auto-generate a topic/tag for the serializer based on its model
29+
if hasattr(self.view, 'swagger_topic'):
30+
link.__dict__['topic'] = str(self.view.swagger_topic).title()
31+
elif serializer and hasattr(serializer, 'Meta'):
32+
link.__dict__['topic'] = str(
33+
serializer.Meta.model._meta.verbose_name_plural
34+
).title()
35+
elif hasattr(self.view, 'model'):
36+
link.__dict__['topic'] = str(self.view.model._meta.verbose_name_plural).title()
37+
else:
38+
warnings.warn('Could not determine a Swagger tag for path {}'.format(path))
39+
return link
40+
41+
def get_description(self, path, method):
42+
self.view._request = self.view.request
43+
setattr(self.view.request, 'swagger_method', method)
44+
description = super(AutoSchema, self).get_description(path, method)
45+
return description
46+
47+
48+
class SwaggerSchemaView(APIView):
49+
_ignore_model_permissions = True
50+
exclude_from_schema = True
51+
permission_classes = [AllowAny]
52+
renderer_classes = [
53+
CoreJSONRenderer,
54+
renderers.OpenAPIRenderer,
55+
renderers.SwaggerUIRenderer
56+
]
57+
58+
def get(self, request):
59+
generator = SchemaGenerator(
60+
title='Ansible Tower API',
61+
patterns=None,
62+
urlconf=None
63+
)
64+
schema = generator.get_schema(request=request)
65+
66+
# By default, DRF OpenAPI serialization places all endpoints in
67+
# a single node based on their root path (/api). Instead, we want to
68+
# group them by topic/tag so that they're categorized in the rendered
69+
# output
70+
document = schema._data.pop('api')
71+
for path, node in document.items():
72+
if isinstance(node, Object):
73+
for action in node.values():
74+
topic = getattr(action, 'topic', None)
75+
if topic:
76+
schema._data.setdefault(topic, Object())
77+
schema._data[topic]._data[path] = node
78+
elif isinstance(node, Link):
79+
topic = getattr(node, 'topic', None)
80+
if topic:
81+
schema._data.setdefault(topic, Object())
82+
schema._data[topic]._data[path] = node
83+
84+
if not schema:
85+
raise exceptions.ValidationError(
86+
'The schema generator did not return a schema Document'
87+
)
88+
89+
return Response(schema)
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1+
{% ifmeth GET %}
12
# Retrieve {{ model_verbose_name|title }} Variable Data:
23

3-
Make a GET request to this resource to retrieve all variables defined for this
4+
Make a GET request to this resource to retrieve all variables defined for a
45
{{ model_verbose_name }}.
6+
{% endifmeth %}
57

8+
{% ifmeth PUT PATCH %}
69
# Update {{ model_verbose_name|title }} Variable Data:
710

8-
Make a PUT request to this resource to update variables defined for this
11+
Make a PUT or PATCH request to this resource to update variables defined for a
912
{{ model_verbose_name }}.
13+
{% endifmeth %}

awx/api/templates/api/group_all_hosts_list.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# List All {{ model_verbose_name_plural|title }} for this {{ parent_model_verbose_name|title }}:
1+
# List All {{ model_verbose_name_plural|title }} for {{ parent_model_verbose_name|title|anora }}:
22

33
Make a GET request to this resource to retrieve a list of all
44
{{ model_verbose_name_plural }} directly or indirectly belonging to this

awx/api/templates/api/group_potential_children_list.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# List Potential Child Groups for this {{ parent_model_verbose_name|title }}:
1+
# List Potential Child Groups for {{ parent_model_verbose_name|title|anora }}:
22

33
Make a GET request to this resource to retrieve a list of
44
{{ model_verbose_name_plural }} available to be added as children of the

awx/api/templates/api/host_all_groups_list.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# List All {{ model_verbose_name_plural|title }} for this {{ parent_model_verbose_name|title }}:
1+
# List All {{ model_verbose_name_plural|title }} for {{ parent_model_verbose_name|title|anora }}:
22

33
Make a GET request to this resource to retrieve a list of all
44
{{ model_verbose_name_plural }} of which the selected

awx/api/templates/api/host_fact_compare_view.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# List Fact Scans for a Host Specific Host Scan
2+
13
Make a GET request to this resource to retrieve system tracking data for a particular scan
24

35
You may filter by datetime:
@@ -8,4 +10,4 @@ and module
810

911
`?datetime=2015-06-01&module=ansible`
1012

11-
{% include "api/_new_in_awx.md" %}
13+
{% include "api/_new_in_awx.md" %}

awx/api/templates/api/host_fact_versions_list.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# List Fact Scans for a Host by Module and Date
2+
13
Make a GET request to this resource to retrieve system tracking scans by module and date/time
24

35
You may filter scan runs using the `from` and `to` properties:
@@ -8,4 +10,4 @@ You may also filter by module
810

911
`?module=packages`
1012

11-
{% include "api/_new_in_awx.md" %}
13+
{% include "api/_new_in_awx.md" %}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# List Red Hat Insights for a Host

awx/api/templates/api/inventory_root_groups_list.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# List Root {{ model_verbose_name_plural|title }} for this {{ parent_model_verbose_name|title }}:
1+
# List Root {{ model_verbose_name_plural|title }} for {{ parent_model_verbose_name|title|anora }}:
22

33
Make a GET request to this resource to retrieve a list of root (top-level)
44
{{ model_verbose_name_plural }} associated with this

awx/api/templates/api/inventory_tree_view.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Group Tree for this {{ model_verbose_name|title }}:
1+
# Group Tree for {{ model_verbose_name|title|anora }}:
22

33
Make a GET request to this resource to retrieve a hierarchical view of groups
44
associated with the selected {{ model_verbose_name }}.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# Retrieve {{ model_verbose_name|title }} Playbooks:
22

33
Make GET request to this resource to retrieve a list of playbooks available
4-
for this {{ model_verbose_name }}.
4+
for {{ model_verbose_name|anora }}.

awx/api/templates/api/retrieve_api_view.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
### Note: starting from api v2, this resource object can be accessed via its named URL.
33
{% endif %}
44

5-
# Retrieve {{ model_verbose_name|title }}:
5+
# Retrieve {{ model_verbose_name|title|anora }}:
66

77
Make GET request to this resource to retrieve a single {{ model_verbose_name }}
88
record containing the following fields:

awx/api/templates/api/retrieve_destroy_api_view.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
### Note: starting from api v2, this resource object can be accessed via its named URL.
33
{% endif %}
44

5-
# Retrieve {{ model_verbose_name|title }}:
5+
# Retrieve {{ model_verbose_name|title|anora }}:
66

77
Make GET request to this resource to retrieve a single {{ model_verbose_name }}
88
record containing the following fields:
99

1010
{% include "api/_result_fields_common.md" %}
1111

12-
# Delete {{ model_verbose_name|title }}:
12+
# Delete {{ model_verbose_name|title|anora }}:
1313

1414
Make a DELETE request to this resource to delete this {{ model_verbose_name }}.
1515

awx/api/templates/api/retrieve_update_api_view.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@
22
### Note: starting from api v2, this resource object can be accessed via its named URL.
33
{% endif %}
44

5-
# Retrieve {{ model_verbose_name|title }}:
5+
{% ifmeth GET %}
6+
# Retrieve {{ model_verbose_name|title|anora }}:
67

78
Make GET request to this resource to retrieve a single {{ model_verbose_name }}
89
record containing the following fields:
910

1011
{% include "api/_result_fields_common.md" %}
12+
{% endifmeth %}
1113

12-
# Update {{ model_verbose_name|title }}:
14+
{% ifmeth PUT PATCH %}
15+
# Update {{ model_verbose_name|title|anora }}:
1316

1417
Make a PUT or PATCH request to this resource to update this
1518
{{ model_verbose_name }}. The following fields may be modified:
@@ -21,5 +24,6 @@ Make a PUT or PATCH request to this resource to update this
2124
For a PUT request, include **all** fields in the request.
2225

2326
For a PATCH request, include only the fields that are being modified.
27+
{% endifmeth %}
2428

2529
{% include "api/_new_in_awx.md" %}

awx/api/templates/api/retrieve_update_destroy_api_view.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@
22
### Note: starting from api v2, this resource object can be accessed via its named URL.
33
{% endif %}
44

5-
# Retrieve {{ model_verbose_name|title }}:
5+
{% ifmeth GET %}
6+
# Retrieve {{ model_verbose_name|title|anora }}:
67

78
Make GET request to this resource to retrieve a single {{ model_verbose_name }}
89
record containing the following fields:
910

1011
{% include "api/_result_fields_common.md" %}
12+
{% endifmeth %}
1113

12-
# Update {{ model_verbose_name|title }}:
14+
{% ifmeth PUT PATCH %}
15+
# Update {{ model_verbose_name|title|anora }}:
1316

1417
Make a PUT or PATCH request to this resource to update this
1518
{{ model_verbose_name }}. The following fields may be modified:
@@ -21,9 +24,12 @@ Make a PUT or PATCH request to this resource to update this
2124
For a PUT request, include **all** fields in the request.
2225

2326
For a PATCH request, include only the fields that are being modified.
27+
{% endifmeth %}
2428

25-
# Delete {{ model_verbose_name|title }}:
29+
{% ifmeth DELETE %}
30+
# Delete {{ model_verbose_name|title|anora }}:
2631

2732
Make a DELETE request to this resource to delete this {{ model_verbose_name }}.
33+
{% endifmeth %}
2834

2935
{% include "api/_new_in_awx.md" %}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
# List {{ model_verbose_name_plural|title }} for this {{ parent_model_verbose_name|title }}:
1+
{% ifmeth GET %}
2+
# List {{ model_verbose_name_plural|title }} for {{ parent_model_verbose_name|title|anora }}:
23

34
Make a GET request to this resource to retrieve a list of
45
{{ model_verbose_name_plural }} associated with the selected
56
{{ parent_model_verbose_name }}.
67

78
{% include "api/_list_common.md" %}
9+
{% endifmeth %}
810

911
{% include "api/_new_in_awx.md" %}

awx/api/templates/api/sub_list_create_api_view.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{% include "api/sub_list_api_view.md" %}
22

3-
# Create {{ model_verbose_name_plural|title }} for this {{ parent_model_verbose_name|title }}:
3+
# Create {{ model_verbose_name_plural|title }} for {{ parent_model_verbose_name|title|anora }}:
44

55
Make a POST request to this resource with the following {{ model_verbose_name }}
66
fields to create a new {{ model_verbose_name }} associated with this
@@ -25,7 +25,7 @@ delete the associated {{ model_verbose_name }}.
2525
}
2626

2727
{% else %}
28-
# Add {{ model_verbose_name_plural|title }} for this {{ parent_model_verbose_name|title }}:
28+
# Add {{ model_verbose_name_plural|title }} for {{ parent_model_verbose_name|title|anora }}:
2929

3030
Make a POST request to this resource with only an `id` field to associate an
3131
existing {{ model_verbose_name }} with this {{ parent_model_verbose_name }}.
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1-
# List Roles for this Team:
1+
# List Roles for a Team:
22

3+
{% ifmeth GET %}
34
Make a GET request to this resource to retrieve a list of roles associated with the selected team.
45

56
{% include "api/_list_common.md" %}
7+
{% endifmeth %}
68

9+
{% ifmeth POST %}
710
# Associate Roles with this Team:
811

912
Make a POST request to this resource to add or remove a role from this team. The following fields may be modified:
1013

1114
* `id`: The Role ID to add to the team. (int, required)
1215
* `disassociate`: Provide if you want to remove the role. (any value, optional)
16+
{% endifmeth %}
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1-
# List Roles for this User:
1+
# List Roles for a User:
22

3+
{% ifmeth GET %}
34
Make a GET request to this resource to retrieve a list of roles associated with the selected user.
45

56
{% include "api/_list_common.md" %}
7+
{% endifmeth %}
68

9+
{% ifmeth POST %}
710
# Associate Roles with this User:
811

912
Make a POST request to this resource to add or remove a role from this user. The following fields may be modified:
1013

1114
* `id`: The Role ID to add to the user. (int, required)
1215
* `disassociate`: Provide if you want to remove the role. (any value, optional)
16+
{% endifmeth %}

0 commit comments

Comments
 (0)