openapi automatic mapping of renderers/parsers media types

This commit is contained in:
Dima Knivets 2019-08-11 15:39:35 +03:00
parent a142467586
commit 86a9176529
2 changed files with 53 additions and 4 deletions

View File

@ -1,4 +1,5 @@
import warnings
from operator import attrgetter
from urllib.parse import urljoin
from django.core.validators import (
@ -8,7 +9,7 @@ from django.core.validators import (
from django.db import models
from django.utils.encoding import force_str
from rest_framework import exceptions, serializers
from rest_framework import exceptions, serializers, renderers
from rest_framework.compat import uritemplate
from rest_framework.fields import _UnvalidatedField, empty
@ -78,7 +79,9 @@ class SchemaGenerator(BaseSchemaGenerator):
class AutoSchema(ViewInspector):
content_types = ['application/json']
request_media_types = []
response_media_types = []
method_mapping = {
'get': 'Retrieve',
'post': 'Create',
@ -90,6 +93,7 @@ class AutoSchema(ViewInspector):
def get_operation(self, path, method):
operation = {}
operation['operationId'] = self._get_operation_id(path, method)
parameters = []
@ -337,6 +341,12 @@ class AutoSchema(ViewInspector):
self._map_min_max(field, content)
return content
if isinstance(field, serializers.FileField):
return {
'type': 'string',
'format': 'binary'
}
# Simplest cases, default to 'string' type:
FIELD_CLASS_SCHEMA_TYPE = {
serializers.BooleanField: 'boolean',
@ -423,6 +433,17 @@ class AutoSchema(ViewInspector):
schema['maximum'] = int(digits * '9') + 1
schema['minimum'] = -schema['maximum']
def map_parsers(self, path, method):
return list(map(attrgetter('media_type'), self.view.parser_classes))
def map_renderers(self, path, method):
media_types = []
for renderer in self.view.renderer_classes:
# I assume this is not relevant to OpenAPI spec
if renderer != renderers.BrowsableAPIRenderer:
media_types.append(renderer.media_type)
return media_types
def _get_serializer(self, method, path):
view = self.view
@ -442,6 +463,8 @@ class AutoSchema(ViewInspector):
if method not in ('PUT', 'PATCH', 'POST'):
return {}
self.request_media_types = self.map_parsers(path, method)
serializer = self._get_serializer(path, method)
if not isinstance(serializer, serializers.Serializer):
@ -459,7 +482,7 @@ class AutoSchema(ViewInspector):
return {
'content': {
ct: {'schema': content}
for ct in self.content_types
for ct in self.request_media_types
}
}
@ -472,6 +495,8 @@ class AutoSchema(ViewInspector):
}
}
self.response_media_types = self.map_renderers(path, method)
item_schema = {}
serializer = self._get_serializer(path, method)
@ -496,7 +521,7 @@ class AutoSchema(ViewInspector):
'200': {
'content': {
ct: {'schema': response_schema}
for ct in self.content_types
for ct in self.response_media_types
},
# description is a mandatory property,
# https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responseObject

View File

@ -287,6 +287,30 @@ class TestOperationIntrospection(TestCase):
},
}
def test_multipart_request_body_generation(self):
"""Test that a view's delete method generates a proper response body schema."""
path = '/{id}/'
method = 'POST'
class ItemSerializer(serializers.Serializer):
attachment = serializers.FileField()
class View(generics.CreateAPIView):
serializer_class = ItemSerializer
view = create_view(
View,
method,
create_request(path),
)
inspector = AutoSchema()
inspector.view = view
request_body = inspector._get_request_body(path, method)
assert 'multipart/form-data' in request_body['content']
attachment = request_body['content']['multipart/form-data']['schema']['properties']['attachment']
assert attachment['format'] == 'binary'
def test_retrieve_response_body_generation(self):
"""Test that a list of properties is returned for retrieve item views."""
path = '/{id}/'