OpenAPI request and response datatypes

This commit is contained in:
p1c2u 2019-10-19 13:01:56 +01:00
parent 8a614de0bb
commit cffc47c60a
8 changed files with 148 additions and 94 deletions

View file

@ -0,0 +1,10 @@
"""OpenAPI core testing module"""
from openapi_core.testing.mock import MockRequestFactory, MockResponseFactory
# backward compatibility
MockRequest = MockRequestFactory.create
MockResponse = MockResponseFactory.create
__all__ = [
'MockRequestFactory', 'MockResponseFactory', 'MockRequest', 'MockResponse',
]

View file

@ -1,36 +1,45 @@
"""OpenAPI core wrappers module""" """OpenAPI core testing mock module"""
from werkzeug.datastructures import ImmutableMultiDict from werkzeug.datastructures import ImmutableMultiDict
from openapi_core.wrappers.base import BaseOpenAPIRequest, BaseOpenAPIResponse from openapi_core.validation.request.datatypes import (
RequestParameters, OpenAPIRequest,
)
from openapi_core.validation.response.datatypes import OpenAPIResponse
class MockRequest(BaseOpenAPIRequest): class MockRequestFactory(object):
def __init__( @classmethod
self, host_url, method, path, path_pattern=None, args=None, def create(
cls, host_url, method, path, path_pattern=None, args=None,
view_args=None, headers=None, cookies=None, data=None, view_args=None, headers=None, cookies=None, data=None,
mimetype='application/json'): mimetype='application/json'):
self.host_url = host_url parameters = RequestParameters(
self.path = path path=view_args or {},
self.path_pattern = path_pattern or path query=ImmutableMultiDict(args or []),
self.method = method.lower() header=headers or {},
cookie=cookies or {},
self.parameters = { )
'path': view_args or {}, path_pattern = path_pattern or path
'query': ImmutableMultiDict(args or []), method = method.lower()
'header': headers or {}, body = data or ''
'cookie': cookies or {}, return OpenAPIRequest(
} host_url=host_url,
path=path,
self.body = data or '' path_pattern=path_pattern,
method=method,
self.mimetype = mimetype parameters=parameters,
body=body,
mimetype=mimetype,
)
class MockResponse(BaseOpenAPIResponse): class MockResponseFactory(object):
def __init__(self, data, status_code=200, mimetype='application/json'): @classmethod
self.data = data def create(cls, data, status_code=200, mimetype='application/json'):
return OpenAPIResponse(
self.status_code = status_code data=data,
self.mimetype = mimetype status_code=status_code,
mimetype=mimetype,
)

View file

@ -1,13 +1,17 @@
"""OpenAPI core validation request datatypes module""" """OpenAPI core validation request datatypes module"""
import attr import attr
from werkzeug.datastructures import ImmutableMultiDict
from openapi_core.validation.datatypes import BaseValidationResult from openapi_core.validation.datatypes import BaseValidationResult
from six.moves.urllib.parse import urljoin
@attr.s @attr.s
class RequestParameters(object): class RequestParameters(object):
path = attr.ib(factory=dict) path = attr.ib(factory=dict)
query = attr.ib(factory=dict) query = attr.ib(factory=ImmutableMultiDict)
header = attr.ib(factory=dict) header = attr.ib(factory=dict)
cookie = attr.ib(factory=dict) cookie = attr.ib(factory=dict)
@ -15,6 +19,25 @@ class RequestParameters(object):
return getattr(self, location) return getattr(self, location)
@attr.s
class OpenAPIRequest(object):
host_url = attr.ib()
path = attr.ib()
path_pattern = attr.ib()
method = attr.ib()
body = attr.ib()
mimetype = attr.ib()
parameters = attr.ib(factory=RequestParameters)
@property
def full_url_pattern(self):
return urljoin(self.host_url, self.path_pattern)
@attr.s @attr.s
class RequestValidationResult(BaseValidationResult): class RequestValidationResult(BaseValidationResult):
body = attr.ib(default=None) body = attr.ib(default=None)

View file

@ -50,7 +50,7 @@ class RequestValidator(object):
def _get_parameters(self, request, params): def _get_parameters(self, request, params):
errors = [] errors = []
seen = set() seen = set()
parameters = RequestParameters() locations = {}
for param_name, param in params: for param_name, param in params:
if (param_name, param.location.value) in seen: if (param_name, param.location.value) in seen:
# skip parameter already seen # skip parameter already seen
@ -79,9 +79,10 @@ class RequestValidator(object):
except OpenAPIMappingError as exc: except OpenAPIMappingError as exc:
errors.append(exc) errors.append(exc)
else: else:
parameters[param.location.value][param_name] = unmarshalled locations.setdefault(param.location.value, {})
locations[param.location.value][param_name] = unmarshalled
return parameters, errors return RequestParameters(**locations), errors
def _get_body(self, request, operation): def _get_body(self, request, operation):
errors = [] errors = []

