mirror of
https://github.com/correl/openapi-core.git
synced 2025-01-01 11:03:19 +00:00
Merge pull request #144 from p1c2u/fix/parameters-on-path-item-object
Parameters on path item object support
This commit is contained in:
commit
ff5f018e26
6 changed files with 149 additions and 7 deletions
15
openapi_core/schema/paths/exceptions.py
Normal file
15
openapi_core/schema/paths/exceptions.py
Normal 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)
|
|
@ -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]
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 = []
|
||||
|
|
|
@ -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 == {}
|
||||
|
|
|
@ -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'
|
||||
|
|
Loading…
Reference in a new issue