Narrow validation exceptions

This commit is contained in:
p1c2u 2019-10-20 14:38:41 +01:00
parent 2bca2526f2
commit cfdf3410d2
8 changed files with 83 additions and 50 deletions

View file

@ -4,7 +4,9 @@ from collections import defaultdict
from json import loads from json import loads
from openapi_core.schema.media_types.exceptions import InvalidMediaTypeValue from openapi_core.schema.media_types.exceptions import InvalidMediaTypeValue
from openapi_core.schema.schemas.exceptions import OpenAPISchemaError from openapi_core.schema.schemas.exceptions import (
CastError, ValidateError, UnmarshalError,
)
MEDIA_TYPE_DESERIALIZERS = { MEDIA_TYPE_DESERIALIZERS = {
@ -37,20 +39,25 @@ class MediaType(object):
return value return value
try: try:
return self.deserialize(value) deserialized = self.deserialize(value)
except ValueError as exc: except ValueError as exc:
raise InvalidMediaTypeValue(exc) raise InvalidMediaTypeValue(exc)
try:
return self.schema.cast(deserialized)
except CastError as exc:
raise InvalidMediaTypeValue(exc)
def unmarshal(self, value, custom_formatters=None, resolver=None): def unmarshal(self, value, custom_formatters=None, resolver=None):
if not self.schema: if not self.schema:
return value return value
try: try:
self.schema.validate(value, resolver=resolver) self.schema.validate(value, resolver=resolver)
except OpenAPISchemaError as exc: except ValidateError as exc:
raise InvalidMediaTypeValue(exc) raise InvalidMediaTypeValue(exc)
try: try:
return self.schema.unmarshal(value, custom_formatters=custom_formatters) return self.schema.unmarshal(value, custom_formatters=custom_formatters)
except OpenAPISchemaError as exc: except UnmarshalError as exc:
raise InvalidMediaTypeValue(exc) raise InvalidMediaTypeValue(exc)

View file

@ -7,8 +7,13 @@ class OpenAPIParameterError(OpenAPIMappingError):
pass pass
class MissingParameterError(OpenAPIParameterError):
"""Missing parameter error"""
pass
@attr.s(hash=True) @attr.s(hash=True)
class MissingParameter(OpenAPIParameterError): class MissingParameter(MissingParameterError):
name = attr.ib() name = attr.ib()
def __str__(self): def __str__(self):
@ -16,7 +21,7 @@ class MissingParameter(OpenAPIParameterError):
@attr.s(hash=True) @attr.s(hash=True)
class MissingRequiredParameter(OpenAPIParameterError): class MissingRequiredParameter(MissingParameterError):
name = attr.ib() name = attr.ib()
def __str__(self): def __str__(self):

View file

@ -10,7 +10,9 @@ from openapi_core.schema.parameters.exceptions import (
EmptyParameterValue, EmptyParameterValue,
) )
from openapi_core.schema.schemas.enums import SchemaType from openapi_core.schema.schemas.enums import SchemaType
from openapi_core.schema.schemas.exceptions import OpenAPISchemaError from openapi_core.schema.schemas.exceptions import (
CastError, ValidateError, UnmarshalError,
)
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -110,7 +112,7 @@ class Parameter(object):
try: try:
return self.schema.cast(deserialized) return self.schema.cast(deserialized)
except OpenAPISchemaError as exc: except CastError as exc:
raise InvalidParameterValue(self.name, exc) raise InvalidParameterValue(self.name, exc)
def unmarshal(self, value, custom_formatters=None, resolver=None): def unmarshal(self, value, custom_formatters=None, resolver=None):
@ -119,7 +121,7 @@ class Parameter(object):
try: try:
self.schema.validate(value, resolver=resolver) self.schema.validate(value, resolver=resolver)
except OpenAPISchemaError as exc: except ValidateError as exc:
raise InvalidParameterValue(self.name, exc) raise InvalidParameterValue(self.name, exc)
try: try:
@ -128,5 +130,5 @@ class Parameter(object):
custom_formatters=custom_formatters, custom_formatters=custom_formatters,
strict=True, strict=True,
) )
except OpenAPISchemaError as exc: except UnmarshalError as exc:
raise InvalidParameterValue(self.name, exc) raise InvalidParameterValue(self.name, exc)

