Always wrap response in a component schema, also paginated once, to improve processability of API code generators #7299

This commit is contained in:
Jonatan Zint 2020-04-29 15:54:45 +02:00
parent 900773ad06
commit 558dd0e19e
2 changed files with 67 additions and 32 deletions

View File

@ -189,6 +189,8 @@ class AutoSchema(ViewInspector):
Return components with their properties from the serializer.
"""
components = {}
if method.lower() == 'delete':
return {}
@ -197,10 +199,28 @@ class AutoSchema(ViewInspector):
if not isinstance(serializer, serializers.Serializer):
return {}
component_name = self.get_component_name(serializer)
item_component_name = self.get_component_name(serializer)
item_schema = self.map_serializer(serializer)
components[item_component_name] = item_schema
content = self.map_serializer(serializer)
return {component_name: content}
response_component_name = self._get_response_component_name(
self.get_operation_id(path, method)
)
if is_list_view(path, method, self.view):
response_component_schema = {
'type': 'array',
'items': self._get_serializer_reference(serializer),
}
paginator = self.get_paginator()
if paginator:
response_component_schema = paginator.get_paginated_response_schema(response_component_schema)
else:
response_component_schema = self._get_serializer_reference(serializer)
components[response_component_name] = response_component_schema
return components
def _to_camel_case(self, snake_str):
components = snake_str.split('_')
@ -613,9 +633,17 @@ class AutoSchema(ViewInspector):
.format(view.__class__.__name__, method, path))
return None
def _get_reference(self, serializer):
def _get_serializer_reference(self, serializer):
return {'$ref': '#/components/schemas/{}'.format(self.get_component_name(serializer))}
@staticmethod
def _get_response_component_name(operation_id):
operation_id = operation_id[0].upper() + operation_id[1:]
return operation_id + 'Response'
def _get_response_reference(self, operation_id):
return {'$ref': '#/components/schemas/{0}'.format(self._get_response_component_name(operation_id))}
def get_request_body(self, path, method):
if method not in ('PUT', 'PATCH', 'POST'):
return {}
@ -627,7 +655,7 @@ class AutoSchema(ViewInspector):
if not isinstance(serializer, serializers.Serializer):
item_schema = {}
else:
item_schema = self._get_reference(serializer)
item_schema = self._get_serializer_reference(serializer)
return {
'content': {
@ -649,20 +677,17 @@ class AutoSchema(ViewInspector):
serializer = self.get_serializer(path, method)
if not isinstance(serializer, serializers.Serializer):
item_schema = {}
if is_list_view(path, method, self.view):
response_schema = {
'type': 'array',
'items': {}
}
else:
response_schema = {}
else:
item_schema = self._get_reference(serializer)
if is_list_view(path, method, self.view):
response_schema = {
'type': 'array',
'items': item_schema,
}
paginator = self.get_paginator()
if paginator:
response_schema = paginator.get_paginated_response_schema(response_schema)
else:
response_schema = item_schema
response_schema = self._get_response_reference(
self.get_operation_id(path, method)
)
status_code = '201' if method == 'POST' else '200'
return {
status_code: {

View File

@ -308,7 +308,8 @@ class TestOperationIntrospection(TestCase):
inspector.view = view
responses = inspector.get_responses(path, method)
assert responses['201']['content']['application/json']['schema']['$ref'] == '#/components/schemas/Item'
assert responses['201']['content']['application/json']['schema']['$ref'] == \
'#/components/schemas/CreateItemResponse'
components = inspector.get_components(path, method)
assert sorted(components['Item']['required']) == ['text', 'write_only']
@ -338,7 +339,7 @@ class TestOperationIntrospection(TestCase):
inspector.view = view
responses = inspector.get_responses(path, method)
assert responses['201']['content']['application/json']['schema']['$ref'] == '#/components/schemas/Item'
assert responses['201']['content']['application/json']['schema']['$ref'] == '#/components/schemas/CreateItemResponse'
components = inspector.get_components(path, method)
assert components['Item']
@ -375,10 +376,7 @@ class TestOperationIntrospection(TestCase):
'content': {
'application/json': {
'schema': {
'type': 'array',
'items': {
'$ref': '#/components/schemas/Item'
},
'$ref': '#/components/schemas/ListItemsResponse'
},
},
},
@ -386,6 +384,12 @@ class TestOperationIntrospection(TestCase):
}
components = inspector.get_components(path, method)
assert components == {
'ListItemsResponse': {
'type': 'array',
'items': {
'$ref': '#/components/schemas/Item',
},
},
'Item': {
'type': 'object',
'properties': {
@ -431,13 +435,7 @@ class TestOperationIntrospection(TestCase):
'content': {
'application/json': {
'schema': {
'type': 'object',
'item': {
'type': 'array',
'items': {
'$ref': '#/components/schemas/Item'
},
},
'$ref': '#/components/schemas/ListItemsResponse'
},
},
},
@ -445,6 +443,15 @@ class TestOperationIntrospection(TestCase):
}
components = inspector.get_components(path, method)
assert components == {
'ListItemsResponse': {
'type': 'object',
'item': {
'type': 'array',
'items': {
'$ref': '#/components/schemas/Item',
},
},
},
'Item': {
'type': 'object',
'properties': {
@ -601,7 +608,7 @@ class TestOperationIntrospection(TestCase):
'content': {
'application/json': {
'schema': {
'$ref': '#/components/schemas/Item'
'$ref': '#/components/schemas/RetrieveItemResponse'
},
},
},
@ -610,6 +617,9 @@ class TestOperationIntrospection(TestCase):
components = inspector.get_components(path, method)
assert components == {
'RetrieveItemResponse': {
'$ref': '#/components/schemas/Item'
},
'Item': {
'type': 'object',
'properties': {