From a461f785f8076515b65cdc0d3df96a846d56cb1f Mon Sep 17 00:00:00 2001 From: p1c2u Date: Tue, 6 Sep 2022 16:34:22 +0100 Subject: [PATCH] Predefined openapi validators --- openapi_core/__init__.py | 4 +- openapi_core/contrib/django/handlers.py | 2 +- openapi_core/contrib/django/middlewares.py | 14 +- openapi_core/contrib/falcon/handlers.py | 2 +- openapi_core/contrib/falcon/middlewares.py | 17 +- openapi_core/contrib/flask/decorators.py | 13 +- openapi_core/contrib/flask/views.py | 12 +- openapi_core/exceptions.py | 88 --- openapi_core/validation/__init__.py | 20 + openapi_core/validation/decorators.py | 6 +- openapi_core/validation/exceptions.py | 48 ++ openapi_core/validation/processors.py | 8 +- openapi_core/validation/request/__init__.py | 19 + openapi_core/validation/request/exceptions.py | 26 + openapi_core/validation/request/shortcuts.py | 48 -- openapi_core/validation/request/validators.py | 54 +- openapi_core/validation/response/__init__.py | 16 + .../validation/response/exceptions.py | 15 + openapi_core/validation/response/shortcuts.py | 40 - .../validation/response/validators.py | 45 +- openapi_core/validation/shortcuts.py | 23 + openapi_core/validation/validators.py | 4 - .../contrib/django/test_django_project.py | 6 +- .../contrib/falcon/test_falcon_project.py | 4 +- .../contrib/flask/test_flask_views.py | 2 +- .../requests/test_requests_validation.py | 15 +- tests/integration/validation/test_minimal.py | 11 +- tests/integration/validation/test_petstore.py | 691 +++++++++++------- .../validation/test_read_only_write_only.py | 30 +- .../validation/test_security_override.py | 27 +- .../integration/validation/test_validators.py | 142 ++-- .../unit/validation/test_request_shortcuts.py | 10 +- .../validation/test_response_shortcuts.py | 10 +- 33 files changed, 854 insertions(+), 618 deletions(-) delete mode 100644 openapi_core/validation/request/shortcuts.py delete mode 100644 openapi_core/validation/response/shortcuts.py create mode 100644 openapi_core/validation/shortcuts.py diff --git a/openapi_core/__init__.py b/openapi_core/__init__.py index 56d0a145..6927a707 100644 --- a/openapi_core/__init__.py +++ b/openapi_core/__init__.py @@ -1,18 +1,18 @@ """OpenAPI core module""" from openapi_core.spec import OpenAPIv30Spec -from openapi_core.validation.request.shortcuts import validate_request from openapi_core.validation.request.validators import RequestBodyValidator from openapi_core.validation.request.validators import ( RequestParametersValidator, ) from openapi_core.validation.request.validators import RequestSecurityValidator from openapi_core.validation.request.validators import RequestValidator -from openapi_core.validation.response.shortcuts import validate_response from openapi_core.validation.response.validators import ResponseDataValidator from openapi_core.validation.response.validators import ( ResponseHeadersValidator, ) from openapi_core.validation.response.validators import ResponseValidator +from openapi_core.validation.shortcuts import validate_request +from openapi_core.validation.shortcuts import validate_response __author__ = "Artur Maciag" __email__ = "maciag.artur@gmail.com" diff --git a/openapi_core/contrib/django/handlers.py b/openapi_core/contrib/django/handlers.py index 9dd808e5..6d20c340 100644 --- a/openapi_core/contrib/django/handlers.py +++ b/openapi_core/contrib/django/handlers.py @@ -1,12 +1,12 @@ """OpenAPI core contrib django handlers module""" from django.http import JsonResponse -from openapi_core.exceptions import MissingRequiredParameter from openapi_core.templating.media_types.exceptions import MediaTypeNotFound from openapi_core.templating.paths.exceptions import OperationNotFound from openapi_core.templating.paths.exceptions import PathNotFound from openapi_core.templating.paths.exceptions import ServerNotFound from openapi_core.validation.exceptions import InvalidSecurity +from openapi_core.validation.exceptions import MissingRequiredParameter class DjangoOpenAPIErrorsHandler: diff --git a/openapi_core/contrib/django/middlewares.py b/openapi_core/contrib/django/middlewares.py index 7226cfe9..08de5f71 100644 --- a/openapi_core/contrib/django/middlewares.py +++ b/openapi_core/contrib/django/middlewares.py @@ -6,8 +6,8 @@ from openapi_core.contrib.django.requests import DjangoOpenAPIRequest from openapi_core.contrib.django.responses import DjangoOpenAPIResponse from openapi_core.validation.processors import OpenAPIProcessor -from openapi_core.validation.request.validators import RequestValidator -from openapi_core.validation.response.validators import ResponseValidator +from openapi_core.validation.request import openapi_request_validator +from openapi_core.validation.response import openapi_response_validator class DjangoOpenAPIMiddleware: @@ -22,15 +22,15 @@ def __init__(self, get_response): if not hasattr(settings, "OPENAPI_SPEC"): raise ImproperlyConfigured("OPENAPI_SPEC not defined in settings") - request_validator = RequestValidator(settings.OPENAPI_SPEC) - response_validator = ResponseValidator(settings.OPENAPI_SPEC) self.validation_processor = OpenAPIProcessor( - request_validator, response_validator + openapi_request_validator, openapi_response_validator ) def __call__(self, request): openapi_request = self._get_openapi_request(request) - req_result = self.validation_processor.process_request(openapi_request) + req_result = self.validation_processor.process_request( + settings.OPENAPI_SPEC, openapi_request + ) if req_result.errors: response = self._handle_request_errors(req_result, request) else: @@ -39,7 +39,7 @@ def __call__(self, request): openapi_response = self._get_openapi_response(response) resp_result = self.validation_processor.process_response( - openapi_request, openapi_response + settings.OPENAPI_SPEC, openapi_request, openapi_response ) if resp_result.errors: return self._handle_response_errors(resp_result, request, response) diff --git a/openapi_core/contrib/falcon/handlers.py b/openapi_core/contrib/falcon/handlers.py index a01e70dc..77d2e63f 100644 --- a/openapi_core/contrib/falcon/handlers.py +++ b/openapi_core/contrib/falcon/handlers.py @@ -4,12 +4,12 @@ from falcon import status_codes from falcon.constants import MEDIA_JSON -from openapi_core.exceptions import MissingRequiredParameter from openapi_core.templating.media_types.exceptions import MediaTypeNotFound from openapi_core.templating.paths.exceptions import OperationNotFound from openapi_core.templating.paths.exceptions import PathNotFound from openapi_core.templating.paths.exceptions import ServerNotFound from openapi_core.validation.exceptions import InvalidSecurity +from openapi_core.validation.exceptions import MissingRequiredParameter class FalconOpenAPIErrorsHandler: diff --git a/openapi_core/contrib/falcon/middlewares.py b/openapi_core/contrib/falcon/middlewares.py index 0311aee0..eac38a24 100644 --- a/openapi_core/contrib/falcon/middlewares.py +++ b/openapi_core/contrib/falcon/middlewares.py @@ -4,8 +4,8 @@ from openapi_core.contrib.falcon.requests import FalconOpenAPIRequest from openapi_core.contrib.falcon.responses import FalconOpenAPIResponse from openapi_core.validation.processors import OpenAPIProcessor -from openapi_core.validation.request.validators import RequestValidator -from openapi_core.validation.response.validators import ResponseValidator +from openapi_core.validation.request import openapi_request_validator +from openapi_core.validation.response import openapi_response_validator class FalconOpenAPIMiddleware: @@ -16,11 +16,13 @@ class FalconOpenAPIMiddleware: def __init__( self, + spec, validation_processor, request_class=None, response_class=None, errors_handler=None, ): + self.spec = spec self.validation_processor = validation_processor self.request_class = request_class or self.request_class self.response_class = response_class or self.response_class @@ -34,12 +36,11 @@ def from_spec( response_class=None, errors_handler=None, ): - request_validator = RequestValidator(spec) - response_validator = ResponseValidator(spec) validation_processor = OpenAPIProcessor( - request_validator, response_validator + openapi_request_validator, openapi_response_validator ) return cls( + spec, validation_processor, request_class=request_class, response_class=response_class, @@ -76,9 +77,11 @@ def _get_openapi_response(self, response): return self.response_class(response) def _process_openapi_request(self, openapi_request): - return self.validation_processor.process_request(openapi_request) + return self.validation_processor.process_request( + self.spec, openapi_request + ) def _process_openapi_response(self, opneapi_request, openapi_response): return self.validation_processor.process_response( - opneapi_request, openapi_response + self.spec, opneapi_request, openapi_response ) diff --git a/openapi_core/contrib/flask/decorators.py b/openapi_core/contrib/flask/decorators.py index 621a5bf5..45025808 100644 --- a/openapi_core/contrib/flask/decorators.py +++ b/openapi_core/contrib/flask/decorators.py @@ -4,13 +4,14 @@ from openapi_core.contrib.flask.requests import FlaskOpenAPIRequest from openapi_core.contrib.flask.responses import FlaskOpenAPIResponse from openapi_core.validation.decorators import OpenAPIDecorator -from openapi_core.validation.request.validators import RequestValidator -from openapi_core.validation.response.validators import ResponseValidator +from openapi_core.validation.request import openapi_request_validator +from openapi_core.validation.response import openapi_response_validator class FlaskOpenAPIViewDecorator(OpenAPIDecorator): def __init__( self, + spec, request_validator, response_validator, request_class=FlaskOpenAPIRequest, @@ -19,6 +20,7 @@ def __init__( openapi_errors_handler=FlaskOpenAPIErrorsHandler, ): super().__init__( + spec, request_validator, response_validator, request_class, @@ -43,11 +45,10 @@ def from_spec( request_provider=FlaskRequestProvider, openapi_errors_handler=FlaskOpenAPIErrorsHandler, ): - request_validator = RequestValidator(spec) - response_validator = ResponseValidator(spec) return cls( - request_validator=request_validator, - response_validator=response_validator, + spec, + request_validator=openapi_request_validator, + response_validator=openapi_response_validator, request_class=request_class, response_class=response_class, request_provider=request_provider, diff --git a/openapi_core/contrib/flask/views.py b/openapi_core/contrib/flask/views.py index 74535695..5bb58778 100644 --- a/openapi_core/contrib/flask/views.py +++ b/openapi_core/contrib/flask/views.py @@ -3,8 +3,8 @@ from openapi_core.contrib.flask.decorators import FlaskOpenAPIViewDecorator from openapi_core.contrib.flask.handlers import FlaskOpenAPIErrorsHandler -from openapi_core.validation.request.validators import RequestValidator -from openapi_core.validation.response.validators import ResponseValidator +from openapi_core.validation.request import openapi_request_validator +from openapi_core.validation.response import openapi_response_validator class FlaskOpenAPIView(MethodView): @@ -14,13 +14,13 @@ class FlaskOpenAPIView(MethodView): def __init__(self, spec): super().__init__() - self.request_validator = RequestValidator(spec) - self.response_validator = ResponseValidator(spec) + self.spec = spec def dispatch_request(self, *args, **kwargs): decorator = FlaskOpenAPIViewDecorator( - request_validator=self.request_validator, - response_validator=self.response_validator, + self.spec, + request_validator=openapi_request_validator, + response_validator=openapi_response_validator, openapi_errors_handler=self.openapi_errors_handler, ) return decorator(super().dispatch_request)(*args, **kwargs) diff --git a/openapi_core/exceptions.py b/openapi_core/exceptions.py index e1755749..504173c5 100644 --- a/openapi_core/exceptions.py +++ b/openapi_core/exceptions.py @@ -1,93 +1,5 @@ """OpenAPI core exceptions module""" -from dataclasses import dataclass - -from openapi_core.validation.request.protocols import Request -from openapi_core.validation.response.protocols import Response class OpenAPIError(Exception): pass - - -class OpenAPIHeaderError(OpenAPIError): - pass - - -class MissingHeaderError(OpenAPIHeaderError): - """Missing header error""" - - -@dataclass -class MissingHeader(MissingHeaderError): - name: str - - def __str__(self): - return f"Missing header (without default value): {self.name}" - - -@dataclass -class MissingRequiredHeader(MissingHeaderError): - name: str - - def __str__(self): - return f"Missing required header: {self.name}" - - -class OpenAPIParameterError(OpenAPIError): - pass - - -class MissingParameterError(OpenAPIParameterError): - """Missing parameter error""" - - -@dataclass -class MissingParameter(MissingParameterError): - name: str - - def __str__(self): - return f"Missing parameter (without default value): {self.name}" - - -@dataclass -class MissingRequiredParameter(MissingParameterError): - name: str - - def __str__(self): - return f"Missing required parameter: {self.name}" - - -class OpenAPIRequestBodyError(OpenAPIError): - pass - - -class MissingRequestBodyError(OpenAPIRequestBodyError): - """Missing request body error""" - - -@dataclass -class MissingRequestBody(MissingRequestBodyError): - request: Request - - def __str__(self): - return "Missing request body" - - -@dataclass -class MissingRequiredRequestBody(MissingRequestBodyError): - request: Request - - def __str__(self): - return "Missing required request body" - - -class OpenAPIResponseError(OpenAPIError): - pass - - -@dataclass -class MissingResponseContent(OpenAPIResponseError): - response: Response - - def __str__(self): - return "Missing response content" diff --git a/openapi_core/validation/__init__.py b/openapi_core/validation/__init__.py index e69de29b..52d41ee2 100644 --- a/openapi_core/validation/__init__.py +++ b/openapi_core/validation/__init__.py @@ -0,0 +1,20 @@ +"""OpenAPI core validation module""" +from openapi_core.validation.request import openapi_request_body_validator +from openapi_core.validation.request import ( + openapi_request_parameters_validator, +) +from openapi_core.validation.request import openapi_request_security_validator +from openapi_core.validation.request import openapi_request_validator +from openapi_core.validation.response import openapi_response_data_validator +from openapi_core.validation.response import openapi_response_headers_validator +from openapi_core.validation.response import openapi_response_validator + +__all__ = [ + "openapi_request_body_validator", + "openapi_request_parameters_validator", + "openapi_request_security_validator", + "openapi_request_validator", + "openapi_response_data_validator", + "openapi_response_headers_validator", + "openapi_response_validator", +] diff --git a/openapi_core/validation/decorators.py b/openapi_core/validation/decorators.py index 2b0899cd..9d8ce93c 100644 --- a/openapi_core/validation/decorators.py +++ b/openapi_core/validation/decorators.py @@ -7,6 +7,7 @@ class OpenAPIDecorator(OpenAPIProcessor): def __init__( self, + spec, request_validator, response_validator, request_class, @@ -15,6 +16,7 @@ def __init__( openapi_errors_handler, ): super().__init__(request_validator, response_validator) + self.spec = spec self.request_class = request_class self.response_class = response_class self.request_provider = request_provider @@ -25,7 +27,7 @@ def __call__(self, view): def decorated(*args, **kwargs): request = self._get_request(*args, **kwargs) openapi_request = self._get_openapi_request(request) - request_result = self.process_request(openapi_request) + request_result = self.process_request(self.spec, openapi_request) if request_result.errors: return self._handle_request_errors(request_result) response = self._handle_request_view( @@ -33,7 +35,7 @@ def decorated(*args, **kwargs): ) openapi_response = self._get_openapi_response(response) response_result = self.process_response( - openapi_request, openapi_response + self.spec, openapi_request, openapi_response ) if response_result.errors: return self._handle_response_errors(response_result) diff --git a/openapi_core/validation/exceptions.py b/openapi_core/validation/exceptions.py index dab7aa24..2cc2b191 100644 --- a/openapi_core/validation/exceptions.py +++ b/openapi_core/validation/exceptions.py @@ -12,3 +12,51 @@ class ValidationError(OpenAPIError): class InvalidSecurity(ValidationError): def __str__(self): return "Security not valid for any requirement" + + +class OpenAPIParameterError(OpenAPIError): + pass + + +class MissingParameterError(OpenAPIParameterError): + """Missing parameter error""" + + +@dataclass +class MissingParameter(MissingParameterError): + name: str + + def __str__(self): + return f"Missing parameter (without default value): {self.name}" + + +@dataclass +class MissingRequiredParameter(MissingParameterError): + name: str + + def __str__(self): + return f"Missing required parameter: {self.name}" + + +class OpenAPIHeaderError(OpenAPIError): + pass + + +class MissingHeaderError(OpenAPIHeaderError): + """Missing header error""" + + +@dataclass +class MissingHeader(MissingHeaderError): + name: str + + def __str__(self): + return f"Missing header (without default value): {self.name}" + + +@dataclass +class MissingRequiredHeader(MissingHeaderError): + name: str + + def __str__(self): + return f"Missing required header: {self.name}" diff --git a/openapi_core/validation/processors.py b/openapi_core/validation/processors.py index 587ebde8..abaf4974 100644 --- a/openapi_core/validation/processors.py +++ b/openapi_core/validation/processors.py @@ -6,8 +6,8 @@ def __init__(self, request_validator, response_validator): self.request_validator = request_validator self.response_validator = response_validator - def process_request(self, request): - return self.request_validator.validate(request) + def process_request(self, spec, request): + return self.request_validator.validate(spec, request) - def process_response(self, request, response): - return self.response_validator.validate(request, response) + def process_response(self, spec, request, response): + return self.response_validator.validate(spec, request, response) diff --git a/openapi_core/validation/request/__init__.py b/openapi_core/validation/request/__init__.py index e69de29b..54a69a34 100644 --- a/openapi_core/validation/request/__init__.py +++ b/openapi_core/validation/request/__init__.py @@ -0,0 +1,19 @@ +"""OpenAPI core validation request module""" +from openapi_core.validation.request.validators import RequestBodyValidator +from openapi_core.validation.request.validators import ( + RequestParametersValidator, +) +from openapi_core.validation.request.validators import RequestSecurityValidator +from openapi_core.validation.request.validators import RequestValidator + +__all__ = [ + "openapi_request_body_validator", + "openapi_request_parameters_validator", + "openapi_request_security_validator", + "openapi_request_validator", +] + +openapi_request_body_validator = RequestBodyValidator() +openapi_request_parameters_validator = RequestParametersValidator() +openapi_request_security_validator = RequestSecurityValidator() +openapi_request_validator = RequestValidator() diff --git a/openapi_core/validation/request/exceptions.py b/openapi_core/validation/request/exceptions.py index 356c3b8c..18d9b37f 100644 --- a/openapi_core/validation/request/exceptions.py +++ b/openapi_core/validation/request/exceptions.py @@ -1,10 +1,36 @@ from dataclasses import dataclass from typing import List +from openapi_core.exceptions import OpenAPIError from openapi_core.validation.request.datatypes import Parameters +from openapi_core.validation.request.protocols import Request @dataclass class ParametersError(Exception): parameters: Parameters context: List[Exception] + + +class OpenAPIRequestBodyError(OpenAPIError): + pass + + +class MissingRequestBodyError(OpenAPIRequestBodyError): + """Missing request body error""" + + +@dataclass +class MissingRequestBody(MissingRequestBodyError): + request: Request + + def __str__(self): + return "Missing request body" + + +@dataclass +class MissingRequiredRequestBody(MissingRequestBodyError): + request: Request + + def __str__(self): + return "Missing required request body" diff --git a/openapi_core/validation/request/shortcuts.py b/openapi_core/validation/request/shortcuts.py deleted file mode 100644 index e810938f..00000000 --- a/openapi_core/validation/request/shortcuts.py +++ /dev/null @@ -1,48 +0,0 @@ -"""OpenAPI core validation request shortcuts module""" -from openapi_core.validation.request.validators import RequestBodyValidator -from openapi_core.validation.request.validators import ( - RequestParametersValidator, -) -from openapi_core.validation.request.validators import RequestSecurityValidator -from openapi_core.validation.request.validators import RequestValidator - - -def validate_request(validator, request): - result = validator.validate(request) - result.raise_for_errors() - return result - - -def spec_validate_request(spec, request, base_url=None): - validator = RequestValidator( - spec, - base_url=base_url, - ) - return validate_request(validator, request) - - -def spec_validate_body(spec, request, base_url=None): - validator = RequestBodyValidator( - spec, - base_url=base_url, - ) - result = validate_request(validator, request) - return result.body - - -def spec_validate_parameters(spec, request, base_url=None): - validator = RequestParametersValidator( - spec, - base_url=base_url, - ) - result = validate_request(validator, request) - return result.parameters - - -def spec_validate_security(spec, request, base_url=None): - validator = RequestSecurityValidator( - spec, - base_url=base_url, - ) - result = validate_request(validator, request) - return result.security diff --git a/openapi_core/validation/request/validators.py b/openapi_core/validation/request/validators.py index ca62b4d8..7af369c6 100644 --- a/openapi_core/validation/request/validators.py +++ b/openapi_core/validation/request/validators.py @@ -3,10 +3,6 @@ from openapi_core.casting.schemas.exceptions import CastError from openapi_core.deserializing.exceptions import DeserializeError -from openapi_core.exceptions import MissingParameter -from openapi_core.exceptions import MissingRequestBody -from openapi_core.exceptions import MissingRequiredParameter -from openapi_core.exceptions import MissingRequiredRequestBody from openapi_core.schema.parameters import iter_params from openapi_core.security.exceptions import SecurityError from openapi_core.security.factories import SecurityProviderFactory @@ -19,13 +15,27 @@ SchemaUnmarshallersFactory, ) from openapi_core.validation.exceptions import InvalidSecurity +from openapi_core.validation.exceptions import MissingParameter +from openapi_core.validation.exceptions import MissingRequiredParameter from openapi_core.validation.request.datatypes import Parameters from openapi_core.validation.request.datatypes import RequestValidationResult +from openapi_core.validation.request.exceptions import MissingRequestBody +from openapi_core.validation.request.exceptions import ( + MissingRequiredRequestBody, +) from openapi_core.validation.request.exceptions import ParametersError from openapi_core.validation.validators import BaseValidator class BaseRequestValidator(BaseValidator): + def validate( + self, + spec, + request, + base_url=None, + ): + raise NotImplementedError + @property def schema_unmarshallers_factory(self): spec_resolver = ( @@ -158,7 +168,14 @@ def _get_body_value(self, request_body, request): class RequestParametersValidator(BaseRequestValidator): - def validate(self, request): + def validate( + self, + spec, + request, + base_url=None, + ): + self.spec = spec + self.base_url = base_url try: path, operation, _, path_result, _ = self._find_path(request) except PathError as exc: @@ -183,7 +200,14 @@ def validate(self, request): class RequestBodyValidator(BaseRequestValidator): - def validate(self, request): + def validate( + self, + spec, + request, + base_url=None, + ): + self.spec = spec + self.base_url = base_url try: _, operation, _, _, _ = self._find_path(request) except PathError as exc: @@ -214,7 +238,14 @@ def validate(self, request): class RequestSecurityValidator(BaseRequestValidator): - def validate(self, request): + def validate( + self, + spec, + request, + base_url=None, + ): + self.spec = spec + self.base_url = base_url try: _, operation, _, _, _ = self._find_path(request) except PathError as exc: @@ -232,7 +263,14 @@ def validate(self, request): class RequestValidator(BaseRequestValidator): - def validate(self, request): + def validate( + self, + spec, + request, + base_url=None, + ): + self.spec = spec + self.base_url = base_url try: path, operation, _, path_result, _ = self._find_path(request) # don't process if operation errors diff --git a/openapi_core/validation/response/__init__.py b/openapi_core/validation/response/__init__.py index e69de29b..5c0fed0c 100644 --- a/openapi_core/validation/response/__init__.py +++ b/openapi_core/validation/response/__init__.py @@ -0,0 +1,16 @@ +"""OpenAPI core validation response module""" +from openapi_core.validation.response.validators import ResponseDataValidator +from openapi_core.validation.response.validators import ( + ResponseHeadersValidator, +) +from openapi_core.validation.response.validators import ResponseValidator + +__all__ = [ + "openapi_response_data_validator", + "openapi_response_headers_validator", + "openapi_response_validator", +] + +openapi_response_data_validator = ResponseDataValidator() +openapi_response_headers_validator = ResponseHeadersValidator() +openapi_response_validator = ResponseValidator() diff --git a/openapi_core/validation/response/exceptions.py b/openapi_core/validation/response/exceptions.py index 8466de83..5808f23b 100644 --- a/openapi_core/validation/response/exceptions.py +++ b/openapi_core/validation/response/exceptions.py @@ -3,8 +3,23 @@ from typing import Dict from typing import List +from openapi_core.exceptions import OpenAPIError +from openapi_core.validation.response.protocols import Response + @dataclass class HeadersError(Exception): headers: Dict[str, Any] context: List[Exception] + + +class OpenAPIResponseError(OpenAPIError): + pass + + +@dataclass +class MissingResponseContent(OpenAPIResponseError): + response: Response + + def __str__(self): + return "Missing response content" diff --git a/openapi_core/validation/response/shortcuts.py b/openapi_core/validation/response/shortcuts.py deleted file mode 100644 index f5ae443b..00000000 --- a/openapi_core/validation/response/shortcuts.py +++ /dev/null @@ -1,40 +0,0 @@ -"""OpenAPI core validation response shortcuts module""" -from openapi_core.validation.response.validators import ResponseDataValidator -from openapi_core.validation.response.validators import ( - ResponseHeadersValidator, -) -from openapi_core.validation.response.validators import ResponseValidator - - -def validate_response(validator, request, response): - result = validator.validate(request, response) - result.raise_for_errors() - return result - - -def spec_validate_response(spec, request, response, base_url=None): - validator = ResponseValidator( - spec, - base_url=base_url, - custom_formatters=None, - custom_media_type_deserializers=None, - ) - return validate_response(validator, request, response) - - -def spec_validate_data(spec, request, response, base_url=None): - validator = ResponseDataValidator( - spec, - base_url=base_url, - ) - result = validate_response(validator, request, response) - return result.data - - -def spec_validate_headers(spec, request, response, base_url=None): - validator = ResponseHeadersValidator( - spec, - base_url=base_url, - ) - result = validate_response(validator, request, response) - return result.headers diff --git a/openapi_core/validation/response/validators.py b/openapi_core/validation/response/validators.py index 49ecb598..8823798a 100644 --- a/openapi_core/validation/response/validators.py +++ b/openapi_core/validation/response/validators.py @@ -3,9 +3,6 @@ from openapi_core.casting.schemas.exceptions import CastError from openapi_core.deserializing.exceptions import DeserializeError -from openapi_core.exceptions import MissingHeader -from openapi_core.exceptions import MissingRequiredHeader -from openapi_core.exceptions import MissingResponseContent from openapi_core.templating.media_types.exceptions import MediaTypeFinderError from openapi_core.templating.paths.exceptions import PathError from openapi_core.templating.responses.exceptions import ResponseFinderError @@ -15,12 +12,24 @@ from openapi_core.unmarshalling.schemas.factories import ( SchemaUnmarshallersFactory, ) +from openapi_core.validation.exceptions import MissingHeader +from openapi_core.validation.exceptions import MissingRequiredHeader from openapi_core.validation.response.datatypes import ResponseValidationResult from openapi_core.validation.response.exceptions import HeadersError +from openapi_core.validation.response.exceptions import MissingResponseContent from openapi_core.validation.validators import BaseValidator class BaseResponseValidator(BaseValidator): + def validate( + self, + spec, + request, + response, + base_url=None, + ): + raise NotImplementedError + @property def schema_unmarshallers_factory(self): spec_resolver = ( @@ -121,7 +130,15 @@ def _get_header(self, name, header, response): class ResponseDataValidator(BaseResponseValidator): - def validate(self, request, response): + def validate( + self, + spec, + request, + response, + base_url=None, + ): + self.spec = spec + self.base_url = base_url try: operation_response = self._find_operation_response( request, response @@ -152,7 +169,15 @@ def validate(self, request, response): class ResponseHeadersValidator(BaseResponseValidator): - def validate(self, request, response): + def validate( + self, + spec, + request, + response, + base_url=None, + ): + self.spec = spec + self.base_url = base_url try: operation_response = self._find_operation_response( request, response @@ -176,7 +201,15 @@ def validate(self, request, response): class ResponseValidator(BaseResponseValidator): - def validate(self, request, response): + def validate( + self, + spec, + request, + response, + base_url=None, + ): + self.spec = spec + self.base_url = base_url try: operation_response = self._find_operation_response( request, response diff --git a/openapi_core/validation/shortcuts.py b/openapi_core/validation/shortcuts.py new file mode 100644 index 00000000..5818d38f --- /dev/null +++ b/openapi_core/validation/shortcuts.py @@ -0,0 +1,23 @@ +"""OpenAPI core validation shortcuts module""" +from openapi_core.validation.request import openapi_request_validator +from openapi_core.validation.response import openapi_response_validator + + +def validate_request( + spec, request, base_url=None, validator=openapi_request_validator +): + result = validator.validate(spec, request, base_url=base_url) + result.raise_for_errors() + return result + + +def validate_response( + spec, + request, + response, + base_url=None, + validator=openapi_response_validator, +): + result = validator.validate(spec, request, response, base_url=base_url) + result.raise_for_errors() + return result diff --git a/openapi_core/validation/validators.py b/openapi_core/validation/validators.py index d22af9a1..445856d1 100644 --- a/openapi_core/validation/validators.py +++ b/openapi_core/validation/validators.py @@ -17,13 +17,9 @@ class BaseValidator: def __init__( self, - spec, - base_url=None, custom_formatters=None, custom_media_type_deserializers=None, ): - self.spec = spec - self.base_url = base_url self.custom_formatters = custom_formatters or {} self.custom_media_type_deserializers = custom_media_type_deserializers diff --git a/tests/integration/contrib/django/test_django_project.py b/tests/integration/contrib/django/test_django_project.py index 1c1de12c..0170bdc2 100644 --- a/tests/integration/contrib/django/test_django_project.py +++ b/tests/integration/contrib/django/test_django_project.py @@ -55,7 +55,7 @@ def test_get_no_required_param(self, client): "errors": [ { "class": ( - "" ), "status": 400, @@ -149,7 +149,7 @@ def test_post_required_header_param_missing(self, client): "errors": [ { "class": ( - "" ), "status": 400, @@ -214,7 +214,7 @@ def test_post_required_cookie_param_missing(self, client): "errors": [ { "class": ( - "" ), "status": 400, diff --git a/tests/integration/contrib/falcon/test_falcon_project.py b/tests/integration/contrib/falcon/test_falcon_project.py index c67b5b7b..921de4e0 100644 --- a/tests/integration/contrib/falcon/test_falcon_project.py +++ b/tests/integration/contrib/falcon/test_falcon_project.py @@ -120,7 +120,7 @@ def test_post_required_header_param_missing(self, client): "errors": [ { "class": ( - "" ), "status": 400, @@ -199,7 +199,7 @@ def test_post_required_cookie_param_missing(self, client): "errors": [ { "class": ( - "" ), "status": 400, diff --git a/tests/integration/contrib/flask/test_flask_views.py b/tests/integration/contrib/flask/test_flask_views.py index 34551bb6..b53e586d 100644 --- a/tests/integration/contrib/flask/test_flask_views.py +++ b/tests/integration/contrib/flask/test_flask_views.py @@ -172,7 +172,7 @@ def test_missing_required_header(self, client): "errors": [ { "class": ( - "" ), "status": 400, diff --git a/tests/integration/contrib/requests/test_requests_validation.py b/tests/integration/contrib/requests/test_requests_validation.py index 7c053576..63a983bd 100644 --- a/tests/integration/contrib/requests/test_requests_validation.py +++ b/tests/integration/contrib/requests/test_requests_validation.py @@ -5,8 +5,8 @@ from openapi_core.contrib.requests import RequestsOpenAPIRequest from openapi_core.contrib.requests import RequestsOpenAPIResponse from openapi_core.spec import OpenAPIv30Spec as Spec -from openapi_core.validation.request.validators import RequestValidator -from openapi_core.validation.response.validators import ResponseValidator +from openapi_core.validation.request import openapi_request_validator +from openapi_core.validation.response import openapi_response_validator class TestRequestsOpenAPIValidation: @@ -25,7 +25,6 @@ def test_response_validator_path_pattern(self, spec): match_querystring=True, headers={"X-Rate-Limit": "12"}, ) - validator = ResponseValidator(spec) request = requests.Request( "POST", "http://localhost/browse/12/", @@ -38,11 +37,12 @@ def test_response_validator_path_pattern(self, spec): response = session.send(request_prepared) openapi_request = RequestsOpenAPIRequest(request) openapi_response = RequestsOpenAPIResponse(response) - result = validator.validate(openapi_request, openapi_response) + result = openapi_response_validator.validate( + spec, openapi_request, openapi_response + ) assert not result.errors def test_request_validator_path_pattern(self, spec): - validator = RequestValidator(spec) request = requests.Request( "POST", "http://localhost/browse/12/", @@ -51,11 +51,10 @@ def test_request_validator_path_pattern(self, spec): json={"param1": 1}, ) openapi_request = RequestsOpenAPIRequest(request) - result = validator.validate(openapi_request) + result = openapi_request_validator.validate(spec, openapi_request) assert not result.errors def test_request_validator_prepared_request(self, spec): - validator = RequestValidator(spec) request = requests.Request( "POST", "http://localhost/browse/12/", @@ -65,5 +64,5 @@ def test_request_validator_prepared_request(self, spec): ) request_prepared = request.prepare() openapi_request = RequestsOpenAPIRequest(request_prepared) - result = validator.validate(openapi_request) + result = openapi_request_validator.validate(spec, openapi_request) assert not result.errors diff --git a/tests/integration/validation/test_minimal.py b/tests/integration/validation/test_minimal.py index a974d813..61722af4 100644 --- a/tests/integration/validation/test_minimal.py +++ b/tests/integration/validation/test_minimal.py @@ -4,8 +4,8 @@ from openapi_core.templating.paths.exceptions import OperationNotFound from openapi_core.templating.paths.exceptions import PathNotFound from openapi_core.testing import MockRequest +from openapi_core.validation.request import openapi_request_validator from openapi_core.validation.request.datatypes import Parameters -from openapi_core.validation.request.validators import RequestValidator class TestMinimal: @@ -28,10 +28,9 @@ class TestMinimal: def test_hosts(self, factory, server, spec_path): spec_dict = factory.spec_from_file(spec_path) spec = Spec.create(spec_dict) - validator = RequestValidator(spec) request = MockRequest(server, "get", "/status") - result = validator.validate(request) + result = openapi_request_validator.validate(spec, request) assert not result.errors @@ -40,10 +39,9 @@ def test_hosts(self, factory, server, spec_path): def test_invalid_operation(self, factory, server, spec_path): spec_dict = factory.spec_from_file(spec_path) spec = Spec.create(spec_dict) - validator = RequestValidator(spec) request = MockRequest(server, "post", "/status") - result = validator.validate(request) + result = openapi_request_validator.validate(spec, request) assert len(result.errors) == 1 assert isinstance(result.errors[0], OperationNotFound) @@ -55,10 +53,9 @@ def test_invalid_operation(self, factory, server, spec_path): def test_invalid_path(self, factory, server, spec_path): spec_dict = factory.spec_from_file(spec_path) spec = Spec.create(spec_dict) - validator = RequestValidator(spec) request = MockRequest(server, "get", "/nonexistent") - result = validator.validate(request) + result = openapi_request_validator.validate(spec, request) assert len(result.errors) == 1 assert isinstance(result.errors[0], PathNotFound) diff --git a/tests/integration/validation/test_petstore.py b/tests/integration/validation/test_petstore.py index 9b4ae582..69f032a4 100644 --- a/tests/integration/validation/test_petstore.py +++ b/tests/integration/validation/test_petstore.py @@ -11,8 +11,6 @@ from openapi_core.deserializing.parameters.exceptions import ( EmptyQueryParameterValue, ) -from openapi_core.exceptions import MissingRequiredHeader -from openapi_core.exceptions import MissingRequiredParameter from openapi_core.extensions.models.models import BaseModel from openapi_core.spec import OpenAPIv30Spec as Spec from openapi_core.templating.media_types.exceptions import MediaTypeNotFound @@ -20,14 +18,19 @@ from openapi_core.testing import MockRequest from openapi_core.testing import MockResponse from openapi_core.unmarshalling.schemas.exceptions import InvalidSchemaValue +from openapi_core.validation.exceptions import MissingRequiredHeader +from openapi_core.validation.exceptions import MissingRequiredParameter +from openapi_core.validation.request import openapi_request_body_validator +from openapi_core.validation.request import ( + openapi_request_parameters_validator, +) +from openapi_core.validation.request import openapi_request_security_validator from openapi_core.validation.request.datatypes import Parameters -from openapi_core.validation.request.shortcuts import spec_validate_body -from openapi_core.validation.request.shortcuts import spec_validate_parameters -from openapi_core.validation.request.shortcuts import spec_validate_security -from openapi_core.validation.request.validators import RequestValidator -from openapi_core.validation.response.shortcuts import spec_validate_data -from openapi_core.validation.response.shortcuts import spec_validate_headers -from openapi_core.validation.response.validators import ResponseValidator +from openapi_core.validation.response import openapi_response_data_validator +from openapi_core.validation.response import openapi_response_headers_validator +from openapi_core.validation.response import openapi_response_validator +from openapi_core.validation.shortcuts import validate_request +from openapi_core.validation.shortcuts import validate_response class TestPetstore: @@ -52,15 +55,7 @@ def spec_dict(self, factory): def spec(self, spec_dict, spec_uri): return Spec.create(spec_dict, url=spec_uri) - @pytest.fixture(scope="module") - def request_validator(self, spec): - return RequestValidator(spec) - - @pytest.fixture(scope="module") - def response_validator(self, spec): - return ResponseValidator(spec) - - def test_get_pets(self, spec, response_validator): + def test_get_pets(self, spec): host_url = "http://petstore.swagger.io/v1" path_pattern = "/v1/pets" query_params = { @@ -76,17 +71,23 @@ def test_get_pets(self, spec, response_validator): ) with pytest.warns(DeprecationWarning): - parameters = spec_validate_parameters(spec, request) - body = spec_validate_body(spec, request) + result = validate_request( + spec, request, validator=openapi_request_parameters_validator + ) - assert parameters == Parameters( + assert result.parameters == Parameters( query={ "limit": 20, "page": 1, "search": "", } ) - assert body is None + + result = validate_request( + spec, request, validator=openapi_request_body_validator + ) + + assert result.body is None data_json = { "data": [], @@ -98,7 +99,7 @@ def test_get_pets(self, spec, response_validator): } response = MockResponse(data, headers=headers) - response_result = response_validator.validate(request, response) + response_result = validate_response(spec, request, response) assert response_result.errors == [] assert isinstance(response_result.data, BaseModel) @@ -107,7 +108,7 @@ def test_get_pets(self, spec, response_validator): "x-next": "next-url", } - def test_get_pets_response(self, spec, response_validator): + def test_get_pets_response(self, spec): host_url = "http://petstore.swagger.io/v1" path_pattern = "/v1/pets" query_params = { @@ -123,17 +124,23 @@ def test_get_pets_response(self, spec, response_validator): ) with pytest.warns(DeprecationWarning): - parameters = spec_validate_parameters(spec, request) - body = spec_validate_body(spec, request) + result = validate_request( + spec, request, validator=openapi_request_parameters_validator + ) - assert parameters == Parameters( + assert result.parameters == Parameters( query={ "limit": 20, "page": 1, "search": "", } ) - assert body is None + + result = validate_request( + spec, request, validator=openapi_request_body_validator + ) + + assert result.body is None data_json = { "data": [ @@ -149,7 +156,7 @@ def test_get_pets_response(self, spec, response_validator): data = json.dumps(data_json) response = MockResponse(data) - response_result = response_validator.validate(request, response) + response_result = validate_response(spec, request, response) assert response_result.errors == [] assert isinstance(response_result.data, BaseModel) @@ -157,7 +164,7 @@ def test_get_pets_response(self, spec, response_validator): assert response_result.data.data[0].id == 1 assert response_result.data.data[0].name == "Cat" - def test_get_pets_response_no_schema(self, spec, response_validator): + def test_get_pets_response_no_schema(self, spec): host_url = "http://petstore.swagger.io/v1" path_pattern = "/v1/pets" query_params = { @@ -173,28 +180,34 @@ def test_get_pets_response_no_schema(self, spec, response_validator): ) with pytest.warns(DeprecationWarning): - parameters = spec_validate_parameters(spec, request) - body = spec_validate_body(spec, request) + result = validate_request( + spec, request, validator=openapi_request_parameters_validator + ) - assert parameters == Parameters( + assert result.parameters == Parameters( query={ "limit": 20, "page": 1, "search": "", } ) - assert body is None + + result = validate_request( + spec, request, validator=openapi_request_body_validator + ) + + assert result.body is None data = "" response = MockResponse(data, status_code=404, mimetype="text/html") with pytest.warns(UserWarning): - response_result = response_validator.validate(request, response) + response_result = validate_response(spec, request, response) assert response_result.errors == [] assert response_result.data == data - def test_get_pets_invalid_response(self, spec, response_validator): + def test_get_pets_invalid_response(self, spec): host_url = "http://petstore.swagger.io/v1" path_pattern = "/v1/pets" query_params = { @@ -210,17 +223,23 @@ def test_get_pets_invalid_response(self, spec, response_validator): ) with pytest.warns(DeprecationWarning): - parameters = spec_validate_parameters(spec, request) - body = spec_validate_body(spec, request) + result = validate_request( + spec, request, validator=openapi_request_parameters_validator + ) - assert parameters == Parameters( + assert result.parameters == Parameters( query={ "limit": 20, "page": 1, "search": "", } ) - assert body is None + + result = validate_request( + spec, request, validator=openapi_request_body_validator + ) + + assert result.body is None response_data_json = { "data": [ @@ -236,9 +255,16 @@ def test_get_pets_invalid_response(self, spec, response_validator): response = MockResponse(response_data) with pytest.raises(InvalidSchemaValue): - spec_validate_data(spec, request, response) - - response_result = response_validator.validate(request, response) + validate_response( + spec, + request, + response, + validator=openapi_response_data_validator, + ) + + response_result = openapi_response_validator.validate( + spec, request, response + ) schema_errors = response_result.errors[0].schema_errors assert response_result.errors == [ @@ -250,7 +276,7 @@ def test_get_pets_invalid_response(self, spec, response_validator): ] assert response_result.data is None - def test_get_pets_ids_param(self, spec, response_validator): + def test_get_pets_ids_param(self, spec): host_url = "http://petstore.swagger.io/v1" path_pattern = "/v1/pets" query_params = { @@ -267,10 +293,11 @@ def test_get_pets_ids_param(self, spec, response_validator): ) with pytest.warns(DeprecationWarning): - parameters = spec_validate_parameters(spec, request) - body = spec_validate_body(spec, request) + result = validate_request( + spec, request, validator=openapi_request_parameters_validator + ) - assert parameters == Parameters( + assert result.parameters == Parameters( query={ "limit": 20, "page": 1, @@ -278,7 +305,12 @@ def test_get_pets_ids_param(self, spec, response_validator): "ids": [12, 13], } ) - assert body is None + + result = validate_request( + spec, request, validator=openapi_request_body_validator + ) + + assert result.body is None data_json = { "data": [], @@ -286,13 +318,13 @@ def test_get_pets_ids_param(self, spec, response_validator): data = json.dumps(data_json) response = MockResponse(data) - response_result = response_validator.validate(request, response) + response_result = validate_response(spec, request, response) assert response_result.errors == [] assert isinstance(response_result.data, BaseModel) assert response_result.data.data == [] - def test_get_pets_tags_param(self, spec, response_validator): + def test_get_pets_tags_param(self, spec): host_url = "http://petstore.swagger.io/v1" path_pattern = "/v1/pets" query_params = [ @@ -309,10 +341,11 @@ def test_get_pets_tags_param(self, spec, response_validator): ) with pytest.warns(DeprecationWarning): - parameters = spec_validate_parameters(spec, request) - body = spec_validate_body(spec, request) + result = validate_request( + spec, request, validator=openapi_request_parameters_validator + ) - assert parameters == Parameters( + assert result.parameters == Parameters( query={ "limit": 20, "page": 1, @@ -320,7 +353,12 @@ def test_get_pets_tags_param(self, spec, response_validator): "tags": ["cats", "dogs"], } ) - assert body is None + + result = validate_request( + spec, request, validator=openapi_request_body_validator + ) + + assert result.body is None data_json = { "data": [], @@ -328,7 +366,7 @@ def test_get_pets_tags_param(self, spec, response_validator): data = json.dumps(data_json) response = MockResponse(data) - response_result = response_validator.validate(request, response) + response_result = validate_response(spec, request, response) assert response_result.errors == [] assert isinstance(response_result.data, BaseModel) @@ -352,11 +390,17 @@ def test_get_pets_parameter_deserialization_error(self, spec): with pytest.warns(DeprecationWarning): with pytest.raises(DeserializeError): - spec_validate_parameters(spec, request) - - body = spec_validate_body(spec, request) + validate_request( + spec, + request, + validator=openapi_request_parameters_validator, + ) + + result = validate_request( + spec, request, validator=openapi_request_body_validator + ) - assert body is None + assert result.body is None def test_get_pets_wrong_parameter_type(self, spec): host_url = "http://petstore.swagger.io/v1" @@ -375,11 +419,17 @@ def test_get_pets_wrong_parameter_type(self, spec): with pytest.warns(DeprecationWarning): with pytest.raises(CastError): - spec_validate_parameters(spec, request) - - body = spec_validate_body(spec, request) + validate_request( + spec, + request, + validator=openapi_request_parameters_validator, + ) + + result = validate_request( + spec, request, validator=openapi_request_body_validator + ) - assert body is None + assert result.body is None def test_get_pets_raises_missing_required_param(self, spec): host_url = "http://petstore.swagger.io/v1" @@ -393,11 +443,17 @@ def test_get_pets_raises_missing_required_param(self, spec): with pytest.warns(DeprecationWarning): with pytest.raises(MissingRequiredParameter): - spec_validate_parameters(spec, request) - - body = spec_validate_body(spec, request) + validate_request( + spec, + request, + validator=openapi_request_parameters_validator, + ) + + result = validate_request( + spec, request, validator=openapi_request_body_validator + ) - assert body is None + assert result.body is None def test_get_pets_empty_value(self, spec): host_url = "http://petstore.swagger.io/v1" @@ -416,10 +472,16 @@ def test_get_pets_empty_value(self, spec): with pytest.warns(DeprecationWarning): with pytest.raises(EmptyQueryParameterValue): - spec_validate_parameters(spec, request) - body = spec_validate_body(spec, request) + validate_request( + spec, + request, + validator=openapi_request_parameters_validator, + ) + result = validate_request( + spec, request, validator=openapi_request_body_validator + ) - assert body is None + assert result.body is None def test_get_pets_allow_empty_value(self, spec): host_url = "http://petstore.swagger.io/v1" @@ -438,9 +500,11 @@ def test_get_pets_allow_empty_value(self, spec): ) with pytest.warns(DeprecationWarning): - parameters = spec_validate_parameters(spec, request) + result = validate_request( + spec, request, validator=openapi_request_parameters_validator + ) - assert parameters == Parameters( + assert result.parameters == Parameters( query={ "page": 1, "limit": 20, @@ -448,9 +512,11 @@ def test_get_pets_allow_empty_value(self, spec): } ) - body = spec_validate_body(spec, request) + result = validate_request( + spec, request, validator=openapi_request_body_validator + ) - assert body is None + assert result.body is None def test_get_pets_none_value(self, spec): host_url = "http://petstore.swagger.io/v1" @@ -468,9 +534,11 @@ def test_get_pets_none_value(self, spec): ) with pytest.warns(DeprecationWarning): - parameters = spec_validate_parameters(spec, request) + result = validate_request( + spec, request, validator=openapi_request_parameters_validator + ) - assert parameters == Parameters( + assert result.parameters == Parameters( query={ "limit": None, "page": 1, @@ -478,9 +546,11 @@ def test_get_pets_none_value(self, spec): } ) - body = spec_validate_body(spec, request) + result = validate_request( + spec, request, validator=openapi_request_body_validator + ) - assert body is None + assert result.body is None def test_get_pets_param_order(self, spec): host_url = "http://petstore.swagger.io/v1" @@ -499,9 +569,11 @@ def test_get_pets_param_order(self, spec): ) with pytest.warns(DeprecationWarning): - parameters = spec_validate_parameters(spec, request) + result = validate_request( + spec, request, validator=openapi_request_parameters_validator + ) - assert parameters == Parameters( + assert result.parameters == Parameters( query={ "limit": None, "order": "desc", @@ -510,9 +582,11 @@ def test_get_pets_param_order(self, spec): } ) - body = spec_validate_body(spec, request) + result = validate_request( + spec, request, validator=openapi_request_body_validator + ) - assert body is None + assert result.body is None def test_get_pets_param_coordinates(self, spec): host_url = "http://petstore.swagger.io/v1" @@ -535,9 +609,11 @@ def test_get_pets_param_coordinates(self, spec): ) with pytest.warns(DeprecationWarning): - parameters = spec_validate_parameters(spec, request) + result = validate_request( + spec, request, validator=openapi_request_parameters_validator + ) - assert parameters == Parameters( + assert result.parameters == Parameters( query={ "limit": None, "page": 1, @@ -546,9 +622,11 @@ def test_get_pets_param_coordinates(self, spec): } ) - body = spec_validate_body(spec, request) + result = validate_request( + spec, request, validator=openapi_request_body_validator + ) - assert body is None + assert result.body is None def test_post_birds(self, spec, spec_dict): host_url = "https://staging.gigantic-server.com/v1" @@ -594,9 +672,11 @@ def test_post_birds(self, spec, spec_dict): cookies=cookies, ) - parameters = spec_validate_parameters(spec, request) + result = validate_request( + spec, request, validator=openapi_request_parameters_validator + ) - assert parameters == Parameters( + assert result.parameters == Parameters( header={ "api-key": self.api_key, }, @@ -608,23 +688,27 @@ def test_post_birds(self, spec, spec_dict): }, ) - body = spec_validate_body(spec, request) + result = validate_request( + spec, request, validator=openapi_request_body_validator + ) schemas = spec_dict["components"]["schemas"] pet_model = schemas["PetCreate"]["x-model"] address_model = schemas["Address"]["x-model"] - assert body.__class__.__name__ == pet_model - assert body.name == pet_name - assert body.tag == pet_tag - assert body.position == 2 - assert body.address.__class__.__name__ == address_model - assert body.address.street == pet_street - assert body.address.city == pet_city - assert body.healthy == pet_healthy - - security = spec_validate_security(spec, request) + assert result.body.__class__.__name__ == pet_model + assert result.body.name == pet_name + assert result.body.tag == pet_tag + assert result.body.position == 2 + assert result.body.address.__class__.__name__ == address_model + assert result.body.address.street == pet_street + assert result.body.address.city == pet_city + assert result.body.healthy == pet_healthy + + result = validate_request( + spec, request, validator=openapi_request_security_validator + ) - assert security == {} + assert result.security == {} def test_post_cats(self, spec, spec_dict): host_url = "https://staging.gigantic-server.com/v1" @@ -665,9 +749,11 @@ def test_post_cats(self, spec, spec_dict): cookies=cookies, ) - parameters = spec_validate_parameters(spec, request) + result = validate_request( + spec, request, validator=openapi_request_parameters_validator + ) - assert parameters == Parameters( + assert result.parameters == Parameters( header={ "api-key": self.api_key, }, @@ -676,19 +762,21 @@ def test_post_cats(self, spec, spec_dict): }, ) - body = spec_validate_body(spec, request) + result = validate_request( + spec, request, validator=openapi_request_body_validator + ) schemas = spec_dict["components"]["schemas"] pet_model = schemas["PetCreate"]["x-model"] address_model = schemas["Address"]["x-model"] - assert body.__class__.__name__ == pet_model - assert body.name == pet_name - assert body.tag == pet_tag - assert body.position == 2 - assert body.address.__class__.__name__ == address_model - assert body.address.street == pet_street - assert body.address.city == pet_city - assert body.healthy == pet_healthy + assert result.body.__class__.__name__ == pet_model + assert result.body.name == pet_name + assert result.body.tag == pet_tag + assert result.body.position == 2 + assert result.body.address.__class__.__name__ == address_model + assert result.body.address.street == pet_street + assert result.body.address.city == pet_city + assert result.body.healthy == pet_healthy def test_post_cats_boolean_string(self, spec, spec_dict): host_url = "https://staging.gigantic-server.com/v1" @@ -729,9 +817,11 @@ def test_post_cats_boolean_string(self, spec, spec_dict): cookies=cookies, ) - parameters = spec_validate_parameters(spec, request) + result = validate_request( + spec, request, validator=openapi_request_parameters_validator + ) - assert parameters == Parameters( + assert result.parameters == Parameters( header={ "api-key": self.api_key, }, @@ -740,19 +830,21 @@ def test_post_cats_boolean_string(self, spec, spec_dict): }, ) - body = spec_validate_body(spec, request) + result = validate_request( + spec, request, validator=openapi_request_body_validator + ) schemas = spec_dict["components"]["schemas"] pet_model = schemas["PetCreate"]["x-model"] address_model = schemas["Address"]["x-model"] - assert body.__class__.__name__ == pet_model - assert body.name == pet_name - assert body.tag == pet_tag - assert body.position == 2 - assert body.address.__class__.__name__ == address_model - assert body.address.street == pet_street - assert body.address.city == pet_city - assert body.healthy is False + assert result.body.__class__.__name__ == pet_model + assert result.body.name == pet_name + assert result.body.tag == pet_tag + assert result.body.position == 2 + assert result.body.address.__class__.__name__ == address_model + assert result.body.address.street == pet_street + assert result.body.address.city == pet_city + assert result.body.healthy is False def test_post_no_one_of_schema(self, spec, spec_dict): host_url = "https://staging.gigantic-server.com/v1" @@ -781,9 +873,11 @@ def test_post_no_one_of_schema(self, spec, spec_dict): cookies=cookies, ) - parameters = spec_validate_parameters(spec, request) + result = validate_request( + spec, request, validator=openapi_request_parameters_validator + ) - assert parameters == Parameters( + assert result.parameters == Parameters( header={ "api-key": self.api_key, }, @@ -793,7 +887,9 @@ def test_post_no_one_of_schema(self, spec, spec_dict): ) with pytest.raises(InvalidSchemaValue): - spec_validate_body(spec, request) + validate_request( + spec, request, validator=openapi_request_body_validator + ) def test_post_cats_only_required_body(self, spec, spec_dict): host_url = "https://staging.gigantic-server.com/v1" @@ -824,9 +920,11 @@ def test_post_cats_only_required_body(self, spec, spec_dict): cookies=cookies, ) - parameters = spec_validate_parameters(spec, request) + result = validate_request( + spec, request, validator=openapi_request_parameters_validator + ) - assert parameters == Parameters( + assert result.parameters == Parameters( header={ "api-key": self.api_key, }, @@ -835,14 +933,16 @@ def test_post_cats_only_required_body(self, spec, spec_dict): }, ) - body = spec_validate_body(spec, request) + result = validate_request( + spec, request, validator=openapi_request_body_validator + ) schemas = spec_dict["components"]["schemas"] pet_model = schemas["PetCreate"]["x-model"] - assert body.__class__.__name__ == pet_model - assert body.name == pet_name - assert not hasattr(body, "tag") - assert not hasattr(body, "address") + assert result.body.__class__.__name__ == pet_model + assert result.body.name == pet_name + assert not hasattr(result.body, "tag") + assert not hasattr(result.body, "address") def test_post_pets_raises_invalid_mimetype(self, spec): host_url = "https://staging.gigantic-server.com/v1" @@ -870,9 +970,11 @@ def test_post_pets_raises_invalid_mimetype(self, spec): cookies=cookies, ) - parameters = spec_validate_parameters(spec, request) + result = validate_request( + spec, request, validator=openapi_request_parameters_validator + ) - assert parameters == Parameters( + assert result.parameters == Parameters( header={ "api-key": self.api_key, }, @@ -882,7 +984,9 @@ def test_post_pets_raises_invalid_mimetype(self, spec): ) with pytest.raises(MediaTypeNotFound): - spec_validate_body(spec, request) + validate_request( + spec, request, validator=openapi_request_body_validator + ) def test_post_pets_missing_cookie(self, spec, spec_dict): host_url = "https://staging.gigantic-server.com/v1" @@ -910,16 +1014,20 @@ def test_post_pets_missing_cookie(self, spec, spec_dict): ) with pytest.raises(MissingRequiredParameter): - spec_validate_parameters(spec, request) + validate_request( + spec, request, validator=openapi_request_parameters_validator + ) - body = spec_validate_body(spec, request) + result = validate_request( + spec, request, validator=openapi_request_body_validator + ) schemas = spec_dict["components"]["schemas"] pet_model = schemas["PetCreate"]["x-model"] - assert body.__class__.__name__ == pet_model - assert body.name == pet_name - assert not hasattr(body, "tag") - assert not hasattr(body, "address") + assert result.body.__class__.__name__ == pet_model + assert result.body.name == pet_name + assert not hasattr(result.body, "tag") + assert not hasattr(result.body, "address") def test_post_pets_missing_header(self, spec, spec_dict): host_url = "https://staging.gigantic-server.com/v1" @@ -947,16 +1055,20 @@ def test_post_pets_missing_header(self, spec, spec_dict): ) with pytest.raises(MissingRequiredParameter): - spec_validate_parameters(spec, request) + validate_request( + spec, request, validator=openapi_request_parameters_validator + ) - body = spec_validate_body(spec, request) + result = validate_request( + spec, request, validator=openapi_request_body_validator + ) schemas = spec_dict["components"]["schemas"] pet_model = schemas["PetCreate"]["x-model"] - assert body.__class__.__name__ == pet_model - assert body.name == pet_name - assert not hasattr(body, "tag") - assert not hasattr(body, "address") + assert result.body.__class__.__name__ == pet_model + assert result.body.name == pet_name + assert not hasattr(result.body, "tag") + assert not hasattr(result.body, "address") def test_post_pets_raises_invalid_server_error(self, spec): host_url = "http://flowerstore.swagger.io/v1" @@ -985,10 +1097,14 @@ def test_post_pets_raises_invalid_server_error(self, spec): ) with pytest.raises(ServerNotFound): - spec_validate_parameters(spec, request) + validate_request( + spec, request, validator=openapi_request_parameters_validator + ) with pytest.raises(ServerNotFound): - spec_validate_body(spec, request) + validate_request( + spec, request, validator=openapi_request_body_validator + ) data_id = 1 data_name = "test" @@ -1005,9 +1121,14 @@ def test_post_pets_raises_invalid_server_error(self, spec): response = MockResponse(data) with pytest.raises(ServerNotFound): - spec_validate_data(spec, request, response) - - def test_get_pet(self, spec, response_validator): + validate_response( + spec, + request, + response, + validator=openapi_response_data_validator, + ) + + def test_get_pet(self, spec): host_url = "http://petstore.swagger.io/v1" path_pattern = "/v1/pets/{petId}" view_args = { @@ -1026,21 +1147,27 @@ def test_get_pet(self, spec, response_validator): headers=headers, ) - parameters = spec_validate_parameters(spec, request) + result = validate_request( + spec, request, validator=openapi_request_parameters_validator + ) - assert parameters == Parameters( + assert result.parameters == Parameters( path={ "petId": 1, } ) - body = spec_validate_body(spec, request) + result = validate_request( + spec, request, validator=openapi_request_body_validator + ) - assert body is None + assert result.body is None - security = spec_validate_security(spec, request) + result = validate_request( + spec, request, validator=openapi_request_security_validator + ) - assert security == { + assert result.security == { "petstore_auth": auth, } @@ -1058,7 +1185,7 @@ def test_get_pet(self, spec, response_validator): data = json.dumps(data_json) response = MockResponse(data) - response_result = response_validator.validate(request, response) + response_result = validate_response(spec, request, response) assert response_result.errors == [] assert isinstance(response_result.data, BaseModel) @@ -1066,7 +1193,7 @@ def test_get_pet(self, spec, response_validator): assert response_result.data.data.id == data_id assert response_result.data.data.name == data_name - def test_get_pet_not_found(self, spec, response_validator): + def test_get_pet_not_found(self, spec): host_url = "http://petstore.swagger.io/v1" path_pattern = "/v1/pets/{petId}" view_args = { @@ -1080,17 +1207,21 @@ def test_get_pet_not_found(self, spec, response_validator): view_args=view_args, ) - parameters = spec_validate_parameters(spec, request) + result = validate_request( + spec, request, validator=openapi_request_parameters_validator + ) - assert parameters == Parameters( + assert result.parameters == Parameters( path={ "petId": 1, } ) - body = spec_validate_body(spec, request) + result = validate_request( + spec, request, validator=openapi_request_body_validator + ) - assert body is None + assert result.body is None code = 404 message = "Not found" @@ -1103,7 +1234,7 @@ def test_get_pet_not_found(self, spec, response_validator): data = json.dumps(data_json) response = MockResponse(data, status_code=404) - response_result = response_validator.validate(request, response) + response_result = validate_response(spec, request, response) assert response_result.errors == [] assert isinstance(response_result.data, BaseModel) @@ -1111,7 +1242,7 @@ def test_get_pet_not_found(self, spec, response_validator): assert response_result.data.message == message assert response_result.data.rootCause == rootCause - def test_get_pet_wildcard(self, spec, response_validator): + def test_get_pet_wildcard(self, spec): host_url = "http://petstore.swagger.io/v1" path_pattern = "/v1/pets/{petId}" view_args = { @@ -1125,28 +1256,32 @@ def test_get_pet_wildcard(self, spec, response_validator): view_args=view_args, ) - parameters = spec_validate_parameters(spec, request) + result = validate_request( + spec, request, validator=openapi_request_parameters_validator + ) - assert parameters == Parameters( + assert result.parameters == Parameters( path={ "petId": 1, } ) - body = spec_validate_body(spec, request) + result = validate_request( + spec, request, validator=openapi_request_body_validator + ) - assert body is None + assert result.body is None data = b"imagedata" response = MockResponse(data, mimetype="image/png") with pytest.warns(UserWarning): - response_result = response_validator.validate(request, response) + response_result = validate_response(spec, request, response) assert response_result.errors == [] assert response_result.data == data - def test_get_tags(self, spec, response_validator): + def test_get_tags(self, spec): host_url = "http://petstore.swagger.io/v1" path_pattern = "/v1/tags" @@ -1157,17 +1292,23 @@ def test_get_tags(self, spec, response_validator): path_pattern=path_pattern, ) - parameters = spec_validate_parameters(spec, request) - body = spec_validate_body(spec, request) + result = validate_request( + spec, request, validator=openapi_request_parameters_validator + ) + + assert result.parameters == Parameters() - assert parameters == Parameters() - assert body is None + result = validate_request( + spec, request, validator=openapi_request_body_validator + ) + + assert result.body is None data_json = ["cats", "birds"] data = json.dumps(data_json) response = MockResponse(data) - response_result = response_validator.validate(request, response) + response_result = validate_response(spec, request, response) assert response_result.errors == [] assert response_result.data == data_json @@ -1191,12 +1332,16 @@ def test_post_tags_extra_body_properties(self, spec, spec_dict): data=data, ) - parameters = spec_validate_parameters(spec, request) + result = validate_request( + spec, request, validator=openapi_request_parameters_validator + ) - assert parameters == Parameters() + assert result.parameters == Parameters() with pytest.raises(InvalidSchemaValue): - spec_validate_body(spec, request) + validate_request( + spec, request, validator=openapi_request_body_validator + ) def test_post_tags_empty_body(self, spec, spec_dict): host_url = "http://petstore.swagger.io/v1" @@ -1212,12 +1357,16 @@ def test_post_tags_empty_body(self, spec, spec_dict): data=data, ) - parameters = spec_validate_parameters(spec, request) + result = validate_request( + spec, request, validator=openapi_request_parameters_validator + ) - assert parameters == Parameters() + assert result.parameters == Parameters() with pytest.raises(InvalidSchemaValue): - spec_validate_body(spec, request) + validate_request( + spec, request, validator=openapi_request_body_validator + ) def test_post_tags_wrong_property_type(self, spec): host_url = "http://petstore.swagger.io/v1" @@ -1233,14 +1382,18 @@ def test_post_tags_wrong_property_type(self, spec): data=data, ) - parameters = spec_validate_parameters(spec, request) + result = validate_request( + spec, request, validator=openapi_request_parameters_validator + ) - assert parameters == Parameters() + assert result.parameters == Parameters() with pytest.raises(InvalidSchemaValue): - spec_validate_body(spec, request) + validate_request( + spec, request, validator=openapi_request_body_validator + ) - def test_post_tags_additional_properties(self, spec, response_validator): + def test_post_tags_additional_properties(self, spec): host_url = "http://petstore.swagger.io/v1" path_pattern = "/v1/tags" pet_name = "Dog" @@ -1257,12 +1410,18 @@ def test_post_tags_additional_properties(self, spec, response_validator): data=data, ) - parameters = spec_validate_parameters(spec, request) - body = spec_validate_body(spec, request) + result = validate_request( + spec, request, validator=openapi_request_parameters_validator + ) + + assert result.parameters == Parameters() + + result = validate_request( + spec, request, validator=openapi_request_body_validator + ) - assert parameters == Parameters() - assert isinstance(body, BaseModel) - assert body.name == pet_name + assert isinstance(result.body, BaseModel) + assert result.body.name == pet_name code = 400 message = "Bad request" @@ -1277,7 +1436,7 @@ def test_post_tags_additional_properties(self, spec, response_validator): data = json.dumps(data_json) response = MockResponse(data, status_code=404) - response_result = response_validator.validate(request, response) + response_result = validate_response(spec, request, response) assert response_result.errors == [] assert isinstance(response_result.data, BaseModel) @@ -1286,7 +1445,7 @@ def test_post_tags_additional_properties(self, spec, response_validator): assert response_result.data.rootCause == rootCause assert response_result.data.additionalinfo == additionalinfo - def test_post_tags_created_now(self, spec, response_validator): + def test_post_tags_created_now(self, spec): host_url = "http://petstore.swagger.io/v1" path_pattern = "/v1/tags" created = "now" @@ -1305,13 +1464,19 @@ def test_post_tags_created_now(self, spec, response_validator): data=data, ) - parameters = spec_validate_parameters(spec, request) - body = spec_validate_body(spec, request) + result = validate_request( + spec, request, validator=openapi_request_parameters_validator + ) + + assert result.parameters == Parameters() + + result = validate_request( + spec, request, validator=openapi_request_body_validator + ) - assert parameters == Parameters() - assert isinstance(body, BaseModel) - assert body.created == created - assert body.name == pet_name + assert isinstance(result.body, BaseModel) + assert result.body.created == created + assert result.body.name == pet_name code = 400 message = "Bad request" @@ -1326,7 +1491,7 @@ def test_post_tags_created_now(self, spec, response_validator): data = json.dumps(data_json) response = MockResponse(data, status_code=404) - response_result = response_validator.validate(request, response) + response_result = validate_response(spec, request, response) assert response_result.errors == [] assert isinstance(response_result.data, BaseModel) @@ -1335,7 +1500,7 @@ def test_post_tags_created_now(self, spec, response_validator): assert response_result.data.rootCause == rootCause assert response_result.data.additionalinfo == additionalinfo - def test_post_tags_created_datetime(self, spec, response_validator): + def test_post_tags_created_datetime(self, spec): host_url = "http://petstore.swagger.io/v1" path_pattern = "/v1/tags" created = "2016-04-16T16:06:05Z" @@ -1354,13 +1519,21 @@ def test_post_tags_created_datetime(self, spec, response_validator): data=data, ) - parameters = spec_validate_parameters(spec, request) - body = spec_validate_body(spec, request) + result = validate_request( + spec, request, validator=openapi_request_parameters_validator + ) + + assert result.parameters == Parameters() - assert parameters == Parameters() - assert isinstance(body, BaseModel) - assert body.created == datetime(2016, 4, 16, 16, 6, 5, tzinfo=UTC) - assert body.name == pet_name + result = validate_request( + spec, request, validator=openapi_request_body_validator + ) + + assert isinstance(result.body, BaseModel) + assert result.body.created == datetime( + 2016, 4, 16, 16, 6, 5, tzinfo=UTC + ) + assert result.body.name == pet_name code = 400 message = "Bad request" @@ -1375,15 +1548,17 @@ def test_post_tags_created_datetime(self, spec, response_validator): response_data = json.dumps(response_data_json) response = MockResponse(response_data, status_code=404) - data = spec_validate_data(spec, request, response) + result = validate_response( + spec, request, response, validator=openapi_response_data_validator + ) - assert isinstance(data, BaseModel) - assert data.code == code - assert data.message == message - assert data.rootCause == rootCause - assert data.additionalinfo == additionalinfo + assert isinstance(result.data, BaseModel) + assert result.data.code == code + assert result.data.message == message + assert result.data.rootCause == rootCause + assert result.data.additionalinfo == additionalinfo - response_result = response_validator.validate(request, response) + response_result = validate_response(spec, request, response) assert response_result.errors == [] assert isinstance(response_result.data, BaseModel) @@ -1392,7 +1567,7 @@ def test_post_tags_created_datetime(self, spec, response_validator): assert response_result.data.rootCause == rootCause assert response_result.data.additionalinfo == additionalinfo - def test_post_tags_created_invalid_type(self, spec, response_validator): + def test_post_tags_created_invalid_type(self, spec): host_url = "http://petstore.swagger.io/v1" path_pattern = "/v1/tags" created = "long time ago" @@ -1411,11 +1586,16 @@ def test_post_tags_created_invalid_type(self, spec, response_validator): data=data, ) - parameters = spec_validate_parameters(spec, request) - with pytest.raises(InvalidSchemaValue): - spec_validate_body(spec, request) + result = validate_request( + spec, request, validator=openapi_request_parameters_validator + ) - assert parameters == Parameters() + assert result.parameters == Parameters() + + with pytest.raises(InvalidSchemaValue): + validate_request( + spec, request, validator=openapi_request_body_validator + ) code = 400 message = "Bad request" @@ -1431,7 +1611,7 @@ def test_post_tags_created_invalid_type(self, spec, response_validator): data = json.dumps(data_json) response = MockResponse(data, status_code=404) - response_result = response_validator.validate(request, response) + response_result = validate_response(spec, request, response) assert response_result.errors == [] assert isinstance(response_result.data, BaseModel) @@ -1441,7 +1621,7 @@ def test_post_tags_created_invalid_type(self, spec, response_validator): assert response_result.data.rootCause == rootCause assert response_result.data.additionalinfo == additionalinfo - def test_delete_tags_with_requestbody(self, spec, response_validator): + def test_delete_tags_with_requestbody(self, spec): host_url = "http://petstore.swagger.io/v1" path_pattern = "/v1/tags" ids = [1, 2, 3] @@ -1457,12 +1637,18 @@ def test_delete_tags_with_requestbody(self, spec, response_validator): data=data, ) - parameters = spec_validate_parameters(spec, request) - body = spec_validate_body(spec, request) + result = validate_request( + spec, request, validator=openapi_request_parameters_validator + ) + + assert result.parameters == Parameters() + + result = validate_request( + spec, request, validator=openapi_request_body_validator + ) - assert parameters == Parameters() - assert isinstance(body, BaseModel) - assert body.ids == ids + assert isinstance(result.body, BaseModel) + assert result.body.ids == ids data = None headers = { @@ -1471,18 +1657,23 @@ def test_delete_tags_with_requestbody(self, spec, response_validator): response = MockResponse(data, status_code=200, headers=headers) with pytest.warns(DeprecationWarning): - response_result = response_validator.validate(request, response) + response_result = validate_response(spec, request, response) assert response_result.errors == [] assert response_result.data is None with pytest.warns(DeprecationWarning): - response_headers = spec_validate_headers(spec, request, response) - - assert response_headers == { + result = validate_response( + spec, + request, + response, + validator=openapi_response_headers_validator, + ) + + assert result.headers == { "x-delete-confirm": True, } - def test_delete_tags_no_requestbody(self, spec, response_validator): + def test_delete_tags_no_requestbody(self, spec): host_url = "http://petstore.swagger.io/v1" path_pattern = "/v1/tags" request = MockRequest( @@ -1492,15 +1683,19 @@ def test_delete_tags_no_requestbody(self, spec, response_validator): path_pattern=path_pattern, ) - parameters = spec_validate_parameters(spec, request) - body = spec_validate_body(spec, request) + result = validate_request( + spec, request, validator=openapi_request_parameters_validator + ) + + assert result.parameters == Parameters() - assert parameters == Parameters() - assert body is None + result = validate_request( + spec, request, validator=openapi_request_body_validator + ) + + assert result.body is None - def test_delete_tags_raises_missing_required_response_header( - self, spec, response_validator - ): + def test_delete_tags_raises_missing_required_response_header(self, spec): host_url = "http://petstore.swagger.io/v1" path_pattern = "/v1/tags" request = MockRequest( @@ -1510,17 +1705,25 @@ def test_delete_tags_raises_missing_required_response_header( path_pattern=path_pattern, ) - parameters = spec_validate_parameters(spec, request) - body = spec_validate_body(spec, request) + result = validate_request( + spec, request, validator=openapi_request_parameters_validator + ) + + assert result.parameters == Parameters() + + result = validate_request( + spec, request, validator=openapi_request_body_validator + ) - assert parameters == Parameters() - assert body is None + assert result.body is None data = None response = MockResponse(data, status_code=200) with pytest.warns(DeprecationWarning): - response_result = response_validator.validate(request, response) + response_result = openapi_response_validator.validate( + spec, request, response + ) assert response_result.errors == [ MissingRequiredHeader(name="x-delete-confirm"), diff --git a/tests/integration/validation/test_read_only_write_only.py b/tests/integration/validation/test_read_only_write_only.py index 9b2e7c7a..0509f793 100644 --- a/tests/integration/validation/test_read_only_write_only.py +++ b/tests/integration/validation/test_read_only_write_only.py @@ -6,18 +6,8 @@ from openapi_core.testing import MockRequest from openapi_core.testing import MockResponse from openapi_core.unmarshalling.schemas.exceptions import InvalidSchemaValue -from openapi_core.validation.request.validators import RequestValidator -from openapi_core.validation.response.validators import ResponseValidator - - -@pytest.fixture -def response_validator(spec): - return ResponseValidator(spec) - - -@pytest.fixture -def request_validator(spec): - return RequestValidator(spec) +from openapi_core.validation.request import openapi_request_validator +from openapi_core.validation.response import openapi_response_validator @pytest.fixture(scope="class") @@ -27,7 +17,7 @@ def spec(factory): class TestReadOnly: - def test_write_a_read_only_property(self, request_validator): + def test_write_a_read_only_property(self, spec): data = json.dumps( { "id": 10, @@ -39,12 +29,12 @@ def test_write_a_read_only_property(self, request_validator): host_url="", method="POST", path="/users", data=data ) - result = request_validator.validate(request) + result = openapi_request_validator.validate(spec, request) assert type(result.errors[0]) == InvalidSchemaValue assert result.body is None - def test_read_only_property_response(self, response_validator): + def test_read_only_property_response(self, spec): data = json.dumps( { "id": 10, @@ -56,7 +46,7 @@ def test_read_only_property_response(self, response_validator): response = MockResponse(data) - result = response_validator.validate(request, response) + result = openapi_response_validator.validate(spec, request, response) assert not result.errors assert result.data == { @@ -66,7 +56,7 @@ def test_read_only_property_response(self, response_validator): class TestWriteOnly: - def test_write_only_property(self, request_validator): + def test_write_only_property(self, spec): data = json.dumps( { "name": "Pedro", @@ -78,7 +68,7 @@ def test_write_only_property(self, request_validator): host_url="", method="POST", path="/users", data=data ) - result = request_validator.validate(request) + result = openapi_request_validator.validate(spec, request) assert not result.errors assert result.body == { @@ -86,7 +76,7 @@ def test_write_only_property(self, request_validator): "hidden": False, } - def test_read_a_write_only_property(self, response_validator): + def test_read_a_write_only_property(self, spec): data = json.dumps( { "id": 10, @@ -98,7 +88,7 @@ def test_read_a_write_only_property(self, response_validator): request = MockRequest(host_url="", method="POST", path="/users") response = MockResponse(data) - result = response_validator.validate(request, response) + result = openapi_response_validator.validate(spec, request, response) assert type(result.errors[0]) == InvalidSchemaValue assert result.data is None diff --git a/tests/integration/validation/test_security_override.py b/tests/integration/validation/test_security_override.py index aef5b629..c5f5f6e2 100644 --- a/tests/integration/validation/test_security_override.py +++ b/tests/integration/validation/test_security_override.py @@ -5,12 +5,7 @@ from openapi_core.spec import OpenAPIv30Spec as Spec from openapi_core.testing import MockRequest from openapi_core.validation.exceptions import InvalidSecurity -from openapi_core.validation.request.validators import RequestValidator - - -@pytest.fixture -def request_validator(spec): - return RequestValidator(spec) +from openapi_core.validation.request import openapi_request_validator @pytest.fixture(scope="class") @@ -31,26 +26,26 @@ def api_key_encoded(self): api_key_bytes_enc = b64encode(api_key_bytes) return str(api_key_bytes_enc, "utf8") - def test_default(self, request_validator): + def test_default(self, spec): args = {"api_key": self.api_key} request = MockRequest(self.host_url, "get", "/resource/one", args=args) - result = request_validator.validate(request) + result = openapi_request_validator.validate(spec, request) assert not result.errors assert result.security == { "api_key": self.api_key, } - def test_default_invalid(self, request_validator): + def test_default_invalid(self, spec): request = MockRequest(self.host_url, "get", "/resource/one") - result = request_validator.validate(request) + result = openapi_request_validator.validate(spec, request) assert type(result.errors[0]) == InvalidSecurity assert result.security is None - def test_override(self, request_validator): + def test_override(self, spec): authorization = "Basic " + self.api_key_encoded headers = { "Authorization": authorization, @@ -59,25 +54,25 @@ def test_override(self, request_validator): self.host_url, "post", "/resource/one", headers=headers ) - result = request_validator.validate(request) + result = openapi_request_validator.validate(spec, request) assert not result.errors assert result.security == { "petstore_auth": self.api_key_encoded, } - def test_override_invalid(self, request_validator): + def test_override_invalid(self, spec): request = MockRequest(self.host_url, "post", "/resource/one") - result = request_validator.validate(request) + result = openapi_request_validator.validate(spec, request) assert type(result.errors[0]) == InvalidSecurity assert result.security is None - def test_remove(self, request_validator): + def test_remove(self, spec): request = MockRequest(self.host_url, "put", "/resource/one") - result = request_validator.validate(request) + result = openapi_request_validator.validate(spec, request) assert not result.errors assert result.security == {} diff --git a/tests/integration/validation/test_validators.py b/tests/integration/validation/test_validators.py index f3c52339..05e42d00 100644 --- a/tests/integration/validation/test_validators.py +++ b/tests/integration/validation/test_validators.py @@ -7,9 +7,6 @@ from openapi_core.deserializing.media_types.exceptions import ( MediaTypeDeserializeError, ) -from openapi_core.exceptions import MissingRequiredParameter -from openapi_core.exceptions import MissingRequiredRequestBody -from openapi_core.exceptions import MissingResponseContent from openapi_core.extensions.models.models import BaseModel from openapi_core.spec import OpenAPIv30Spec as Spec from openapi_core.templating.media_types.exceptions import MediaTypeNotFound @@ -19,10 +16,15 @@ from openapi_core.testing import MockRequest from openapi_core.testing import MockResponse from openapi_core.unmarshalling.schemas.exceptions import InvalidSchemaValue +from openapi_core.validation import openapi_request_validator +from openapi_core.validation import openapi_response_validator from openapi_core.validation.exceptions import InvalidSecurity +from openapi_core.validation.exceptions import MissingRequiredParameter from openapi_core.validation.request.datatypes import Parameters -from openapi_core.validation.request.validators import RequestValidator -from openapi_core.validation.response.validators import ResponseValidator +from openapi_core.validation.request.exceptions import ( + MissingRequiredRequestBody, +) +from openapi_core.validation.response.exceptions import MissingResponseContent class TestRequestValidator: @@ -45,45 +47,41 @@ def spec_dict(self, factory): def spec(self, spec_dict): return Spec.create(spec_dict) - @pytest.fixture(scope="session") - def validator(self, spec): - return RequestValidator(spec, base_url=self.host_url) - - def test_request_server_error(self, validator): + def test_request_server_error(self, spec): request = MockRequest("http://petstore.invalid.net/v1", "get", "/") - result = validator.validate(request) + result = openapi_request_validator.validate(spec, request) assert len(result.errors) == 1 assert type(result.errors[0]) == PathNotFound assert result.body is None assert result.parameters == Parameters() - def test_invalid_path(self, validator): + def test_invalid_path(self, spec): request = MockRequest(self.host_url, "get", "/v1") - result = validator.validate(request) + result = openapi_request_validator.validate(spec, request) assert len(result.errors) == 1 assert type(result.errors[0]) == PathNotFound assert result.body is None assert result.parameters == Parameters() - def test_invalid_operation(self, validator): + def test_invalid_operation(self, spec): request = MockRequest(self.host_url, "patch", "/v1/pets") - result = validator.validate(request) + result = openapi_request_validator.validate(spec, request) assert len(result.errors) == 1 assert type(result.errors[0]) == OperationNotFound assert result.body is None assert result.parameters == Parameters() - def test_missing_parameter(self, validator): + def test_missing_parameter(self, spec): request = MockRequest(self.host_url, "get", "/v1/pets") with pytest.warns(DeprecationWarning): - result = validator.validate(request) + result = openapi_request_validator.validate(spec, request) assert type(result.errors[0]) == MissingRequiredParameter assert result.body is None @@ -94,7 +92,7 @@ def test_missing_parameter(self, validator): }, ) - def test_get_pets(self, validator): + def test_get_pets(self, spec): args = {"limit": "10", "ids": ["1", "2"], "api_key": self.api_key} request = MockRequest( self.host_url, @@ -105,7 +103,7 @@ def test_get_pets(self, validator): ) with pytest.warns(DeprecationWarning): - result = validator.validate(request) + result = openapi_request_validator.validate(spec, request) assert result.errors == [] assert result.body is None @@ -121,7 +119,7 @@ def test_get_pets(self, validator): "api_key": self.api_key, } - def test_get_pets_webob(self, validator): + def test_get_pets_webob(self, spec): from webob.multidict import GetDict request = MockRequest( @@ -135,7 +133,7 @@ def test_get_pets_webob(self, validator): ) with pytest.warns(DeprecationWarning): - result = validator.validate(request) + result = openapi_request_validator.validate(spec, request) assert result.errors == [] assert result.body is None @@ -148,7 +146,7 @@ def test_get_pets_webob(self, validator): }, ) - def test_missing_body(self, validator): + def test_missing_body(self, spec): headers = { "api-key": self.api_key_encoded, } @@ -164,7 +162,7 @@ def test_missing_body(self, validator): cookies=cookies, ) - result = validator.validate(request) + result = openapi_request_validator.validate(spec, request) assert len(result.errors) == 1 assert type(result.errors[0]) == MissingRequiredRequestBody @@ -178,7 +176,7 @@ def test_missing_body(self, validator): }, ) - def test_invalid_content_type(self, validator): + def test_invalid_content_type(self, spec): data = "csv,data" headers = { "api-key": self.api_key_encoded, @@ -197,7 +195,7 @@ def test_invalid_content_type(self, validator): cookies=cookies, ) - result = validator.validate(request) + result = openapi_request_validator.validate(spec, request) assert len(result.errors) == 1 assert type(result.errors[0]) == MediaTypeNotFound @@ -211,7 +209,7 @@ def test_invalid_content_type(self, validator): }, ) - def test_invalid_complex_parameter(self, validator, spec_dict): + def test_invalid_complex_parameter(self, spec, spec_dict): pet_name = "Cat" pet_tag = "cats" pet_street = "Piekna" @@ -250,7 +248,7 @@ def test_invalid_complex_parameter(self, validator, spec_dict): cookies=cookies, ) - result = validator.validate(request) + result = openapi_request_validator.validate(spec, request) assert len(result.errors) == 1 assert type(result.errors[0]) == InvalidSchemaValue @@ -275,7 +273,7 @@ def test_invalid_complex_parameter(self, validator, spec_dict): assert result.body.address.street == pet_street assert result.body.address.city == pet_city - def test_post_pets(self, validator, spec_dict): + def test_post_pets(self, spec, spec_dict): pet_name = "Cat" pet_tag = "cats" pet_street = "Piekna" @@ -309,7 +307,7 @@ def test_post_pets(self, validator, spec_dict): cookies=cookies, ) - result = validator.validate(request) + result = openapi_request_validator.validate(spec, request) assert result.errors == [] assert result.parameters == Parameters( @@ -333,7 +331,7 @@ def test_post_pets(self, validator, spec_dict): assert result.body.address.street == pet_street assert result.body.address.city == pet_city - def test_post_pets_plain_no_schema(self, validator, spec_dict): + def test_post_pets_plain_no_schema(self, spec, spec_dict): data = "plain text" headers = { "api-key": self.api_key_encoded, @@ -353,7 +351,7 @@ def test_post_pets_plain_no_schema(self, validator, spec_dict): ) with pytest.warns(UserWarning): - result = validator.validate(request) + result = openapi_request_validator.validate(spec, request) assert result.errors == [] assert result.parameters == Parameters( @@ -367,7 +365,7 @@ def test_post_pets_plain_no_schema(self, validator, spec_dict): assert result.security == {} assert result.body == data - def test_get_pet_unauthorized(self, validator): + def test_get_pet_unauthorized(self, spec): request = MockRequest( self.host_url, "get", @@ -376,7 +374,7 @@ def test_get_pet_unauthorized(self, validator): view_args={"petId": "1"}, ) - result = validator.validate(request) + result = openapi_request_validator.validate(spec, request) assert result.errors == [ InvalidSecurity(), @@ -385,7 +383,7 @@ def test_get_pet_unauthorized(self, validator): assert result.parameters == Parameters() assert result.security is None - def test_get_pet(self, validator): + def test_get_pet(self, spec): authorization = "Basic " + self.api_key_encoded headers = { "Authorization": authorization, @@ -399,7 +397,7 @@ def test_get_pet(self, validator): headers=headers, ) - result = validator.validate(request) + result = openapi_request_validator.validate(spec, request) assert result.errors == [] assert result.body is None @@ -447,47 +445,43 @@ def spec_dict(self): def spec(self, spec_dict): return Spec.create(spec_dict) - @pytest.fixture(scope="session") - def validator(self, spec): - return RequestValidator(spec, base_url="http://example.com") - - def test_request_missing_param(self, validator): + def test_request_missing_param(self, spec): request = MockRequest("http://example.com", "get", "/resource") - result = validator.validate(request) + result = openapi_request_validator.validate(spec, request) assert len(result.errors) == 1 assert type(result.errors[0]) == MissingRequiredParameter assert result.body is None assert result.parameters == Parameters() - def test_request_invalid_param(self, validator): + def test_request_invalid_param(self, spec): request = MockRequest( "http://example.com", "get", "/resource", args={"resId": "invalid"}, ) - result = validator.validate(request) + result = openapi_request_validator.validate(spec, request) assert len(result.errors) == 1 assert type(result.errors[0]) == CastError assert result.body is None assert result.parameters == Parameters() - def test_request_valid_param(self, validator): + def test_request_valid_param(self, spec): request = MockRequest( "http://example.com", "get", "/resource", args={"resId": "10"}, ) - result = validator.validate(request) + result = openapi_request_validator.validate(spec, request) assert len(result.errors) == 0 assert result.body is None assert result.parameters == Parameters(query={"resId": 10}) - def test_request_override_param(self, spec_dict): + def test_request_override_param(self, spec, spec_dict): # override path parameter on operation spec_dict["paths"]["/resource"]["get"]["parameters"] = [ { @@ -500,17 +494,16 @@ def test_request_override_param(self, spec_dict): }, } ] - validator = RequestValidator( - Spec.create(spec_dict), base_url="http://example.com" - ) request = MockRequest("http://example.com", "get", "/resource") - result = validator.validate(request) + result = openapi_request_validator.validate( + spec, request, base_url="http://example.com" + ) assert len(result.errors) == 0 assert result.body is None assert result.parameters == Parameters() - def test_request_override_param_uniqueness(self, spec_dict): + def test_request_override_param_uniqueness(self, spec, spec_dict): # add parameter on operation with same name as on path but # different location spec_dict["paths"]["/resource"]["get"]["parameters"] = [ @@ -524,11 +517,10 @@ def test_request_override_param_uniqueness(self, spec_dict): }, } ] - validator = RequestValidator( - Spec.create(spec_dict), base_url="http://example.com" - ) request = MockRequest("http://example.com", "get", "/resource") - result = validator.validate(request) + result = openapi_request_validator.validate( + spec, request, base_url="http://example.com" + ) assert len(result.errors) == 1 assert type(result.errors[0]) == MissingRequiredParameter @@ -548,88 +540,84 @@ def spec_dict(self, factory): def spec(self, spec_dict): return Spec.create(spec_dict) - @pytest.fixture - def validator(self, spec): - return ResponseValidator(spec, base_url=self.host_url) - - def test_invalid_server(self, validator): + def test_invalid_server(self, spec): request = MockRequest("http://petstore.invalid.net/v1", "get", "/") response = MockResponse("Not Found", status_code=404) - result = validator.validate(request, response) + result = openapi_response_validator.validate(spec, request, response) assert len(result.errors) == 1 assert type(result.errors[0]) == PathNotFound assert result.data is None assert result.headers == {} - def test_invalid_operation(self, validator): + def test_invalid_operation(self, spec): request = MockRequest(self.host_url, "patch", "/v1/pets") response = MockResponse("Not Found", status_code=404) - result = validator.validate(request, response) + result = openapi_response_validator.validate(spec, request, response) assert len(result.errors) == 1 assert type(result.errors[0]) == OperationNotFound assert result.data is None assert result.headers == {} - def test_invalid_response(self, validator): + def test_invalid_response(self, spec): request = MockRequest(self.host_url, "get", "/v1/pets") response = MockResponse("Not Found", status_code=409) - result = validator.validate(request, response) + result = openapi_response_validator.validate(spec, request, response) assert len(result.errors) == 1 assert type(result.errors[0]) == ResponseNotFound assert result.data is None assert result.headers == {} - def test_invalid_content_type(self, validator): + def test_invalid_content_type(self, spec): request = MockRequest(self.host_url, "get", "/v1/pets") response = MockResponse("Not Found", mimetype="text/csv") - result = validator.validate(request, response) + result = openapi_response_validator.validate(spec, request, response) assert len(result.errors) == 1 assert type(result.errors[0]) == MediaTypeNotFound assert result.data is None assert result.headers == {} - def test_missing_body(self, validator): + def test_missing_body(self, spec): request = MockRequest(self.host_url, "get", "/v1/pets") response = MockResponse(None) - result = validator.validate(request, response) + result = openapi_response_validator.validate(spec, request, response) assert len(result.errors) == 1 assert type(result.errors[0]) == MissingResponseContent assert result.data is None assert result.headers == {} - def test_invalid_media_type(self, validator): + def test_invalid_media_type(self, spec): request = MockRequest(self.host_url, "get", "/v1/pets") response = MockResponse("abcde") - result = validator.validate(request, response) + result = openapi_response_validator.validate(spec, request, response) assert len(result.errors) == 1 assert type(result.errors[0]) == MediaTypeDeserializeError assert result.data is None assert result.headers == {} - def test_invalid_media_type_value(self, validator): + def test_invalid_media_type_value(self, spec): request = MockRequest(self.host_url, "get", "/v1/pets") response = MockResponse("{}") - result = validator.validate(request, response) + result = openapi_response_validator.validate(spec, request, response) assert len(result.errors) == 1 assert type(result.errors[0]) == InvalidSchemaValue assert result.data is None assert result.headers == {} - def test_invalid_value(self, validator): + def test_invalid_value(self, spec): request = MockRequest(self.host_url, "get", "/v1/tags") response_json = { "data": [ @@ -639,14 +627,14 @@ def test_invalid_value(self, validator): response_data = json.dumps(response_json) response = MockResponse(response_data) - result = validator.validate(request, response) + result = openapi_response_validator.validate(spec, request, response) assert len(result.errors) == 1 assert type(result.errors[0]) == InvalidSchemaValue assert result.data is None assert result.headers == {} - def test_get_pets(self, validator): + def test_get_pets(self, spec): request = MockRequest(self.host_url, "get", "/v1/pets") response_json = { "data": [ @@ -662,7 +650,7 @@ def test_get_pets(self, validator): response_data = json.dumps(response_json) response = MockResponse(response_data) - result = validator.validate(request, response) + result = openapi_response_validator.validate(spec, request, response) assert result.errors == [] assert isinstance(result.data, BaseModel) diff --git a/tests/unit/validation/test_request_shortcuts.py b/tests/unit/validation/test_request_shortcuts.py index 26c5f8af..f9ab9cde 100644 --- a/tests/unit/validation/test_request_shortcuts.py +++ b/tests/unit/validation/test_request_shortcuts.py @@ -3,12 +3,12 @@ import pytest from openapi_core.testing.datatypes import ResultMock -from openapi_core.validation.request.shortcuts import spec_validate_request +from openapi_core.validation.shortcuts import validate_request class TestValidateRequest: @mock.patch( - "openapi_core.validation.request.shortcuts.RequestValidator.validate" + "openapi_core.validation.shortcuts.openapi_request_validator.validate" ) def test_validator_valid(self, mock_validate): spec = mock.sentinel.spec @@ -17,13 +17,13 @@ def test_validator_valid(self, mock_validate): validation_result = ResultMock(parameters=parameters) mock_validate.return_value = validation_result - result = spec_validate_request(spec, request) + result = validate_request(spec, request) assert result == validation_result mock_validate.aasert_called_once_with(request) @mock.patch( - "openapi_core.validation.request.shortcuts.RequestValidator.validate" + "openapi_core.validation.shortcuts.openapi_request_validator.validate" ) def test_validator_error(self, mock_validate): spec = mock.sentinel.spec @@ -31,6 +31,6 @@ def test_validator_error(self, mock_validate): mock_validate.return_value = ResultMock(error_to_raise=ValueError) with pytest.raises(ValueError): - spec_validate_request(spec, request) + validate_request(spec, request) mock_validate.aasert_called_once_with(request) diff --git a/tests/unit/validation/test_response_shortcuts.py b/tests/unit/validation/test_response_shortcuts.py index d422dfce..fbdcfeec 100644 --- a/tests/unit/validation/test_response_shortcuts.py +++ b/tests/unit/validation/test_response_shortcuts.py @@ -3,12 +3,12 @@ import pytest from openapi_core.testing.datatypes import ResultMock -from openapi_core.validation.response.shortcuts import spec_validate_response +from openapi_core.validation.shortcuts import validate_response class TestSpecValidateData: @mock.patch( - "openapi_core.validation.response.shortcuts.ResponseValidator.validate" + "openapi_core.validation.shortcuts.openapi_response_validator.validate" ) def test_validator_valid(self, mock_validate): spec = mock.sentinel.spec @@ -18,13 +18,13 @@ def test_validator_valid(self, mock_validate): validation_result = ResultMock(data=data) mock_validate.return_value = validation_result - result = spec_validate_response(spec, request, response) + result = validate_response(spec, request, response) assert result == validation_result mock_validate.aasert_called_once_with(request, response) @mock.patch( - "openapi_core.validation.response.shortcuts.ResponseValidator.validate" + "openapi_core.validation.shortcuts.openapi_response_validator.validate" ) def test_validator_error(self, mock_validate): spec = mock.sentinel.spec @@ -33,6 +33,6 @@ def test_validator_error(self, mock_validate): mock_validate.return_value = ResultMock(error_to_raise=ValueError) with pytest.raises(ValueError): - spec_validate_response(spec, request, response) + validate_response(spec, request, response) mock_validate.aasert_called_once_with(request, response) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy