diff --git a/openapi_core/validation/request/validators.py b/openapi_core/validation/request/validators.py index c36b6a20..734b5894 100644 --- a/openapi_core/validation/request/validators.py +++ b/openapi_core/validation/request/validators.py @@ -1,4 +1,5 @@ """OpenAPI core validation request validators module""" +from itertools import chain from six import iteritems from openapi_core.schema.exceptions import OpenAPIMappingError @@ -28,30 +29,34 @@ def validate(self, request): try: path = self.spec[operation_pattern] - # don't process if operation errors - except OpenAPIMappingError as exc: - return RequestValidationResult([exc, ], None, None) - - path_params, path_params_errors = self._get_parameters(request, path) - - try: operation = self.spec.get_operation( operation_pattern, request.method) # don't process if operation errors except OpenAPIMappingError as exc: return RequestValidationResult([exc, ], None, None) - op_params, op_params_errors = self._get_parameters(request, operation) + params, params_errors = self._get_parameters( + request, chain( + iteritems(operation.parameters), + iteritems(path.parameters) + ) + ) + body, body_errors = self._get_body(request, operation) - errors = path_params_errors + op_params_errors + body_errors - return RequestValidationResult(errors, body, path_params + op_params) + errors = params_errors + body_errors + return RequestValidationResult(errors, body, params) - def _get_parameters(self, request, operation): + def _get_parameters(self, request, params): errors = [] - + seen = set() parameters = RequestParameters() - for param_name, param in iteritems(operation.parameters): + for param_name, param in params: + if (param_name, param.location.value) in seen: + # skip parameter already seen + # e.g. overriden path item paremeter on operation + continue + seen.add((param_name, param.location.value)) try: raw_value = param.get_value(request) except MissingParameter: diff --git a/tests/integration/test_validators.py b/tests/integration/test_validators.py index d6e6a5a3..2f26a7bc 100644 --- a/tests/integration/test_validators.py +++ b/tests/integration/test_validators.py @@ -235,7 +235,7 @@ def test_get_pet(self, validator): class TestPathItemParamsValidator(object): @pytest.fixture - def spec_dict(self, factory): + def spec_dict(self): return { "openapi": "3.0.0", "info": { @@ -305,6 +305,50 @@ def test_request_valid_param(self, validator): assert result.body is None assert result.parameters == {'query': {'resId': 10}} + def test_request_override_param(self, spec_dict): + # override path parameter on operation + spec_dict["paths"]["/resource"]["get"]["parameters"] = [ + { + # full valid parameter object required + "name": "resId", + "in": "query", + "required": False, + "schema": { + "type": "integer", + }, + } + ] + validator = RequestValidator(create_spec(spec_dict)) + request = MockRequest('http://example.com', 'get', '/resource') + result = validator.validate(request) + + assert len(result.errors) == 0 + assert result.body is None + assert result.parameters == {} + + def test_request_override_param_uniqueness(self, spec_dict): + # add parameter on operation with same name as on path but + # different location + spec_dict["paths"]["/resource"]["get"]["parameters"] = [ + { + # full valid parameter object required + "name": "resId", + "in": "header", + "required": False, + "schema": { + "type": "integer", + }, + } + ] + validator = RequestValidator(create_spec(spec_dict)) + request = MockRequest('http://example.com', 'get', '/resource') + result = validator.validate(request) + + assert len(result.errors) == 1 + assert type(result.errors[0]) == MissingRequiredParameter + assert result.body is None + assert result.parameters == {} + class TestResponseValidator(object):