Skip to content

Commit

Permalink
improve ACCEPT header handling #42
Browse files Browse the repository at this point in the history
  • Loading branch information
tfranzel committed Apr 27, 2020
1 parent 69fb791 commit 266904c
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 14 deletions.
7 changes: 3 additions & 4 deletions drf_spectacular/management/commands/spectacular.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@

from django.core.management.base import BaseCommand
from django.utils.module_loading import import_string
from rest_framework import renderers

from drf_spectacular.settings import spectacular_settings
from drf_spectacular.plumbing import GENERATOR_STATS
from drf_spectacular.renderers import NoAliasOpenAPIRenderer
from drf_spectacular.renderers import OpenApiYamlRenderer, OpenApiJsonRenderer
from drf_spectacular.validation import validate_schema


Expand Down Expand Up @@ -58,7 +57,7 @@ def handle(self, *args, **options):

def get_renderer(self, format):
renderer_cls = {
'openapi': NoAliasOpenAPIRenderer,
'openapi-json': renderers.JSONOpenAPIRenderer,
'openapi': OpenApiYamlRenderer,
'openapi-json': OpenApiJsonRenderer,
}[format]
return renderer_cls()
21 changes: 18 additions & 3 deletions drf_spectacular/renderers.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import yaml
from rest_framework.renderers import OpenAPIRenderer
from rest_framework.renderers import OpenAPIRenderer, JSONRenderer


class NoAliasOpenAPIRenderer(OpenAPIRenderer):
""" Remove this temp fix once DRF 3.11 is no longer supported """
class OpenApiYamlRenderer(OpenAPIRenderer):
media_type = 'application/vnd.oai.openapi'

def render(self, data, media_type=None, renderer_context=None):
# disable yaml advanced feature 'alias' for clean, portable, and readable output
Expand All @@ -12,3 +12,18 @@ def ignore_aliases(self, data):
return True

return yaml.dump(data, default_flow_style=False, sort_keys=False, Dumper=Dumper).encode('utf-8')


class OpenApiYamlRenderer2(OpenApiYamlRenderer):
media_type = 'application/yaml'


class OpenApiJsonRenderer(JSONRenderer):
media_type = 'application/vnd.oai.openapi+json'

def get_indent(self, accepted_media_type, renderer_context):
return super().get_indent(accepted_media_type, renderer_context) or 4


class OpenApiJsonRenderer2(OpenApiJsonRenderer):
media_type = 'application/json'
14 changes: 9 additions & 5 deletions drf_spectacular/views.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from collections import namedtuple

from rest_framework.renderers import JSONOpenAPIRenderer
from rest_framework.response import Response
from rest_framework.views import APIView

from drf_spectacular.renderers import NoAliasOpenAPIRenderer
from drf_spectacular.renderers import (
OpenApiJsonRenderer, OpenApiJsonRenderer2,
OpenApiYamlRenderer, OpenApiYamlRenderer2
)
from drf_spectacular.settings import spectacular_settings
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema
Expand All @@ -22,7 +24,9 @@ class SpectacularAPIView(APIView):
- YAML: application/vnd.oai.openapi
- JSON: application/vnd.oai.openapi+json
"""
renderer_classes = [NoAliasOpenAPIRenderer, JSONOpenAPIRenderer]
renderer_classes = [
OpenApiYamlRenderer, OpenApiYamlRenderer2, OpenApiJsonRenderer, OpenApiJsonRenderer2
]
permission_classes = spectacular_settings.SERVE_PERMISSIONS

generator_class = spectacular_settings.DEFAULT_GENERATOR_CLASS
Expand All @@ -41,8 +45,8 @@ def get(self, request):


class SpectacularYAMLAPIView(SpectacularAPIView):
renderer_classes = [NoAliasOpenAPIRenderer]
renderer_classes = [OpenApiYamlRenderer, OpenApiYamlRenderer2]


class SpectacularJSONAPIView(SpectacularAPIView):
renderer_classes = [JSONOpenAPIRenderer]
renderer_classes = [OpenApiJsonRenderer, OpenApiJsonRenderer2]
4 changes: 2 additions & 2 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@


def assert_schema(schema, reference_file):
from drf_spectacular.renderers import NoAliasOpenAPIRenderer
from drf_spectacular.renderers import OpenApiYamlRenderer

schema_yml = NoAliasOpenAPIRenderer().render(schema, renderer_context={})
schema_yml = OpenApiYamlRenderer().render(schema, renderer_context={})

with open(reference_file.replace('.yml', '_out.yml'), 'wb') as fh:
fh.write(schema_yml)
Expand Down
18 changes: 18 additions & 0 deletions tests/test_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,21 @@ def test_spectacular_view_custom_urlconf(no_warnings):
response = APIClient().get('/api/v2/pi-fast')
assert response.status_code == 200
assert response.content == b'3.1415'


@pytest.mark.parametrize(['accept', 'format', 'indent'], [
('application/vnd.oai.openapi', 'yaml', None),
('application/yaml', 'yaml', None),
('application/vnd.oai.openapi+json', 'json', 4),
('application/json', 'json', 4),
('application/json; indent=8', 'json', 8),
])
@pytest.mark.urls(__name__)
def test_spectacular_view_accept(accept, format, indent):
response = APIClient().get('/api/v1/schema', HTTP_ACCEPT=accept)
assert response.status_code == 200
assert response.accepted_media_type == accept
if format == 'json':
assert response.content.startswith(b'{\n' + indent * b' ' + b'"openapi": "3.0.3"')
if format == 'yaml':
assert response.content.startswith(b'openapi: 3.0.3\n')

0 comments on commit 266904c

Please sign in to comment.