83 lines
2.9 KiB
Python
83 lines
2.9 KiB
Python
from collections import OrderedDict
|
|
from datetime import time, timedelta
|
|
from decimal import Decimal
|
|
from uuid import UUID
|
|
|
|
import yaml
|
|
from django.utils.safestring import SafeString
|
|
from rest_framework.exceptions import ErrorDetail
|
|
from rest_framework.renderers import BaseRenderer, JSONRenderer
|
|
|
|
|
|
class OpenApiYamlRenderer(BaseRenderer):
|
|
media_type = 'application/vnd.oai.openapi'
|
|
format = 'yaml'
|
|
|
|
def render(self, data, accepted_media_type=None, renderer_context=None):
|
|
# disable yaml advanced feature 'alias' for clean, portable, and readable output
|
|
class Dumper(yaml.SafeDumper):
|
|
def ignore_aliases(self, data):
|
|
return True
|
|
|
|
def error_detail_representer(dumper, data):
|
|
return dumper.represent_dict({'string': str(data), 'code': data.code})
|
|
Dumper.add_representer(ErrorDetail, error_detail_representer)
|
|
|
|
def multiline_str_representer(dumper, data):
|
|
scalar = dumper.represent_str(data)
|
|
scalar.style = '|' if '\n' in data else None
|
|
return scalar
|
|
Dumper.add_representer(str, multiline_str_representer)
|
|
|
|
def decimal_representer(dumper, data):
|
|
# prevent emitting "!! float" tags on fractionless decimals
|
|
value = f'{data:f}'
|
|
if '.' in value:
|
|
return dumper.represent_scalar('tag:yaml.org,2002:float', value)
|
|
else:
|
|
return dumper.represent_scalar('tag:yaml.org,2002:int', value)
|
|
Dumper.add_representer(Decimal, decimal_representer)
|
|
|
|
def timedelta_representer(dumper, data):
|
|
return dumper.represent_str(str(data.total_seconds()))
|
|
Dumper.add_representer(timedelta, timedelta_representer)
|
|
|
|
def time_representer(dumper, data):
|
|
return dumper.represent_str(data.isoformat())
|
|
Dumper.add_representer(time, time_representer)
|
|
|
|
def uuid_representer(dumper, data):
|
|
return dumper.represent_str(str(data))
|
|
Dumper.add_representer(UUID, uuid_representer)
|
|
|
|
def safestring_representer(dumper, data):
|
|
return dumper.represent_str(data)
|
|
Dumper.add_representer(SafeString, safestring_representer)
|
|
|
|
def ordereddict_representer(dumper, data):
|
|
return dumper.represent_dict(dict(data))
|
|
Dumper.add_representer(OrderedDict, ordereddict_representer)
|
|
|
|
return yaml.dump(
|
|
data,
|
|
default_flow_style=False,
|
|
sort_keys=False,
|
|
allow_unicode=True,
|
|
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'
|