mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-07-28 08:59:54 +03:00
Always wrap response in a component schema, also paginated once, to improve processability of API code generators #7299
This commit is contained in:
parent
900773ad06
commit
558dd0e19e
|
@ -189,6 +189,8 @@ class AutoSchema(ViewInspector):
|
||||||
Return components with their properties from the serializer.
|
Return components with their properties from the serializer.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
components = {}
|
||||||
|
|
||||||
if method.lower() == 'delete':
|
if method.lower() == 'delete':
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
@ -197,10 +199,28 @@ class AutoSchema(ViewInspector):
|
||||||
if not isinstance(serializer, serializers.Serializer):
|
if not isinstance(serializer, serializers.Serializer):
|
||||||
return {}
|
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)
|
response_component_name = self._get_response_component_name(
|
||||||
return {component_name: content}
|
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):
|
def _to_camel_case(self, snake_str):
|
||||||
components = snake_str.split('_')
|
components = snake_str.split('_')
|
||||||
|
@ -613,9 +633,17 @@ class AutoSchema(ViewInspector):
|
||||||
.format(view.__class__.__name__, method, path))
|
.format(view.__class__.__name__, method, path))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _get_reference(self, serializer):
|
def _get_serializer_reference(self, serializer):
|
||||||
return {'$ref': '#/components/schemas/{}'.format(self.get_component_name(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):
|
def get_request_body(self, path, method):
|
||||||
if method not in ('PUT', 'PATCH', 'POST'):
|
if method not in ('PUT', 'PATCH', 'POST'):
|
||||||
return {}
|
return {}
|
||||||
|
@ -627,7 +655,7 @@ class AutoSchema(ViewInspector):
|
||||||
if not isinstance(serializer, serializers.Serializer):
|
if not isinstance(serializer, serializers.Serializer):
|
||||||
item_schema = {}
|
item_schema = {}
|
||||||
else:
|
else:
|
||||||
item_schema = self._get_reference(serializer)
|
item_schema = self._get_serializer_reference(serializer)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'content': {
|
'content': {
|
||||||
|
@ -649,20 +677,17 @@ class AutoSchema(ViewInspector):
|
||||||
serializer = self.get_serializer(path, method)
|
serializer = self.get_serializer(path, method)
|
||||||
|
|
||||||
if not isinstance(serializer, serializers.Serializer):
|
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:
|
else:
|
||||||
item_schema = self._get_reference(serializer)
|
response_schema = self._get_response_reference(
|
||||||
|
self.get_operation_id(path, method)
|
||||||
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
|
|
||||||
status_code = '201' if method == 'POST' else '200'
|
status_code = '201' if method == 'POST' else '200'
|
||||||
return {
|
return {
|
||||||
status_code: {
|
status_code: {
|
||||||
|
|
|
@ -308,7 +308,8 @@ class TestOperationIntrospection(TestCase):
|
||||||
inspector.view = view
|
inspector.view = view
|
||||||
|
|
||||||
responses = inspector.get_responses(path, method)
|
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)
|
components = inspector.get_components(path, method)
|
||||||
assert sorted(components['Item']['required']) == ['text', 'write_only']
|
assert sorted(components['Item']['required']) == ['text', 'write_only']
|
||||||
|
@ -338,7 +339,7 @@ class TestOperationIntrospection(TestCase):
|
||||||
inspector.view = view
|
inspector.view = view
|
||||||
|
|
||||||
responses = inspector.get_responses(path, method)
|
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)
|
components = inspector.get_components(path, method)
|
||||||
assert components['Item']
|
assert components['Item']
|
||||||
|
|
||||||
|
@ -375,10 +376,7 @@ class TestOperationIntrospection(TestCase):
|
||||||
'content': {
|
'content': {
|
||||||
'application/json': {
|
'application/json': {
|
||||||
'schema': {
|
'schema': {
|
||||||
'type': 'array',
|
'$ref': '#/components/schemas/ListItemsResponse'
|
||||||
'items': {
|
|
||||||
'$ref': '#/components/schemas/Item'
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -386,6 +384,12 @@ class TestOperationIntrospection(TestCase):
|
||||||
}
|
}
|
||||||
components = inspector.get_components(path, method)
|
components = inspector.get_components(path, method)
|
||||||
assert components == {
|
assert components == {
|
||||||
|
'ListItemsResponse': {
|
||||||
|
'type': 'array',
|
||||||
|
'items': {
|
||||||
|
'$ref': '#/components/schemas/Item',
|
||||||
|
},
|
||||||
|
},
|
||||||
'Item': {
|
'Item': {
|
||||||
'type': 'object',
|
'type': 'object',
|
||||||
'properties': {
|
'properties': {
|
||||||
|
@ -431,13 +435,7 @@ class TestOperationIntrospection(TestCase):
|
||||||
'content': {
|
'content': {
|
||||||
'application/json': {
|
'application/json': {
|
||||||
'schema': {
|
'schema': {
|
||||||
'type': 'object',
|
'$ref': '#/components/schemas/ListItemsResponse'
|
||||||
'item': {
|
|
||||||
'type': 'array',
|
|
||||||
'items': {
|
|
||||||
'$ref': '#/components/schemas/Item'
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -445,6 +443,15 @@ class TestOperationIntrospection(TestCase):
|
||||||
}
|
}
|
||||||
components = inspector.get_components(path, method)
|
components = inspector.get_components(path, method)
|
||||||
assert components == {
|
assert components == {
|
||||||
|
'ListItemsResponse': {
|
||||||
|
'type': 'object',
|
||||||
|
'item': {
|
||||||
|
'type': 'array',
|
||||||
|
'items': {
|
||||||
|
'$ref': '#/components/schemas/Item',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
'Item': {
|
'Item': {
|
||||||
'type': 'object',
|
'type': 'object',
|
||||||
'properties': {
|
'properties': {
|
||||||
|
@ -601,7 +608,7 @@ class TestOperationIntrospection(TestCase):
|
||||||
'content': {
|
'content': {
|
||||||
'application/json': {
|
'application/json': {
|
||||||
'schema': {
|
'schema': {
|
||||||
'$ref': '#/components/schemas/Item'
|
'$ref': '#/components/schemas/RetrieveItemResponse'
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -610,6 +617,9 @@ class TestOperationIntrospection(TestCase):
|
||||||
|
|
||||||
components = inspector.get_components(path, method)
|
components = inspector.get_components(path, method)
|
||||||
assert components == {
|
assert components == {
|
||||||
|
'RetrieveItemResponse': {
|
||||||
|
'$ref': '#/components/schemas/Item'
|
||||||
|
},
|
||||||
'Item': {
|
'Item': {
|
||||||
'type': 'object',
|
'type': 'object',
|
||||||
'properties': {
|
'properties': {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user