From 9837b8699e44afe15f3d869a0616bcefb2477d42 Mon Sep 17 00:00:00 2001 From: p1c2u Date: Fri, 11 Feb 2022 09:54:51 +0000 Subject: [PATCH] Validators exceptions catch --- openapi_core/validation/request/exceptions.py | 10 ++ openapi_core/validation/request/validators.py | 102 +++++++++++------- .../validation/response/exceptions.py | 10 ++ .../validation/response/validators.py | 96 ++++++++++------- 4 files changed, 143 insertions(+), 75 deletions(-) create mode 100644 openapi_core/validation/request/exceptions.py create mode 100644 openapi_core/validation/response/exceptions.py diff --git a/openapi_core/validation/request/exceptions.py b/openapi_core/validation/request/exceptions.py new file mode 100644 index 00000000..356c3b8c --- /dev/null +++ b/openapi_core/validation/request/exceptions.py @@ -0,0 +1,10 @@ +from dataclasses import dataclass +from typing import List + +from openapi_core.validation.request.datatypes import Parameters + + +@dataclass +class ParametersError(Exception): + parameters: Parameters + context: List[Exception] diff --git a/openapi_core/validation/request/validators.py b/openapi_core/validation/request/validators.py index 06d18f42..711f6d2b 100644 --- a/openapi_core/validation/request/validators.py +++ b/openapi_core/validation/request/validators.py @@ -21,6 +21,7 @@ from openapi_core.validation.exceptions import InvalidSecurity from openapi_core.validation.request.datatypes import Parameters from openapi_core.validation.request.datatypes import RequestValidationResult +from openapi_core.validation.request.exceptions import ParametersError from openapi_core.validation.validators import BaseValidator @@ -74,7 +75,10 @@ def _get_parameters(self, request, path, operation): location = getattr(parameters, param_location) location[param_name] = value - return parameters, errors + if errors: + raise ParametersError(context=errors, parameters=parameters) + + return parameters def _get_parameter(self, param, request): name = param["name"] @@ -114,7 +118,7 @@ def _get_security(self, request, operation): except SecurityError: continue - raise InvalidSecurity() + raise InvalidSecurity def _get_security_value(self, scheme_name, request): security_schemes = self.spec / "components#securitySchemes" @@ -126,44 +130,24 @@ def _get_security_value(self, scheme_name, request): def _get_body(self, request, operation): if "requestBody" not in operation: - return None, [] + return None request_body = operation / "requestBody" - try: - raw_body = self._get_body_value(request_body, request) - except MissingRequiredRequestBody as exc: - return None, [exc] - except MissingRequestBody: - return None, [] - - try: - media_type, mimetype = self._get_media_type( - request_body / "content", request.mimetype - ) - except MediaTypeFinderError as exc: - return None, [exc] - - try: - deserialised = self._deserialise_data(mimetype, raw_body) - except DeserializeError as exc: - return None, [exc] - - try: - casted = self._cast(media_type, deserialised) - except CastError as exc: - return None, [exc] + raw_body = self._get_body_value(request_body, request) + media_type, mimetype = self._get_media_type( + request_body / "content", request.mimetype + ) + deserialised = self._deserialise_data(mimetype, raw_body) + casted = self._cast(media_type, deserialised) if "schema" not in media_type: - return casted, [] + return casted schema = media_type / "schema" - try: - body = self._unmarshal(schema, casted) - except (ValidateError, UnmarshalError) as exc: - return None, [exc] + body = self._unmarshal(schema, casted) - return body, [] + return body def _get_body_value(self, request_body, request): if not request.body: @@ -186,7 +170,13 @@ def validate(self, request): request.parameters.path or path_result.variables ) - params, params_errors = self._get_parameters(request, path, operation) + try: + params = self._get_parameters(request, path, operation) + except ParametersError as exc: + params = exc.parameters + params_errors = exc.context + else: + params_errors = [] return RequestValidationResult( errors=params_errors, @@ -203,10 +193,26 @@ def validate(self, request): except PathError as exc: return RequestValidationResult(errors=[exc]) - body, body_errors = self._get_body(request, operation) + try: + body = self._get_body(request, operation) + except ( + MissingRequiredRequestBody, + MediaTypeFinderError, + DeserializeError, + CastError, + ValidateError, + UnmarshalError, + ) as exc: + body = None + errors = [exc] + except MissingRequestBody: + body = None + errors = [] + else: + errors = [] return RequestValidationResult( - errors=body_errors, + errors=errors, body=body, ) @@ -250,9 +256,31 @@ def validate(self, request): request.parameters.path or path_result.variables ) - params, params_errors = self._get_parameters(request, path, operation) + try: + params = self._get_parameters(request, path, operation) + except ParametersError as exc: + params = exc.parameters + params_errors = exc.context + else: + params_errors = [] - body, body_errors = self._get_body(request, operation) + try: + body = self._get_body(request, operation) + except ( + MissingRequiredRequestBody, + MediaTypeFinderError, + DeserializeError, + CastError, + ValidateError, + UnmarshalError, + ) as exc: + body = None + body_errors = [exc] + except MissingRequestBody: + body = None + body_errors = [] + else: + body_errors = [] errors = params_errors + body_errors return RequestValidationResult( diff --git a/openapi_core/validation/response/exceptions.py b/openapi_core/validation/response/exceptions.py new file mode 100644 index 00000000..8466de83 --- /dev/null +++ b/openapi_core/validation/response/exceptions.py @@ -0,0 +1,10 @@ +from dataclasses import dataclass +from typing import Any +from typing import Dict +from typing import List + + +@dataclass +class HeadersError(Exception): + headers: Dict[str, Any] + context: List[Exception] diff --git a/openapi_core/validation/response/validators.py b/openapi_core/validation/response/validators.py index 94b2da91..388c0ec6 100644 --- a/openapi_core/validation/response/validators.py +++ b/openapi_core/validation/response/validators.py @@ -16,6 +16,7 @@ SchemaUnmarshallersFactory, ) from openapi_core.validation.response.datatypes import ResponseValidationResult +from openapi_core.validation.response.exceptions import HeadersError from openapi_core.validation.validators import BaseValidator @@ -46,40 +47,22 @@ def _get_operation_response(self, operation, response): def _get_data(self, response, operation_response): if "content" not in operation_response: - return None, [] + return None - try: - media_type, mimetype = self._get_media_type( - operation_response / "content", response.mimetype - ) - except MediaTypeFinderError as exc: - return None, [exc] - - try: - raw_data = self._get_data_value(response) - except MissingResponseContent as exc: - return None, [exc] - - try: - deserialised = self._deserialise_data(mimetype, raw_data) - except DeserializeError as exc: - return None, [exc] - - try: - casted = self._cast(media_type, deserialised) - except CastError as exc: - return None, [exc] + media_type, mimetype = self._get_media_type( + operation_response / "content", response.mimetype + ) + raw_data = self._get_data_value(response) + deserialised = self._deserialise_data(mimetype, raw_data) + casted = self._cast(media_type, deserialised) if "schema" not in media_type: - return casted, [] + return casted schema = media_type / "schema" - try: - data = self._unmarshal(schema, casted) - except (ValidateError, UnmarshalError) as exc: - return None, [exc] + data = self._unmarshal(schema, casted) - return data, [] + return data def _get_data_value(self, response): if not response.data: @@ -89,7 +72,7 @@ def _get_data_value(self, response): def _get_headers(self, response, operation_response): if "headers" not in operation_response: - return {}, [] + return {} headers = operation_response / "headers" @@ -115,7 +98,10 @@ def _get_headers(self, response, operation_response): else: validated[name] = value - return validated, errors + if errors: + raise HeadersError(context=errors, headers=validated) + + return validated def _get_header(self, name, header, response): deprecated = header.getkey("deprecated", False) @@ -146,7 +132,20 @@ def validate(self, request, response): except (PathError, ResponseFinderError) as exc: return ResponseValidationResult(errors=[exc]) - data, data_errors = self._get_data(response, operation_response) + try: + data = self._get_data(response, operation_response) + except ( + MediaTypeFinderError, + MissingResponseContent, + DeserializeError, + CastError, + ValidateError, + UnmarshalError, + ) as exc: + data = None + data_errors = [exc] + else: + data_errors = [] return ResponseValidationResult( errors=data_errors, @@ -164,9 +163,13 @@ def validate(self, request, response): except (PathError, ResponseFinderError) as exc: return ResponseValidationResult(errors=[exc]) - headers, headers_errors = self._get_headers( - response, operation_response - ) + try: + headers = self._get_headers(response, operation_response) + except HeadersError as exc: + headers = exc.headers + headers_errors = exc.context + else: + headers_errors = [] return ResponseValidationResult( errors=headers_errors, @@ -184,11 +187,28 @@ def validate(self, request, response): except (PathError, ResponseFinderError) as exc: return ResponseValidationResult(errors=[exc]) - data, data_errors = self._get_data(response, operation_response) + try: + data = self._get_data(response, operation_response) + except ( + MediaTypeFinderError, + MissingResponseContent, + DeserializeError, + CastError, + ValidateError, + UnmarshalError, + ) as exc: + data = None + data_errors = [exc] + else: + data_errors = [] - headers, headers_errors = self._get_headers( - response, operation_response - ) + try: + headers = self._get_headers(response, operation_response) + except HeadersError as exc: + headers = exc.headers + headers_errors = exc.context + else: + headers_errors = [] errors = data_errors + headers_errors return ResponseValidationResult(