Adapts schema generation for pagination.

- add `domain` and `protocol` to `AutoSchema` init
- add `get_schema_domain` and `get_schema_protocol` to `AutoSchema`
- adapt pagination classes to accept `domain`, `protocol` and `path` for method `get_paginated_response_schema`
- adapt `test_paginated_list_response_body_generation`
  - do not use custom `Pagination` implementation, rather use `PageNumberPagination`
  - make sure new parameters `domain`, `protocol` and `path` are tested as well
This commit is contained in:
Robert Stein 2020-11-29 17:37:24 +01:00
parent 8812394ed8
commit 1c7035f721
3 changed files with 56 additions and 27 deletions

View File

@ -229,7 +229,7 @@ class PageNumberPagination(BasePagination):
('results', data) ('results', data)
])) ]))
def get_paginated_response_schema(self, schema): def get_paginated_response_schema(self, schema, url='/accounts/', domain='api.example.org', protocol='http'):
return { return {
'type': 'object', 'type': 'object',
'properties': { 'properties': {
@ -241,15 +241,15 @@ class PageNumberPagination(BasePagination):
'type': 'string', 'type': 'string',
'nullable': True, 'nullable': True,
'format': 'uri', 'format': 'uri',
'example': 'http://api.example.org/accounts/?{page_query_param}=4'.format( 'example': '{protocol}://{domain}{url}?{page_query_param}=4'.format(
page_query_param=self.page_query_param) page_query_param=self.page_query_param, protocol=protocol, domain=domain, url=url)
}, },
'previous': { 'previous': {
'type': 'string', 'type': 'string',
'nullable': True, 'nullable': True,
'format': 'uri', 'format': 'uri',
'example': 'http://api.example.org/accounts/?{page_query_param}=2'.format( 'example': '{protocol}://{domain}{url}?{page_query_param}=2'.format(
page_query_param=self.page_query_param) page_query_param=self.page_query_param, protocol=protocol, domain=domain, url=url)
}, },
'results': schema, 'results': schema,
}, },
@ -402,7 +402,7 @@ class LimitOffsetPagination(BasePagination):
('results', data) ('results', data)
])) ]))
def get_paginated_response_schema(self, schema): def get_paginated_response_schema(self, schema, url='/accounts/', domain='api.example.org', protocol='http'):
return { return {
'type': 'object', 'type': 'object',
'properties': { 'properties': {
@ -414,15 +414,19 @@ class LimitOffsetPagination(BasePagination):
'type': 'string', 'type': 'string',
'nullable': True, 'nullable': True,
'format': 'uri', 'format': 'uri',
'example': 'http://api.example.org/accounts/?{offset_param}=400&{limit_param}=100'.format( 'example': '{protocol}://{domain}{url}?{offset_param}=400&{limit_param}=100'.format(
offset_param=self.offset_query_param, limit_param=self.limit_query_param), offset_param=self.offset_query_param, limit_param=self.limit_query_param,
protocol=protocol, domain=domain, url=url
),
}, },
'previous': { 'previous': {
'type': 'string', 'type': 'string',
'nullable': True, 'nullable': True,
'format': 'uri', 'format': 'uri',
'example': 'http://api.example.org/accounts/?{offset_param}=200&{limit_param}=100'.format( 'example': '{protocol}://{domain}{url}?{offset_param}=200&{limit_param}=100'.format(
offset_param=self.offset_query_param, limit_param=self.limit_query_param), offset_param=self.offset_query_param, limit_param=self.limit_query_param,
protocol=protocol, domain=domain, url=url
),
}, },
'results': schema, 'results': schema,
}, },
@ -898,7 +902,7 @@ class CursorPagination(BasePagination):
('results', data) ('results', data)
])) ]))
def get_paginated_response_schema(self, schema): def get_paginated_response_schema(self, schema, url='/accounts/', domain='api.example.org', protocol='http'):
return { return {
'type': 'object', 'type': 'object',
'properties': { 'properties': {

View File

@ -118,7 +118,8 @@ class SchemaGenerator(BaseSchemaGenerator):
class AutoSchema(ViewInspector): class AutoSchema(ViewInspector):
def __init__(self, tags=None, operation_id_base=None, component_name=None): def __init__(self, tags=None, operation_id_base=None, component_name=None, domain='api.example.org',
protocol='http'):
""" """
:param operation_id_base: user-defined name in operationId. If empty, it will be deducted from the Model/Serializer/View name. :param operation_id_base: user-defined name in operationId. If empty, it will be deducted from the Model/Serializer/View name.
:param component_name: user-defined component's name. If empty, it will be deducted from the Serializer's class name. :param component_name: user-defined component's name. If empty, it will be deducted from the Serializer's class name.
@ -128,6 +129,8 @@ class AutoSchema(ViewInspector):
self._tags = tags self._tags = tags
self.operation_id_base = operation_id_base self.operation_id_base = operation_id_base
self.component_name = component_name self.component_name = component_name
self.domain = domain
self.protocol = protocol
super().__init__() super().__init__()
request_media_types = [] request_media_types = []
@ -683,7 +686,12 @@ class AutoSchema(ViewInspector):
} }
paginator = self.get_paginator() paginator = self.get_paginator()
if paginator: if paginator:
response_schema = paginator.get_paginated_response_schema(response_schema) response_schema = paginator.get_paginated_response_schema(
response_schema,
url=path,
domain=self.get_schema_domain(),
protocol=self.get_schema_protocol()
)
else: else:
response_schema = item_schema response_schema = item_schema
status_code = '201' if method == 'POST' else '200' status_code = '201' if method == 'POST' else '200'
@ -713,6 +721,12 @@ class AutoSchema(ViewInspector):
return [path.split('/')[0].replace('_', '-')] return [path.split('/')[0].replace('_', '-')]
def get_schema_domain(self):
return self.domain
def get_schema_protocol(self):
return self.protocol
def _get_path_parameters(self, path, method): def _get_path_parameters(self, path, method):
warnings.warn( warnings.warn(
"Method `_get_path_parameters()` has been renamed to `get_path_parameters()`. " "Method `_get_path_parameters()` has been renamed to `get_path_parameters()`. "

View File

@ -405,26 +405,22 @@ class TestOperationIntrospection(TestCase):
path = '/' path = '/'
method = 'GET' method = 'GET'
class Pagination(pagination.BasePagination):
def get_paginated_response_schema(self, schema):
return {
'type': 'object',
'item': schema,
}
class ItemSerializer(serializers.Serializer): class ItemSerializer(serializers.Serializer):
text = serializers.CharField() text = serializers.CharField()
class View(generics.GenericAPIView): class View(generics.GenericAPIView):
serializer_class = ItemSerializer serializer_class = ItemSerializer
pagination_class = Pagination pagination_class = pagination.PageNumberPagination
view = create_view( view = create_view(
View, View,
method, method,
create_request(path), create_request(path),
) )
inspector = AutoSchema() inspector = AutoSchema(
domain='www.django-rest-framework.org',
protocol='https'
)
inspector.view = view inspector.view = view
responses = inspector.get_responses(path, method) responses = inspector.get_responses(path, method)
@ -435,7 +431,21 @@ class TestOperationIntrospection(TestCase):
'application/json': { 'application/json': {
'schema': { 'schema': {
'type': 'object', 'type': 'object',
'item': { 'properties': {
'next': {
'example': 'https://www.django-rest-framework.org/?page=4',
'format': 'uri',
'nullable': True,
'type': 'string'
},
'previous': {
'example': 'https://www.django-rest-framework.org/?page=2',
'format': 'uri',
'nullable': True,
'type': 'string'
},
'count': {'example': 123, 'type': 'integer'},
'results': {
'type': 'array', 'type': 'array',
'items': { 'items': {
'$ref': '#/components/schemas/Item' '$ref': '#/components/schemas/Item'
@ -445,6 +455,7 @@ class TestOperationIntrospection(TestCase):
}, },
}, },
}, },
},
} }
components = inspector.get_components(path, method) components = inspector.get_components(path, method)
assert components == { assert components == {
@ -583,7 +594,7 @@ class TestOperationIntrospection(TestCase):
method = 'GET' method = 'GET'
class Pagination(pagination.BasePagination): class Pagination(pagination.BasePagination):
def get_paginated_response_schema(self, schema): def get_paginated_response_schema(self, schema, url='/accounts/', domain='api.example.org', protocol='http'):
return { return {
'type': 'object', 'type': 'object',
'item': schema, 'item': schema,