123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- from django.core.exceptions import ImproperlyConfigured
- from django.shortcuts import resolve_url
- from django.template.loader import render_to_string
- from django.utils.encoding import force_str
- from django.utils.functional import Promise
- from rest_framework.renderers import BaseRenderer, JSONRenderer, TemplateHTMLRenderer
- from rest_framework.utils import encoders, json
- from .app_settings import redoc_settings, swagger_settings
- from .codecs import VALIDATORS, OpenAPICodecJson, OpenAPICodecYaml
- from .openapi import Swagger
- from .utils import filter_none
- class _SpecRenderer(BaseRenderer):
- """Base class for text renderers. Handles encoding and validation."""
- charset = 'utf-8'
- validators = []
- codec_class = None
- @classmethod
- def with_validators(cls, validators):
- assert all(vld in VALIDATORS for vld in validators), "allowed validators are " + ", ".join(VALIDATORS)
- return type(cls.__name__, (cls,), {'validators': validators})
- def render(self, data, media_type=None, renderer_context=None):
- assert self.codec_class, "must override codec_class"
- codec = self.codec_class(self.validators)
- if not isinstance(data, Swagger): # pragma: no cover
- # if `swagger` is not a ``Swagger`` object, it means we somehow got a non-success ``Response``
- # in that case, it's probably better to let the default ``JSONRenderer`` render it
- # see https://github.com/axnsan12/drf-yasg/issues/58
- return JSONRenderer().render(data, media_type, renderer_context)
- return codec.encode(data)
- class OpenAPIRenderer(_SpecRenderer):
- """Renders the schema as a JSON document with the ``application/openapi+json`` specific mime type."""
- media_type = 'application/openapi+json'
- format = 'openapi'
- codec_class = OpenAPICodecJson
- class SwaggerJSONRenderer(_SpecRenderer):
- """Renders the schema as a JSON document with the generic ``application/json`` mime type."""
- media_type = 'application/json'
- format = '.json'
- codec_class = OpenAPICodecJson
- class SwaggerYAMLRenderer(_SpecRenderer):
- """Renders the schema as a YAML document."""
- media_type = 'application/yaml'
- format = '.yaml'
- codec_class = OpenAPICodecYaml
- class _UIRenderer(BaseRenderer):
- """Base class for web UI renderers. Handles loading and passing settings to the appropriate template."""
- media_type = 'text/html'
- charset = 'utf-8'
- template = ''
- def render(self, swagger, accepted_media_type=None, renderer_context=None):
- if not isinstance(swagger, Swagger): # pragma: no cover
- try:
- # if `swagger` is not a ``Swagger`` object, it means we somehow got a non-success ``Response``
- # in that case, it's probably better to let the default ``TemplateHTMLRenderer`` render it
- # see https://github.com/axnsan12/drf-yasg/issues/58
- return TemplateHTMLRenderer().render(swagger, accepted_media_type, renderer_context)
- except ImproperlyConfigured:
- # Fall back to using eg '404 Not Found'
- response = renderer_context['response']
- return '%d %s' % (response.status_code, response.status_text.title())
- self.set_context(renderer_context, swagger)
- return render_to_string(self.template, renderer_context, renderer_context['request'])
- def set_context(self, renderer_context, swagger=None):
- renderer_context['title'] = swagger.info.title or '' if swagger else ''
- renderer_context['version'] = swagger.info.version or '' if swagger else ''
- renderer_context['oauth2_config'] = json.dumps(self.get_oauth2_config(), cls=encoders.JSONEncoder)
- renderer_context['USE_SESSION_AUTH'] = swagger_settings.USE_SESSION_AUTH
- renderer_context.update(self.get_auth_urls())
- def resolve_url(self, to):
- if isinstance(to, Promise):
- to = str(to)
- if to is None:
- return None
- args, kwargs = None, None
- if not isinstance(to, str):
- if len(to) > 2:
- to, args, kwargs = to
- elif len(to) == 2:
- to, kwargs = to
- args = args or ()
- kwargs = kwargs or {}
- return resolve_url(to, *args, **kwargs)
- def get_auth_urls(self):
- urls = {
- 'LOGIN_URL': self.resolve_url(swagger_settings.LOGIN_URL),
- 'LOGOUT_URL': self.resolve_url(swagger_settings.LOGOUT_URL),
- }
- return filter_none(urls)
- def get_oauth2_config(self):
- data = swagger_settings.OAUTH2_CONFIG
- assert isinstance(data, dict), "OAUTH2_CONFIG must be a dict"
- return data
- class SwaggerUIRenderer(_UIRenderer):
- """Renders a swagger-ui web interface for schema browsing."""
- template = 'drf-yasg/swagger-ui.html'
- format = 'swagger'
- def set_context(self, renderer_context, swagger=None):
- super(SwaggerUIRenderer, self).set_context(renderer_context, swagger)
- swagger_ui_settings = self.get_swagger_ui_settings()
- request = renderer_context.get('request', None)
- oauth_redirect_url = force_str(swagger_ui_settings.get('oauth2RedirectUrl', ''))
- if request and oauth_redirect_url:
- swagger_ui_settings['oauth2RedirectUrl'] = request.build_absolute_uri(oauth_redirect_url)
- renderer_context['swagger_settings'] = json.dumps(swagger_ui_settings, cls=encoders.JSONEncoder)
- def get_swagger_ui_settings(self):
- data = {
- 'url': self.resolve_url(swagger_settings.SPEC_URL),
- 'operationsSorter': swagger_settings.OPERATIONS_SORTER,
- 'tagsSorter': swagger_settings.TAGS_SORTER,
- 'docExpansion': swagger_settings.DOC_EXPANSION,
- 'deepLinking': swagger_settings.DEEP_LINKING,
- 'showExtensions': swagger_settings.SHOW_EXTENSIONS,
- 'defaultModelRendering': swagger_settings.DEFAULT_MODEL_RENDERING,
- 'defaultModelExpandDepth': swagger_settings.DEFAULT_MODEL_DEPTH,
- 'defaultModelsExpandDepth': swagger_settings.DEFAULT_MODEL_DEPTH,
- 'showCommonExtensions': swagger_settings.SHOW_COMMON_EXTENSIONS,
- 'oauth2RedirectUrl': swagger_settings.OAUTH2_REDIRECT_URL,
- 'supportedSubmitMethods': swagger_settings.SUPPORTED_SUBMIT_METHODS,
- 'displayOperationId': swagger_settings.DISPLAY_OPERATION_ID,
- 'persistAuth': swagger_settings.PERSIST_AUTH,
- 'refetchWithAuth': swagger_settings.REFETCH_SCHEMA_WITH_AUTH,
- 'refetchOnLogout': swagger_settings.REFETCH_SCHEMA_ON_LOGOUT,
- 'fetchSchemaWithQuery': swagger_settings.FETCH_SCHEMA_WITH_QUERY,
- }
- data = filter_none(data)
- if swagger_settings.VALIDATOR_URL != '':
- data['validatorUrl'] = self.resolve_url(swagger_settings.VALIDATOR_URL)
- return data
- class ReDocRenderer(_UIRenderer):
- """Renders a ReDoc web interface for schema browsing."""
- template = 'drf-yasg/redoc.html'
- format = 'redoc'
- def set_context(self, renderer_context, swagger=None):
- super(ReDocRenderer, self).set_context(renderer_context, swagger)
- renderer_context['redoc_settings'] = json.dumps(self.get_redoc_settings(), cls=encoders.JSONEncoder)
- def get_redoc_settings(self):
- data = {
- 'url': self.resolve_url(redoc_settings.SPEC_URL),
- 'lazyRendering': redoc_settings.LAZY_RENDERING,
- 'hideHostname': redoc_settings.HIDE_HOSTNAME,
- 'expandResponses': redoc_settings.EXPAND_RESPONSES,
- 'pathInMiddlePanel': redoc_settings.PATH_IN_MIDDLE,
- 'nativeScrollbars': redoc_settings.NATIVE_SCROLLBARS,
- 'requiredPropsFirst': redoc_settings.REQUIRED_PROPS_FIRST,
- 'fetchSchemaWithQuery': redoc_settings.FETCH_SCHEMA_WITH_QUERY,
- }
- return filter_none(data)
- class ReDocOldRenderer(ReDocRenderer):
- """Renders a ReDoc 1.x.x web interface for schema browsing."""
- template = 'drf-yasg/redoc-old.html'
|