View file

@ -23,13 +23,13 @@ class ValidateError(OpenAPISchemaError):
pass pass
class UnmarshallError(OpenAPISchemaError): class UnmarshalError(OpenAPISchemaError):
"""Schema unmarshall operation error""" """Schema unmarshal operation error"""
pass pass
@attr.s(hash=True) @attr.s(hash=True)
class UnmarshallValueError(UnmarshallError): class UnmarshalValueError(UnmarshalError):
"""Failed to unmarshal value to type""" """Failed to unmarshal value to type"""
value = attr.ib() value = attr.ib()
type = attr.ib() type = attr.ib()
@ -57,7 +57,7 @@ class InvalidSchemaValue(ValidateError):
).format(value=self.value, type=self.type, errors=errors) ).format(value=self.value, type=self.type, errors=errors)
class UnmarshallerError(UnmarshallError): class UnmarshallerError(UnmarshalError):
"""Unmarshaller error""" """Unmarshaller error"""
pass pass

View file

@ -16,7 +16,7 @@ from openapi_core.schema.schemas._format import oas30_format_checker
from openapi_core.schema.schemas.enums import SchemaFormat, SchemaType from openapi_core.schema.schemas.enums import SchemaFormat, SchemaType
from openapi_core.schema.schemas.exceptions import ( from openapi_core.schema.schemas.exceptions import (
CastError, InvalidSchemaValue, CastError, InvalidSchemaValue,
UnmarshallerError, UnmarshallValueError, UnmarshallError, UnmarshallerError, UnmarshalValueError, UnmarshalError,
) )
from openapi_core.schema.schemas.util import ( from openapi_core.schema.schemas.util import (
forcebool, format_date, format_datetime, format_byte, format_uuid, forcebool, format_date, format_datetime, format_byte, format_uuid,
@ -202,12 +202,12 @@ class Schema(object):
warnings.warn("The schema is deprecated", DeprecationWarning) warnings.warn("The schema is deprecated", DeprecationWarning)
if value is None: if value is None:
if not self.nullable: if not self.nullable:
raise UnmarshallError( raise UnmarshalError(
"Null value for non-nullable schema", value, self.type) "Null value for non-nullable schema", value, self.type)
return self.default return self.default
if self.enum and value not in self.enum: if self.enum and value not in self.enum:
raise UnmarshallError("Invalid value for enum: {0}".format(value)) raise UnmarshalError("Invalid value for enum: {0}".format(value))
unmarshal_mapping = self.get_unmarshal_mapping( unmarshal_mapping = self.get_unmarshal_mapping(
custom_formatters=custom_formatters, strict=strict) custom_formatters=custom_formatters, strict=strict)
@ -219,7 +219,7 @@ class Schema(object):
try: try:
unmarshalled = unmarshal_callable(value) unmarshalled = unmarshal_callable(value)
except ValueError as exc: except ValueError as exc:
raise UnmarshallValueError(value, self.type, exc) raise UnmarshalValueError(value, self.type, exc)
return unmarshalled return unmarshalled
@ -254,7 +254,7 @@ class Schema(object):
for subschema in self.one_of: for subschema in self.one_of:
try: try:
unmarshalled = subschema.unmarshal(value, custom_formatters) unmarshalled = subschema.unmarshal(value, custom_formatters)
except UnmarshallError: except UnmarshalError:
continue continue
else: else:
if result is not None: if result is not None:
@ -271,7 +271,7 @@ class Schema(object):
unmarshal_callable = unmarshal_mapping[schema_type] unmarshal_callable = unmarshal_mapping[schema_type]
try: try:
return unmarshal_callable(value) return unmarshal_callable(value)
except (UnmarshallError, ValueError): except (UnmarshalError, ValueError):
continue continue
log.warning("failed to unmarshal any type") log.warning("failed to unmarshal any type")
@ -300,7 +300,7 @@ class Schema(object):
try: try:
unmarshalled = self._unmarshal_properties( unmarshalled = self._unmarshal_properties(
value, one_of_schema, custom_formatters=custom_formatters) value, one_of_schema, custom_formatters=custom_formatters)
except (UnmarshallError, ValueError): except (UnmarshalError, ValueError):
pass pass
else: else:
if properties is not None: if properties is not None:

View file

@ -2,8 +2,16 @@
from itertools import chain from itertools import chain
from six import iteritems from six import iteritems
from openapi_core.schema.exceptions import OpenAPIMappingError from openapi_core.schema.media_types.exceptions import (
from openapi_core.schema.parameters.exceptions import MissingParameter InvalidMediaTypeValue, InvalidContentType,
)
from openapi_core.schema.operations.exceptions import InvalidOperation
from openapi_core.schema.parameters.exceptions import (
OpenAPIParameterError, MissingRequiredParameter,
)
from openapi_core.schema.paths.exceptions import InvalidPath
from openapi_core.schema.request_bodies.exceptions import MissingRequestBody
from openapi_core.schema.servers.exceptions import InvalidServer
from openapi_core.validation.request.datatypes import ( from openapi_core.validation.request.datatypes import (
RequestParameters, RequestValidationResult, RequestParameters, RequestValidationResult,
) )
@ -20,7 +28,7 @@ class RequestValidator(object):
try: try:
server = self.spec.get_server(request.full_url_pattern) server = self.spec.get_server(request.full_url_pattern)
# don't process if server errors # don't process if server errors
except OpenAPIMappingError as exc: except InvalidServer as exc:
return RequestValidationResult([exc, ], None, None) return RequestValidationResult([exc, ], None, None)
operation_pattern = get_operation_pattern( operation_pattern = get_operation_pattern(
@ -29,10 +37,14 @@ class RequestValidator(object):
try: try:
path = self.spec[operation_pattern] path = self.spec[operation_pattern]
except InvalidPath as exc:
return RequestValidationResult([exc, ], None, None)
try:
operation = self.spec.get_operation( operation = self.spec.get_operation(
operation_pattern, request.method) operation_pattern, request.method)
# don't process if operation errors # don't process if operation errors
except OpenAPIMappingError as exc: except InvalidOperation as exc:
return RequestValidationResult([exc, ], None, None) return RequestValidationResult([exc, ], None, None)
params, params_errors = self._get_parameters( params, params_errors = self._get_parameters(
@ -59,15 +71,15 @@ class RequestValidator(object):
seen.add((param_name, param.location.value)) seen.add((param_name, param.location.value))
try: try:
raw_value = param.get_raw_value(request) raw_value = param.get_raw_value(request)
except MissingParameter: except MissingRequiredParameter as exc:
continue
except OpenAPIMappingError as exc:
errors.append(exc) errors.append(exc)
continue continue
except OpenAPIParameterError:
continue
try: try:
casted = param.cast(raw_value) casted = param.cast(raw_value)
except OpenAPIMappingError as exc: except OpenAPIParameterError as exc:
errors.append(exc) errors.append(exc)
continue continue
@ -76,7 +88,7 @@ class RequestValidator(object):
casted, self.custom_formatters, casted, self.custom_formatters,
resolver=self.spec._resolver, resolver=self.spec._resolver,
) )
except OpenAPIMappingError as exc: except OpenAPIParameterError as exc:
errors.append(exc) errors.append(exc)
else: else:
locations.setdefault(param.location.value, {}) locations.setdefault(param.location.value, {})
@ -93,17 +105,17 @@ class RequestValidator(object):
body = None body = None
try: try:
media_type = operation.request_body[request.mimetype] media_type = operation.request_body[request.mimetype]
except OpenAPIMappingError as exc: except InvalidContentType as exc:
errors.append(exc) errors.append(exc)
else: else:
try: try:
raw_body = operation.request_body.get_value(request) raw_body = operation.request_body.get_value(request)
except OpenAPIMappingError as exc: except MissingRequestBody as exc:
errors.append(exc) errors.append(exc)
else: else:
try: try:
casted = media_type.cast(raw_body) casted = media_type.cast(raw_body)
except OpenAPIMappingError as exc: except InvalidMediaTypeValue as exc:
errors.append(exc) errors.append(exc)
else: else:
try: try:
@ -111,7 +123,7 @@ class RequestValidator(object):
casted, self.custom_formatters, casted, self.custom_formatters,
resolver=self.spec._resolver, resolver=self.spec._resolver,
) )
except OpenAPIMappingError as exc: except InvalidMediaTypeValue as exc:
errors.append(exc) errors.append(exc)
return body, errors return body, errors

View file

@ -1,5 +1,12 @@
"""OpenAPI core validation response validators module""" """OpenAPI core validation response validators module"""
from openapi_core.schema.exceptions import OpenAPIMappingError from openapi_core.schema.operations.exceptions import InvalidOperation
from openapi_core.schema.media_types.exceptions import (
InvalidMediaTypeValue, InvalidContentType,
)
from openapi_core.schema.responses.exceptions import (
InvalidResponse, MissingResponseContent,
)
from openapi_core.schema.servers.exceptions import InvalidServer
from openapi_core.validation.response.datatypes import ResponseValidationResult from openapi_core.validation.response.datatypes import ResponseValidationResult
from openapi_core.validation.util import get_operation_pattern from openapi_core.validation.util import get_operation_pattern
@ -14,7 +21,7 @@ class ResponseValidator(object):
try: try:
server = self.spec.get_server(request.full_url_pattern) server = self.spec.get_server(request.full_url_pattern)
# don't process if server errors # don't process if server errors
except OpenAPIMappingError as exc: except InvalidServer as exc:
return ResponseValidationResult([exc, ], None, None) return ResponseValidationResult([exc, ], None, None)
operation_pattern = get_operation_pattern( operation_pattern = get_operation_pattern(
@ -25,14 +32,14 @@ class ResponseValidator(object):
operation = self.spec.get_operation( operation = self.spec.get_operation(
operation_pattern, request.method) operation_pattern, request.method)
# don't process if operation errors # don't process if operation errors
except OpenAPIMappingError as exc: except InvalidOperation as exc:
return ResponseValidationResult([exc, ], None, None) return ResponseValidationResult([exc, ], None, None)
try: try:
operation_response = operation.get_response( operation_response = operation.get_response(
str(response.status_code)) str(response.status_code))
# don't process if operation response errors # don't process if operation response errors
except OpenAPIMappingError as exc: except InvalidResponse as exc:
return ResponseValidationResult([exc, ], None, None) return ResponseValidationResult([exc, ], None, None)
data, data_errors = self._get_data(response, operation_response) data, data_errors = self._get_data(response, operation_response)
@ -52,17 +59,17 @@ class ResponseValidator(object):
data = None data = None
try: try:
media_type = operation_response[response.mimetype] media_type = operation_response[response.mimetype]
except OpenAPIMappingError as exc: except InvalidContentType as exc:
errors.append(exc) errors.append(exc)
else: else:
try: try:
raw_data = operation_response.get_value(response) raw_data = operation_response.get_value(response)
except OpenAPIMappingError as exc: except MissingResponseContent as exc:
errors.append(exc) errors.append(exc)
else: else:
try: try:
casted = media_type.cast(raw_data) casted = media_type.cast(raw_data)
except OpenAPIMappingError as exc: except InvalidMediaTypeValue as exc:
errors.append(exc) errors.append(exc)
else: else:
try: try:
@ -70,7 +77,7 @@ class ResponseValidator(object):
casted, self.custom_formatters, casted, self.custom_formatters,
resolver=self.spec._resolver, resolver=self.spec._resolver,
) )
except OpenAPIMappingError as exc: except InvalidMediaTypeValue as exc:
errors.append(exc) errors.append(exc)
return data, errors return data, errors

View file

@ -8,7 +8,7 @@ from openapi_core.extensions.models.models import Model
from openapi_core.schema.schemas.enums import SchemaFormat, SchemaType from openapi_core.schema.schemas.enums import SchemaFormat, SchemaType
from openapi_core.schema.schemas.exceptions import ( from openapi_core.schema.schemas.exceptions import (
InvalidSchemaValue, OpenAPISchemaError, UnmarshallerStrictTypeError, InvalidSchemaValue, OpenAPISchemaError, UnmarshallerStrictTypeError,
UnmarshallValueError, UnmarshallError, InvalidCustomFormatSchemaValue, UnmarshalValueError, UnmarshalError, InvalidCustomFormatSchemaValue,
FormatterNotFoundError, FormatterNotFoundError,
) )
from openapi_core.schema.schemas.models import Schema from openapi_core.schema.schemas.models import Schema
@ -97,7 +97,7 @@ class TestSchemaUnmarshal(object):
schema = Schema('string') schema = Schema('string')
value = None value = None
with pytest.raises(UnmarshallError): with pytest.raises(UnmarshalError):
schema.unmarshal(value) schema.unmarshal(value)
def test_string_default(self): def test_string_default(self):
@ -105,7 +105,7 @@ class TestSchemaUnmarshal(object):
schema = Schema('string', default=default_value) schema = Schema('string', default=default_value)
value = None value = None
with pytest.raises(UnmarshallError): with pytest.raises(UnmarshalError):
schema.unmarshal(value) schema.unmarshal(value)
def test_string_default_nullable(self): def test_string_default_nullable(self):
@ -196,7 +196,7 @@ class TestSchemaUnmarshal(object):
schema = Schema('integer', enum=[1, 2, 3]) schema = Schema('integer', enum=[1, 2, 3])
value = '123' value = '123'
with pytest.raises(UnmarshallError): with pytest.raises(UnmarshalError):
schema.unmarshal(value) schema.unmarshal(value)
def test_integer_enum(self): def test_integer_enum(self):
@ -211,7 +211,7 @@ class TestSchemaUnmarshal(object):
schema = Schema('integer', enum=[1, 2, 3]) schema = Schema('integer', enum=[1, 2, 3])
value = '2' value = '2'
with pytest.raises(UnmarshallError): with pytest.raises(UnmarshalError):
schema.unmarshal(value) schema.unmarshal(value)
def test_integer_default(self): def test_integer_default(self):
@ -219,7 +219,7 @@ class TestSchemaUnmarshal(object):
schema = Schema('integer', default=default_value) schema = Schema('integer', default=default_value)
value = None value = None
with pytest.raises(UnmarshallError): with pytest.raises(UnmarshalError):
schema.unmarshal(value) schema.unmarshal(value)
def test_integer_default_nullable(self): def test_integer_default_nullable(self):
@ -250,14 +250,14 @@ class TestSchemaUnmarshal(object):
schema = Schema('array', items=Schema('string')) schema = Schema('array', items=Schema('string'))
value = '123' value = '123'
with pytest.raises(UnmarshallValueError): with pytest.raises(UnmarshalValueError):
schema.unmarshal(value) schema.unmarshal(value)
def test_array_of_integer_string_invalid(self): def test_array_of_integer_string_invalid(self):
schema = Schema('array', items=Schema('integer')) schema = Schema('array', items=Schema('integer'))
value = '123' value = '123'
with pytest.raises(UnmarshallValueError): with pytest.raises(UnmarshalValueError):
schema.unmarshal(value) schema.unmarshal(value)
def test_boolean_valid(self): def test_boolean_valid(self):