View file

@ -4,6 +4,15 @@ import attr
from openapi_core.validation.datatypes import BaseValidationResult from openapi_core.validation.datatypes import BaseValidationResult
@attr.s
class OpenAPIResponse(object):
data = attr.ib()
status_code = attr.ib()
mimetype = attr.ib()
@attr.s @attr.s
class ResponseValidationResult(BaseValidationResult): class ResponseValidationResult(BaseValidationResult):
data = attr.ib(default=None) data = attr.ib(default=None)

View file

@ -3,7 +3,7 @@ import pytest
from openapi_core.schema.operations.exceptions import InvalidOperation from openapi_core.schema.operations.exceptions import InvalidOperation
from openapi_core.schema.paths.exceptions import InvalidPath from openapi_core.schema.paths.exceptions import InvalidPath
from openapi_core.shortcuts import create_spec from openapi_core.shortcuts import create_spec
from openapi_core.testing.mock import MockRequest from openapi_core.testing import MockRequest
from openapi_core.validation.request.validators import RequestValidator from openapi_core.validation.request.validators import RequestValidator

View file

@ -15,8 +15,10 @@ from openapi_core.schema.parameters.exceptions import (
from openapi_core.schema.schemas.enums import SchemaType from openapi_core.schema.schemas.enums import SchemaType
from openapi_core.schema.schemas.exceptions import InvalidSchemaValue from openapi_core.schema.schemas.exceptions import InvalidSchemaValue
from openapi_core.schema.servers.exceptions import InvalidServer from openapi_core.schema.servers.exceptions import InvalidServer
from openapi_core.shortcuts import create_spec from openapi_core.shortcuts import (
from openapi_core.testing.mock import MockRequest, MockResponse create_spec, validate_parameters, validate_body,
)
from openapi_core.testing import MockRequest, MockResponse
from openapi_core.validation.request.datatypes import RequestParameters from openapi_core.validation.request.datatypes import RequestParameters
from openapi_core.validation.request.validators import RequestValidator from openapi_core.validation.request.validators import RequestValidator
from openapi_core.validation.response.validators import ResponseValidator from openapi_core.validation.response.validators import ResponseValidator
@ -64,8 +66,8 @@ class TestPetstore(object):
path_pattern=path_pattern, args=query_params, path_pattern=path_pattern, args=query_params,
) )
parameters = request.get_parameters(spec) parameters = validate_parameters(spec, request)
body = request.get_body(spec) body = validate_body(spec, request)
assert parameters == RequestParameters( assert parameters == RequestParameters(
query={ query={
@ -100,8 +102,8 @@ class TestPetstore(object):
path_pattern=path_pattern, args=query_params, path_pattern=path_pattern, args=query_params,
) )
parameters = request.get_parameters(spec) parameters = validate_parameters(spec, request)
body = request.get_body(spec) body = validate_body(spec, request)
assert parameters == RequestParameters( assert parameters == RequestParameters(
query={ query={
@ -146,8 +148,8 @@ class TestPetstore(object):
path_pattern=path_pattern, args=query_params, path_pattern=path_pattern, args=query_params,
) )
parameters = request.get_parameters(spec) parameters = validate_parameters(spec, request)
body = request.get_body(spec) body = validate_body(spec, request)
assert parameters == RequestParameters( assert parameters == RequestParameters(
query={ query={
@ -197,8 +199,8 @@ class TestPetstore(object):
path_pattern=path_pattern, args=query_params, path_pattern=path_pattern, args=query_params,
) )
parameters = request.get_parameters(spec) parameters = validate_parameters(spec, request)
body = request.get_body(spec) body = validate_body(spec, request)
assert parameters == RequestParameters( assert parameters == RequestParameters(
query={ query={
@ -235,8 +237,8 @@ class TestPetstore(object):
path_pattern=path_pattern, args=query_params, path_pattern=path_pattern, args=query_params,
) )
parameters = request.get_parameters(spec) parameters = validate_parameters(spec, request)
body = request.get_body(spec) body = validate_body(spec, request)
assert parameters == RequestParameters( assert parameters == RequestParameters(
query={ query={
@ -274,9 +276,9 @@ class TestPetstore(object):
) )
with pytest.raises(InvalidParameterValue): with pytest.raises(InvalidParameterValue):
request.get_parameters(spec) validate_parameters(spec, request)
body = request.get_body(spec) body = validate_body(spec, request)
assert body is None assert body is None
@ -293,9 +295,9 @@ class TestPetstore(object):
) )
with pytest.raises(InvalidParameterValue): with pytest.raises(InvalidParameterValue):
request.get_parameters(spec) validate_parameters(spec, request)
body = request.get_body(spec) body = validate_body(spec, request)
assert body is None assert body is None
@ -308,9 +310,9 @@ class TestPetstore(object):
) )
with pytest.raises(MissingRequiredParameter): with pytest.raises(MissingRequiredParameter):
request.get_parameters(spec) validate_parameters(spec, request)
body = request.get_body(spec) body = validate_body(spec, request)
assert body is None assert body is None
@ -327,8 +329,8 @@ class TestPetstore(object):
) )
with pytest.raises(EmptyParameterValue): with pytest.raises(EmptyParameterValue):
request.get_parameters(spec) validate_parameters(spec, request)
body = request.get_body(spec) body = validate_body(spec, request)
assert body is None assert body is None
@ -344,7 +346,7 @@ class TestPetstore(object):
path_pattern=path_pattern, args=query_params, path_pattern=path_pattern, args=query_params,
) )
parameters = request.get_parameters(spec) parameters = validate_parameters(spec, request)
assert parameters == RequestParameters( assert parameters == RequestParameters(
query={ query={
@ -354,7 +356,7 @@ class TestPetstore(object):
} }
) )
body = request.get_body(spec) body = validate_body(spec, request)
assert body is None assert body is None
@ -393,7 +395,7 @@ class TestPetstore(object):
headers=headers, cookies=cookies, headers=headers, cookies=cookies,
) )
parameters = request.get_parameters(spec) parameters = validate_parameters(spec, request)
assert parameters == RequestParameters( assert parameters == RequestParameters(
header={ header={
@ -404,7 +406,7 @@ class TestPetstore(object):
}, },
) )
body = request.get_body(spec) body = validate_body(spec, request)
schemas = spec_dict['components']['schemas'] schemas = spec_dict['components']['schemas']
pet_model = schemas['PetCreate']['x-model'] pet_model = schemas['PetCreate']['x-model']
@ -453,7 +455,7 @@ class TestPetstore(object):
headers=headers, cookies=cookies, headers=headers, cookies=cookies,
) )
parameters = request.get_parameters(spec) parameters = validate_parameters(spec, request)
assert parameters == RequestParameters( assert parameters == RequestParameters(
header={ header={
@ -464,7 +466,7 @@ class TestPetstore(object):
}, },
) )
body = request.get_body(spec) body = validate_body(spec, request)
schemas = spec_dict['components']['schemas'] schemas = spec_dict['components']['schemas']
pet_model = schemas['PetCreate']['x-model'] pet_model = schemas['PetCreate']['x-model']
@ -513,7 +515,7 @@ class TestPetstore(object):
headers=headers, cookies=cookies, headers=headers, cookies=cookies,
) )
parameters = request.get_parameters(spec) parameters = validate_parameters(spec, request)
assert parameters == RequestParameters( assert parameters == RequestParameters(
header={ header={
@ -524,7 +526,7 @@ class TestPetstore(object):
}, },
) )
body = request.get_body(spec) body = validate_body(spec, request)
schemas = spec_dict['components']['schemas'] schemas = spec_dict['components']['schemas']
pet_model = schemas['PetCreate']['x-model'] pet_model = schemas['PetCreate']['x-model']
@ -561,7 +563,7 @@ class TestPetstore(object):
headers=headers, cookies=cookies, headers=headers, cookies=cookies,
) )
parameters = request.get_parameters(spec) parameters = validate_parameters(spec, request)
assert parameters == RequestParameters( assert parameters == RequestParameters(
header={ header={
@ -573,7 +575,7 @@ class TestPetstore(object):
) )
with pytest.raises(InvalidMediaTypeValue): with pytest.raises(InvalidMediaTypeValue):
request.get_body(spec) validate_body(spec, request)
def test_post_cats_only_required_body(self, spec, spec_dict): def test_post_cats_only_required_body(self, spec, spec_dict):
host_url = 'http://petstore.swagger.io/v1' host_url = 'http://petstore.swagger.io/v1'
@ -600,7 +602,7 @@ class TestPetstore(object):
headers=headers, cookies=cookies, headers=headers, cookies=cookies,
) )
parameters = request.get_parameters(spec) parameters = validate_parameters(spec, request)
assert parameters == RequestParameters( assert parameters == RequestParameters(
header={ header={
@ -611,7 +613,7 @@ class TestPetstore(object):
}, },
) )
body = request.get_body(spec) body = validate_body(spec, request)
schemas = spec_dict['components']['schemas'] schemas = spec_dict['components']['schemas']
pet_model = schemas['PetCreate']['x-model'] pet_model = schemas['PetCreate']['x-model']
@ -641,7 +643,7 @@ class TestPetstore(object):
headers=headers, cookies=cookies, headers=headers, cookies=cookies,
) )
parameters = request.get_parameters(spec) parameters = validate_parameters(spec, request)
assert parameters == RequestParameters( assert parameters == RequestParameters(
header={ header={
@ -653,7 +655,7 @@ class TestPetstore(object):
) )
with pytest.raises(InvalidContentType): with pytest.raises(InvalidContentType):
request.get_body(spec) validate_body(spec, request)
def test_post_pets_missing_cookie(self, spec, spec_dict): def test_post_pets_missing_cookie(self, spec, spec_dict):
host_url = 'http://petstore.swagger.io/v1' host_url = 'http://petstore.swagger.io/v1'
@ -678,9 +680,9 @@ class TestPetstore(object):
) )
with pytest.raises(MissingRequiredParameter): with pytest.raises(MissingRequiredParameter):
request.get_parameters(spec) validate_parameters(spec, request)
body = request.get_body(spec) body = validate_body(spec, request)
schemas = spec_dict['components']['schemas'] schemas = spec_dict['components']['schemas']
pet_model = schemas['PetCreate']['x-model'] pet_model = schemas['PetCreate']['x-model']
@ -712,9 +714,9 @@ class TestPetstore(object):
) )
with pytest.raises(MissingRequiredParameter): with pytest.raises(MissingRequiredParameter):
request.get_parameters(spec) validate_parameters(spec, request)
body = request.get_body(spec) body = validate_body(spec, request)
schemas = spec_dict['components']['schemas'] schemas = spec_dict['components']['schemas']
pet_model = schemas['PetCreate']['x-model'] pet_model = schemas['PetCreate']['x-model']
@ -745,10 +747,10 @@ class TestPetstore(object):
) )
with pytest.raises(InvalidServer): with pytest.raises(InvalidServer):
request.get_parameters(spec) validate_parameters(spec, request)
with pytest.raises(InvalidServer): with pytest.raises(InvalidServer):
request.get_body(spec) validate_body(spec, request)
def test_get_pet(self, spec, response_validator): def test_get_pet(self, spec, response_validator):
host_url = 'http://petstore.swagger.io/v1' host_url = 'http://petstore.swagger.io/v1'
@ -761,7 +763,7 @@ class TestPetstore(object):
path_pattern=path_pattern, view_args=view_args, path_pattern=path_pattern, view_args=view_args,
) )
parameters = request.get_parameters(spec) parameters = validate_parameters(spec, request)
assert parameters == RequestParameters( assert parameters == RequestParameters(
path={ path={
@ -769,7 +771,7 @@ class TestPetstore(object):
} }
) )
body = request.get_body(spec) body = validate_body(spec, request)
assert body is None assert body is None
@ -806,7 +808,7 @@ class TestPetstore(object):
path_pattern=path_pattern, view_args=view_args, path_pattern=path_pattern, view_args=view_args,
) )
parameters = request.get_parameters(spec) parameters = validate_parameters(spec, request)
assert parameters == RequestParameters( assert parameters == RequestParameters(
path={ path={
@ -814,7 +816,7 @@ class TestPetstore(object):
} }
) )
body = request.get_body(spec) body = validate_body(spec, request)
assert body is None assert body is None
@ -848,7 +850,7 @@ class TestPetstore(object):
path_pattern=path_pattern, view_args=view_args, path_pattern=path_pattern, view_args=view_args,
) )
parameters = request.get_parameters(spec) parameters = validate_parameters(spec, request)
assert parameters == RequestParameters( assert parameters == RequestParameters(
path={ path={
@ -856,7 +858,7 @@ class TestPetstore(object):
} }
) )
body = request.get_body(spec) body = validate_body(spec, request)
assert body is None assert body is None
@ -877,8 +879,8 @@ class TestPetstore(object):
path_pattern=path_pattern, path_pattern=path_pattern,
) )
parameters = request.get_parameters(spec) parameters = validate_parameters(spec, request)
body = request.get_body(spec) body = validate_body(spec, request)
assert parameters == RequestParameters() assert parameters == RequestParameters()
assert body is None assert body is None
@ -908,12 +910,12 @@ class TestPetstore(object):
path_pattern=path_pattern, data=data, path_pattern=path_pattern, data=data,
) )
parameters = request.get_parameters(spec) parameters = validate_parameters(spec, request)
assert parameters == RequestParameters() assert parameters == RequestParameters()
with pytest.raises(InvalidMediaTypeValue): with pytest.raises(InvalidMediaTypeValue):
request.get_body(spec) validate_body(spec, request)
def test_post_tags_empty_body(self, spec, spec_dict): def test_post_tags_empty_body(self, spec, spec_dict):
host_url = 'http://petstore.swagger.io/v1' host_url = 'http://petstore.swagger.io/v1'
@ -926,12 +928,12 @@ class TestPetstore(object):
path_pattern=path_pattern, data=data, path_pattern=path_pattern, data=data,
) )
parameters = request.get_parameters(spec) parameters = validate_parameters(spec, request)
assert parameters == RequestParameters() assert parameters == RequestParameters()
with pytest.raises(InvalidMediaTypeValue): with pytest.raises(InvalidMediaTypeValue):
request.get_body(spec) validate_body(spec, request)
def test_post_tags_wrong_property_type(self, spec): def test_post_tags_wrong_property_type(self, spec):
host_url = 'http://petstore.swagger.io/v1' host_url = 'http://petstore.swagger.io/v1'
@ -944,12 +946,12 @@ class TestPetstore(object):
path_pattern=path_pattern, data=data, path_pattern=path_pattern, data=data,
) )
parameters = request.get_parameters(spec) parameters = validate_parameters(spec, request)
assert parameters == RequestParameters() assert parameters == RequestParameters()
with pytest.raises(InvalidMediaTypeValue): with pytest.raises(InvalidMediaTypeValue):
request.get_body(spec) validate_body(spec, request)
def test_post_tags_additional_properties( def test_post_tags_additional_properties(
self, spec, response_validator): self, spec, response_validator):
@ -966,8 +968,8 @@ class TestPetstore(object):
path_pattern=path_pattern, data=data, path_pattern=path_pattern, data=data,
) )
parameters = request.get_parameters(spec) parameters = validate_parameters(spec, request)
body = request.get_body(spec) body = validate_body(spec, request)
assert parameters == RequestParameters() assert parameters == RequestParameters()
assert isinstance(body, BaseModel) assert isinstance(body, BaseModel)
@ -1012,8 +1014,8 @@ class TestPetstore(object):
path_pattern=path_pattern, data=data, path_pattern=path_pattern, data=data,
) )
parameters = request.get_parameters(spec) parameters = validate_parameters(spec, request)
body = request.get_body(spec) body = validate_body(spec, request)
assert parameters == RequestParameters() assert parameters == RequestParameters()
assert isinstance(body, BaseModel) assert isinstance(body, BaseModel)
@ -1059,8 +1061,8 @@ class TestPetstore(object):
path_pattern=path_pattern, data=data, path_pattern=path_pattern, data=data,
) )
parameters = request.get_parameters(spec) parameters = validate_parameters(spec, request)
body = request.get_body(spec) body = validate_body(spec, request)
assert parameters == RequestParameters() assert parameters == RequestParameters()
assert isinstance(body, BaseModel) assert isinstance(body, BaseModel)
@ -1106,9 +1108,9 @@ class TestPetstore(object):
path_pattern=path_pattern, data=data, path_pattern=path_pattern, data=data,
) )
parameters = request.get_parameters(spec) parameters = validate_parameters(spec, request)
with pytest.raises(InvalidMediaTypeValue): with pytest.raises(InvalidMediaTypeValue):
request.get_body(spec) validate_body(spec, request)
assert parameters == RequestParameters() assert parameters == RequestParameters()

View file

@ -17,7 +17,7 @@ from openapi_core.schema.responses.exceptions import (
) )
from openapi_core.schema.servers.exceptions import InvalidServer from openapi_core.schema.servers.exceptions import InvalidServer
from openapi_core.shortcuts import create_spec from openapi_core.shortcuts import create_spec
from openapi_core.testing.mock import MockRequest, MockResponse from openapi_core.testing import MockRequest, MockResponse
from openapi_core.validation.request.datatypes import RequestParameters from openapi_core.validation.request.datatypes import RequestParameters
from openapi_core.validation.request.validators import RequestValidator from openapi_core.validation.request.validators import RequestValidator
from openapi_core.validation.response.validators import ResponseValidator from openapi_core.validation.response.validators import ResponseValidator