Improve validator error handling

openapi3
Cristi Vîjdea 2018-01-11 21:19:42 +01:00
parent c225f66fb7
commit 57d77cc48a
6 changed files with 23 additions and 15 deletions

View File

@ -126,7 +126,7 @@ In ``urls.py``:
contact=openapi.Contact(email="contact@snippets.local"), contact=openapi.Contact(email="contact@snippets.local"),
license=openapi.License(name="BSD License"), license=openapi.License(name="BSD License"),
), ),
validators=['ssv', 'flex'], validators=['flex', 'ssv'],
public=True, public=True,
permission_classes=(permissions.AllowAny,), permission_classes=(permissions.AllowAny,),
) )
@ -267,7 +267,7 @@ caching the schema view in-memory, with some sane defaults:
Given the numerous methods to manually customzie the generated schema, it makes sense to validate the result to ensure Given the numerous methods to manually customzie the generated schema, it makes sense to validate the result to ensure
it still conforms to OpenAPI 2.0. To this end, validation is provided at the generation point using python swagger it still conforms to OpenAPI 2.0. To this end, validation is provided at the generation point using python swagger
libraries, and can be activated by passing :python:`validators=['ssv', 'flex']` to ``get_schema_view``; if the generated libraries, and can be activated by passing :python:`validators=['flex', 'ssv']` to ``get_schema_view``; if the generated
schema is not valid, a :python:`SwaggerValidationError` is raised by the handling codec. schema is not valid, a :python:`SwaggerValidationError` is raised by the handling codec.
**Warning:** This internal validation can slow down your server. **Warning:** This internal validation can slow down your server.

View File

@ -12,22 +12,22 @@ from .app_settings import swagger_settings
from .errors import SwaggerValidationError from .errors import SwaggerValidationError
def _validate_flex(spec, codec): def _validate_flex(spec):
from flex.core import parse as validate_flex from flex.core import parse as validate_flex
from flex.exceptions import ValidationError from flex.exceptions import ValidationError
try: try:
validate_flex(spec) validate_flex(spec)
except ValidationError as ex: except ValidationError as ex:
raise_from(SwaggerValidationError(str(ex), 'flex', spec, codec), ex) raise_from(SwaggerValidationError(str(ex)), ex)
def _validate_swagger_spec_validator(spec, codec): def _validate_swagger_spec_validator(spec):
from swagger_spec_validator.validator20 import validate_spec as validate_ssv from swagger_spec_validator.validator20 import validate_spec as validate_ssv
from swagger_spec_validator.common import SwaggerValidationError as SSVErr from swagger_spec_validator.common import SwaggerValidationError as SSVErr
try: try:
validate_ssv(spec) validate_ssv(spec)
except SSVErr as ex: except SSVErr as ex:
raise_from(SwaggerValidationError(str(ex), 'swagger_spec_validator', spec, codec), ex) raise_from(SwaggerValidationError(str(ex)), ex)
#: #:
@ -61,10 +61,17 @@ class _OpenAPICodec(object):
raise TypeError('Expected a `openapi.Swagger` instance') raise TypeError('Expected a `openapi.Swagger` instance')
spec = self.generate_swagger_object(document) spec = self.generate_swagger_object(document)
errors = {}
for validator in self.validators: for validator in self.validators:
# validate a deepcopy of the spec to prevent the validator from messing with it try:
# for example, swagger_spec_validator adds an x-scope property to all references # validate a deepcopy of the spec to prevent the validator from messing with it
VALIDATORS[validator](copy.deepcopy(spec), self) # for example, swagger_spec_validator adds an x-scope property to all references
VALIDATORS[validator](copy.deepcopy(spec))
except SwaggerValidationError as e:
errors[validator] = str(e)
if errors:
raise SwaggerValidationError("spec validation failed", errors, spec, self)
return force_bytes(self._dump_dict(spec)) return force_bytes(self._dump_dict(spec))
def encode_error(self, err): def encode_error(self, err):

View File

@ -3,9 +3,9 @@ class SwaggerError(Exception):
class SwaggerValidationError(SwaggerError): class SwaggerValidationError(SwaggerError):
def __init__(self, msg, validator_name, spec, source_codec, *args): def __init__(self, msg, errors=None, spec=None, source_codec=None, *args):
super(SwaggerValidationError, self).__init__(msg, *args) super(SwaggerValidationError, self).__init__(msg, *args)
self.validator_name = validator_name self.errors = errors
self.spec = spec self.spec = spec
self.source_codec = source_codec self.source_codec = source_codec

View File

@ -13,7 +13,7 @@ class SwaggerExceptionMiddleware(object):
def process_exception(self, request, exception): def process_exception(self, request, exception):
if isinstance(exception, SwaggerValidationError): if isinstance(exception, SwaggerValidationError):
err = {'errors': {exception.validator_name: str(exception)}} err = {'errors': exception.errors, 'message': str(exception)}
codec = exception.source_codec codec = exception.source_codec
if isinstance(codec, _OpenAPICodec): if isinstance(codec, _OpenAPICodec):
err = codec.encode_error(err) err = codec.encode_error(err)

View File

@ -9,7 +9,7 @@ from .codecs import VALIDATORS, OpenAPICodecJson, OpenAPICodecYaml
class _SpecRenderer(BaseRenderer): class _SpecRenderer(BaseRenderer):
"""Base class for text renderers. Handles encoding and validation.""" """Base class for text renderers. Handles encoding and validation."""
charset = None charset = None
validators = ['ssv', 'flex'] validators = []
codec_class = None codec_class = None
@classmethod @classmethod

View File

@ -12,8 +12,9 @@ from rest_framework.views import APIView
from .app_settings import swagger_settings from .app_settings import swagger_settings
from .generators import OpenAPISchemaGenerator from .generators import OpenAPISchemaGenerator
from .renderers import OpenAPIRenderer, ReDocRenderer, SwaggerJSONRenderer, SwaggerUIRenderer, SwaggerYAMLRenderer, \ from .renderers import (
ReDocAlphaRenderer OpenAPIRenderer, ReDocAlphaRenderer, ReDocRenderer, SwaggerJSONRenderer, SwaggerUIRenderer, SwaggerYAMLRenderer
)
SPEC_RENDERERS = (SwaggerYAMLRenderer, SwaggerJSONRenderer, OpenAPIRenderer) SPEC_RENDERERS = (SwaggerYAMLRenderer, SwaggerJSONRenderer, OpenAPIRenderer)
UI_RENDERERS = { UI_RENDERERS = {