Merge pull request #144 from p1c2u/fix/parameters-on-path-item-object

Parameters on path item object support
This commit is contained in:
A 2019-06-18 22:32:52 +01:00 committed by GitHub
commit ff5f018e26
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 149 additions and 7 deletions

View file

@ -0,0 +1,15 @@
import attr
from openapi_core.schema.exceptions import OpenAPIMappingError
class OpenAPIPathError(OpenAPIMappingError):
pass
@attr.s(hash=True)
class InvalidPath(OpenAPIPathError):
path_pattern = attr.ib()
def __str__(self):
return "Unknown path {0}".format(self.path_pattern)

View file

@ -4,6 +4,7 @@ import logging
from openapi_core.compat import partialmethod
from openapi_core.schema.operations.exceptions import InvalidOperation
from openapi_core.schema.paths.exceptions import InvalidPath
from openapi_core.schema.servers.exceptions import InvalidServer
@ -19,8 +20,8 @@ class Spec(object):
self.servers = servers or []
self.components = components
def __getitem__(self, path_name):
return self.paths[path_name]
def __getitem__(self, path_pattern):
return self.get_path(path_pattern)
@property
def default_url(self):
@ -36,6 +37,12 @@ class Spec(object):
def get_server_url(self, index=0):
return self.servers[index].default_url
def get_path(self, path_pattern):
try:
return self.paths[path_pattern]
except KeyError:
raise InvalidPath(path_pattern)
def get_operation(self, path_pattern, http_method):
try:
return self.paths[path_pattern].operations[http_method]

View file

@ -16,6 +16,16 @@ class RequestParameters(dict):
def __setitem__(self, location, value):
raise NotImplementedError
def __add__(self, other):
if not isinstance(other, self.__class__):
raise ValueError("Invalid type")
for location in self.valid_locations:
if location in other:
self[location].update(other[location])
return self
@classmethod
def validate_location(cls, location):
if location not in cls.valid_locations:

View file

@ -26,6 +26,14 @@ class RequestValidator(object):
server.default_url, request.full_url_pattern
)
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)
@ -33,11 +41,11 @@ class RequestValidator(object):
except OpenAPIMappingError as exc:
return RequestValidationResult([exc, ], None, None)
params, params_errors = self._get_parameters(request, operation)
op_params, op_params_errors = self._get_parameters(request, operation)
body, body_errors = self._get_body(request, operation)
errors = params_errors + body_errors
return RequestValidationResult(errors, body, params)
errors = path_params_errors + op_params_errors + body_errors
return RequestValidationResult(errors, body, path_params + op_params)
def _get_parameters(self, request, operation):
errors = []

View file

@ -1,6 +1,7 @@
import pytest
from openapi_core.schema.operations.exceptions import InvalidOperation
from openapi_core.schema.paths.exceptions import InvalidPath
from openapi_core.shortcuts import create_spec
from openapi_core.validation.request.validators import RequestValidator
from openapi_core.wrappers.mock import MockRequest
@ -39,7 +40,7 @@ class TestMinimal(object):
spec_dict = factory.spec_from_file(spec_path)
spec = create_spec(spec_dict)
validator = RequestValidator(spec)
request = MockRequest(server, "get", "/nonexistent")
request = MockRequest(server, "post", "/status")
result = validator.validate(request)
@ -47,3 +48,18 @@ class TestMinimal(object):
assert isinstance(result.errors[0], InvalidOperation)
assert result.body is None
assert result.parameters == {}
@pytest.mark.parametrize("server", servers)
@pytest.mark.parametrize("spec_path", spec_paths)
def test_invalid_path(self, factory, server, spec_path):
spec_dict = factory.spec_from_file(spec_path)
spec = create_spec(spec_dict)
validator = RequestValidator(spec)
request = MockRequest(server, "get", "/nonexistent")
result = validator.validate(request)
assert len(result.errors) == 1
assert isinstance(result.errors[0], InvalidPath)
assert result.body is None
assert result.parameters == {}

View file

@ -9,6 +9,8 @@ from openapi_core.schema.media_types.exceptions import (
from openapi_core.extensions.models.models import BaseModel
from openapi_core.schema.operations.exceptions import InvalidOperation
from openapi_core.schema.parameters.exceptions import MissingRequiredParameter
from openapi_core.schema.parameters.exceptions import InvalidParameterValue
from openapi_core.schema.paths.exceptions import InvalidPath
from openapi_core.schema.request_bodies.exceptions import MissingRequestBody
from openapi_core.schema.responses.exceptions import (
MissingResponseContent, InvalidResponse,
@ -54,11 +56,21 @@ class TestRequestValidator(object):
assert result.body is None
assert result.parameters == {}
def test_invalid_operation(self, validator):
def test_invalid_path(self, validator):
request = MockRequest(self.host_url, 'get', '/v1')
result = validator.validate(request)
assert len(result.errors) == 1
assert type(result.errors[0]) == InvalidPath
assert result.body is None
assert result.parameters == {}
def test_invalid_operation(self, validator):
request = MockRequest(self.host_url, 'patch', '/v1/pets')
result = validator.validate(request)
assert len(result.errors) == 1
assert type(result.errors[0]) == InvalidOperation
assert result.body is None
@ -220,6 +232,80 @@ class TestRequestValidator(object):
}
class TestPathItemParamsValidator(object):
@pytest.fixture
def spec_dict(self, factory):
return {
"openapi": "3.0.0",
"info": {
"title": "Test path item parameter validation",
"version": "0.1",
},
"paths": {
"/resource": {
"parameters": [
{
"name": "resId",
"in": "query",
"required": True,
"schema": {
"type": "integer",
},
},
],
"get": {
"responses": {
"default": {
"description": "Return the resource."
}
}
}
}
}
}
@pytest.fixture
def spec(self, spec_dict):
return create_spec(spec_dict)
@pytest.fixture
def validator(self, spec):
return RequestValidator(spec)
def test_request_missing_param(self, validator):
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 == {}
def test_request_invalid_param(self, validator):
request = MockRequest(
'http://example.com', 'get', '/resource',
args={'resId': 'invalid'},
)
result = validator.validate(request)
assert len(result.errors) == 1
assert type(result.errors[0]) == InvalidParameterValue
assert result.body is None
assert result.parameters == {}
def test_request_valid_param(self, validator):
request = MockRequest(
'http://example.com', 'get', '/resource',
args={'resId': '10'},
)
result = validator.validate(request)
assert len(result.errors) == 0
assert result.body is None
assert result.parameters == {'query': {'resId': 10}}
class TestResponseValidator(object):
host_url = 'http://petstore.swagger.io'