mirror of
https://github.com/correl/openapi-core.git
synced 2024-11-21 19:18:41 +00:00
Spec path
This commit is contained in:
parent
b79c49420e
commit
35f8e28157
38 changed files with 1530 additions and 723 deletions
|
@ -14,7 +14,7 @@ class PrimitiveCaster(object):
|
|||
try:
|
||||
return self.caster_callable(value)
|
||||
except (ValueError, TypeError):
|
||||
raise CastError(value, self.schema.type.value)
|
||||
raise CastError(value, self.schema['type'])
|
||||
|
||||
|
||||
class DummyCaster(object):
|
||||
|
@ -31,7 +31,7 @@ class ArrayCaster(object):
|
|||
|
||||
@property
|
||||
def items_caster(self):
|
||||
return self.casters_factory.create(self.schema.items)
|
||||
return self.casters_factory.create(self.schema / 'items')
|
||||
|
||||
def __call__(self, value):
|
||||
if value in (None, NoValue):
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
from openapi_core.schema.schemas.enums import SchemaType
|
||||
|
||||
from openapi_core.casting.schemas.casters import (
|
||||
PrimitiveCaster, DummyCaster, ArrayCaster
|
||||
)
|
||||
|
@ -9,23 +7,24 @@ from openapi_core.casting.schemas.util import forcebool
|
|||
class SchemaCastersFactory(object):
|
||||
|
||||
DUMMY_CASTERS = [
|
||||
SchemaType.STRING, SchemaType.OBJECT, SchemaType.ANY,
|
||||
'string', 'object', 'any',
|
||||
]
|
||||
PRIMITIVE_CASTERS = {
|
||||
SchemaType.INTEGER: int,
|
||||
SchemaType.NUMBER: float,
|
||||
SchemaType.BOOLEAN: forcebool,
|
||||
'integer': int,
|
||||
'number': float,
|
||||
'boolean': forcebool,
|
||||
}
|
||||
COMPLEX_CASTERS = {
|
||||
SchemaType.ARRAY: ArrayCaster,
|
||||
'array': ArrayCaster,
|
||||
}
|
||||
|
||||
def create(self, schema):
|
||||
if schema.type in self.DUMMY_CASTERS:
|
||||
schema_type = schema.getkey('type', 'any')
|
||||
if schema_type in self.DUMMY_CASTERS:
|
||||
return DummyCaster()
|
||||
elif schema.type in self.PRIMITIVE_CASTERS:
|
||||
caster_callable = self.PRIMITIVE_CASTERS[schema.type]
|
||||
elif schema_type in self.PRIMITIVE_CASTERS:
|
||||
caster_callable = self.PRIMITIVE_CASTERS[schema_type]
|
||||
return PrimitiveCaster(schema, caster_callable)
|
||||
elif schema.type in self.COMPLEX_CASTERS:
|
||||
caster_class = self.COMPLEX_CASTERS[schema.type]
|
||||
elif schema_type in self.COMPLEX_CASTERS:
|
||||
caster_class = self.COMPLEX_CASTERS[schema_type]
|
||||
return caster_class(schema, self)
|
||||
|
|
|
@ -20,11 +20,11 @@ class MediaTypeDeserializersFactory(object):
|
|||
custom_deserializers = {}
|
||||
self.custom_deserializers = custom_deserializers
|
||||
|
||||
def create(self, media_type):
|
||||
def create(self, mimetype):
|
||||
deserialize_callable = self.get_deserializer_callable(
|
||||
media_type.mimetype)
|
||||
mimetype)
|
||||
return PrimitiveDeserializer(
|
||||
media_type.mimetype, deserialize_callable)
|
||||
mimetype, deserialize_callable)
|
||||
|
||||
def get_deserializer_callable(self, mimetype):
|
||||
if mimetype in self.custom_deserializers:
|
||||
|
|
|
@ -3,6 +3,7 @@ from openapi_core.deserializing.parameters.exceptions import (
|
|||
EmptyParameterValue,
|
||||
)
|
||||
from openapi_core.schema.parameters.enums import ParameterLocation
|
||||
from openapi_core.spec.parameters import get_aslist, get_explode, get_style
|
||||
|
||||
|
||||
class PrimitiveDeserializer(object):
|
||||
|
@ -11,15 +12,20 @@ class PrimitiveDeserializer(object):
|
|||
self.param = param
|
||||
self.deserializer_callable = deserializer_callable
|
||||
|
||||
def __call__(self, value):
|
||||
if (self.param.location == ParameterLocation.QUERY and value == "" and
|
||||
not self.param.allow_empty_value):
|
||||
raise EmptyParameterValue(
|
||||
value, self.param.style, self.param.name)
|
||||
self.aslist = get_aslist(self.param)
|
||||
self.explode = get_explode(self.param)
|
||||
self.style = get_style(self.param)
|
||||
|
||||
if not self.param.aslist or self.param.explode:
|
||||
def __call__(self, value):
|
||||
style = get_style(self.param)
|
||||
if (self.param['in'] == 'query' and value == "" and
|
||||
not self.param.getkey('allowEmptyValue', False)):
|
||||
raise EmptyParameterValue(
|
||||
value, self.style, self.param['name'])
|
||||
|
||||
if not self.aslist or self.explode:
|
||||
return value
|
||||
try:
|
||||
return self.deserializer_callable(value)
|
||||
except (ValueError, TypeError, AttributeError):
|
||||
raise DeserializeError(value, self.param.style)
|
||||
raise DeserializeError(value, self.style)
|
||||
|
|
|
@ -3,24 +3,26 @@ import warnings
|
|||
from openapi_core.deserializing.parameters.deserializers import (
|
||||
PrimitiveDeserializer,
|
||||
)
|
||||
from openapi_core.schema.parameters.enums import ParameterStyle
|
||||
from openapi_core.schema.parameters import get_style
|
||||
|
||||
|
||||
class ParameterDeserializersFactory(object):
|
||||
|
||||
PARAMETER_STYLE_DESERIALIZERS = {
|
||||
ParameterStyle.FORM: lambda x: x.split(','),
|
||||
ParameterStyle.SIMPLE: lambda x: x.split(','),
|
||||
ParameterStyle.SPACE_DELIMITED: lambda x: x.split(' '),
|
||||
ParameterStyle.PIPE_DELIMITED: lambda x: x.split('|'),
|
||||
'form': lambda x: x.split(','),
|
||||
'simple': lambda x: x.split(','),
|
||||
'spaceDelimited': lambda x: x.split(' '),
|
||||
'pipeDelimited': lambda x: x.split('|'),
|
||||
}
|
||||
|
||||
def create(self, param):
|
||||
if param.deprecated:
|
||||
if param.getkey('deprecated', False):
|
||||
warnings.warn(
|
||||
"{0} parameter is deprecated".format(param.name),
|
||||
"{0} parameter is deprecated".format(param['name']),
|
||||
DeprecationWarning,
|
||||
)
|
||||
|
||||
deserialize_callable = self.PARAMETER_STYLE_DESERIALIZERS[param.style]
|
||||
style = get_style(param)
|
||||
|
||||
deserialize_callable = self.PARAMETER_STYLE_DESERIALIZERS[style]
|
||||
return PrimitiveDeserializer(param, deserialize_callable)
|
||||
|
|
98
openapi_core/schema/models.py
Normal file
98
openapi_core/schema/models.py
Normal file
|
@ -0,0 +1,98 @@
|
|||
import sys
|
||||
|
||||
|
||||
class Spec(object):
|
||||
sep = '/'
|
||||
|
||||
def __new__(cls, *args):
|
||||
return cls._from_parts(args)
|
||||
|
||||
@classmethod
|
||||
def _parse_args(cls, args):
|
||||
# This is useful when you don't want to create an instance, just
|
||||
# canonicalize some constructor arguments.
|
||||
parts = []
|
||||
for a in args:
|
||||
if isinstance(a, Spec):
|
||||
parts += a._parts
|
||||
else:
|
||||
if isinstance(a, str):
|
||||
# Force-cast str subclasses to str (issue #21127)
|
||||
parts.append(str(a))
|
||||
else:
|
||||
raise TypeError(
|
||||
"argument should be a str object or a Spec "
|
||||
"object returning str, not %r"
|
||||
% type(a))
|
||||
return cls.parse_parts(parts)
|
||||
|
||||
@classmethod
|
||||
def parse_parts(cls, parts):
|
||||
parsed = []
|
||||
sep = cls.sep
|
||||
root = ''
|
||||
it = reversed(parts)
|
||||
for part in it:
|
||||
if not part:
|
||||
continue
|
||||
root, rel = cls.splitroot(part)
|
||||
if sep in rel:
|
||||
for x in reversed(rel.split(sep)):
|
||||
if x and x != '.':
|
||||
parsed.append(sys.intern(x))
|
||||
else:
|
||||
if rel and rel != '.':
|
||||
parsed.append(sys.intern(rel))
|
||||
parsed.reverse()
|
||||
return root, parsed
|
||||
|
||||
@classmethod
|
||||
def splitroot(cls, part, sep=sep):
|
||||
if part and part[0] == sep:
|
||||
stripped_part = part.lstrip(sep)
|
||||
# According to POSIX path resolution:
|
||||
# http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11
|
||||
# "A pathname that begins with two successive slashes may be
|
||||
# interpreted in an implementation-defined manner, although more
|
||||
# than two leading slashes shall be treated as a single slash".
|
||||
if len(part) - len(stripped_part) == 2:
|
||||
return sep * 2, stripped_part
|
||||
else:
|
||||
return sep, stripped_part
|
||||
else:
|
||||
return '', part
|
||||
|
||||
@classmethod
|
||||
def _from_parts(cls, args):
|
||||
self = object.__new__(cls)
|
||||
root, parts = cls._parse_args(args)
|
||||
self._root = root
|
||||
self._parts = parts
|
||||
return self
|
||||
|
||||
@classmethod
|
||||
def _from_parsed_parts(cls, root, parts):
|
||||
self = object.__new__(cls)
|
||||
self._root = root
|
||||
self._parts = parts
|
||||
return self
|
||||
|
||||
def join_parsed_parts(self, root, parts, root2, parts2):
|
||||
"""
|
||||
Join the two paths represented by the respective
|
||||
(root, parts) tuples. Return a new (root, parts) tuple.
|
||||
"""
|
||||
if root2:
|
||||
return root2, root2 + parts2[1:]
|
||||
elif parts:
|
||||
return root, parts + parts2
|
||||
return root2, parts2
|
||||
|
||||
def _make_child(self, args):
|
||||
root, parts = self._parse_args(args)
|
||||
root, parts = self.join_parsed_parts(
|
||||
self._root, self._parts, root, parts)
|
||||
return self._from_parsed_parts(root, parts)
|
||||
|
||||
def __truediv__(self, key):
|
||||
return self._make_child((key,))
|
|
@ -1,6 +1,8 @@
|
|||
"""OpenAPI core servers models module"""
|
||||
from six import iteritems
|
||||
|
||||
from openapi_core.schema.servers.utils import is_absolute
|
||||
|
||||
|
||||
class Server(object):
|
||||
|
||||
|
@ -30,7 +32,7 @@ class Server(object):
|
|||
def is_absolute(self, url=None):
|
||||
if url is None:
|
||||
url = self.url
|
||||
return url.startswith('//') or '://' in url
|
||||
return is_absolute(url)
|
||||
|
||||
|
||||
class ServerVariable(object):
|
||||
|
|
2
openapi_core/schema/servers/utils.py
Normal file
2
openapi_core/schema/servers/utils.py
Normal file
|
@ -0,0 +1,2 @@
|
|||
def is_absolute(url):
|
||||
return url.startswith('//') or '://' in url
|
|
@ -3,6 +3,7 @@ from jsonschema.validators import RefResolver
|
|||
from openapi_spec_validator import (
|
||||
default_handlers, openapi_v3_spec_validator,
|
||||
)
|
||||
from openapi_spec_validator.validators import Dereferencer
|
||||
|
||||
from openapi_core.schema.specs.factories import SpecFactory
|
||||
|
||||
|
@ -16,5 +17,8 @@ def create_spec(
|
|||
|
||||
spec_resolver = RefResolver(
|
||||
spec_url, spec_dict, handlers=handlers)
|
||||
dereferencer = Dereferencer(spec_resolver)
|
||||
from openapi_core.spec.paths import SpecPath
|
||||
return SpecPath.from_spec(spec_dict, dereferencer)
|
||||
spec_factory = SpecFactory(spec_resolver)
|
||||
return spec_factory.create(spec_dict, spec_url=spec_url)
|
||||
|
|
|
@ -7,13 +7,14 @@ from openapi_core.security.providers import (
|
|||
class SecurityProviderFactory(object):
|
||||
|
||||
PROVIDERS = {
|
||||
SecuritySchemeType.API_KEY: ApiKeyProvider,
|
||||
SecuritySchemeType.HTTP: HttpProvider,
|
||||
'apiKey': ApiKeyProvider,
|
||||
'http': HttpProvider,
|
||||
}
|
||||
|
||||
def create(self, scheme):
|
||||
if scheme.type == SecuritySchemeType.API_KEY:
|
||||
scheme_type = scheme['type']
|
||||
if scheme_type == 'apiKey':
|
||||
return ApiKeyProvider(scheme)
|
||||
elif scheme.type == SecuritySchemeType.HTTP:
|
||||
elif scheme_type == 'http':
|
||||
return HttpProvider(scheme)
|
||||
return UnsupportedProvider(scheme)
|
||||
|
|
|
@ -18,10 +18,12 @@ class UnsupportedProvider(BaseProvider):
|
|||
class ApiKeyProvider(BaseProvider):
|
||||
|
||||
def __call__(self, request):
|
||||
source = getattr(request.parameters, self.scheme.apikey_in.value)
|
||||
if self.scheme.name not in source:
|
||||
name = self.scheme['name']
|
||||
location = self.scheme['in']
|
||||
source = getattr(request.parameters, location)
|
||||
if name not in source:
|
||||
raise SecurityError("Missing api key parameter.")
|
||||
return source.get(self.scheme.name)
|
||||
return source[name]
|
||||
|
||||
|
||||
class HttpProvider(BaseProvider):
|
||||
|
@ -35,7 +37,8 @@ class HttpProvider(BaseProvider):
|
|||
except ValueError:
|
||||
raise SecurityError('Could not parse authorization header.')
|
||||
|
||||
if auth_type.lower() != self.scheme.scheme.value:
|
||||
scheme = self.scheme['scheme']
|
||||
if auth_type.lower() != scheme:
|
||||
raise SecurityError(
|
||||
'Unknown authorization method %s' % auth_type)
|
||||
|
||||
|
|
0
openapi_core/spec/__init__.py
Normal file
0
openapi_core/spec/__init__.py
Normal file
23
openapi_core/spec/accessors.py
Normal file
23
openapi_core/spec/accessors.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
from contextlib import contextmanager
|
||||
|
||||
from dictpath.accessors import DictOrListAccessor
|
||||
|
||||
|
||||
class SpecAccessor(DictOrListAccessor):
|
||||
|
||||
def __init__(self, dict_or_list, dereferencer):
|
||||
super(SpecAccessor, self).__init__(dict_or_list)
|
||||
self.dereferencer = dereferencer
|
||||
|
||||
@contextmanager
|
||||
def open(self, parts):
|
||||
content = self.dict_or_list
|
||||
for part in parts:
|
||||
content = content[part]
|
||||
if '$ref' in content:
|
||||
content = self.dereferencer.dereference(
|
||||
content)
|
||||
try:
|
||||
yield content
|
||||
finally:
|
||||
pass
|
24
openapi_core/spec/parameters.py
Normal file
24
openapi_core/spec/parameters.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
def get_aslist(param):
|
||||
return (
|
||||
param.get('schema', None) and
|
||||
param['schema']['type'] in ['array', 'object']
|
||||
)
|
||||
|
||||
|
||||
def get_style(param):
|
||||
if 'style' in param:
|
||||
return param['style']
|
||||
|
||||
# determine default
|
||||
return (
|
||||
'simple' if param['in'] in ['path', 'header'] else 'form'
|
||||
)
|
||||
|
||||
|
||||
def get_explode(param):
|
||||
if 'explode' in param:
|
||||
return param['explode']
|
||||
|
||||
#determine default
|
||||
style = get_style(param)
|
||||
return style == 'form'
|
16
openapi_core/spec/paths.py
Normal file
16
openapi_core/spec/paths.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
from dictpath.paths import AccessorPath
|
||||
|
||||
from openapi_core.spec.accessors import SpecAccessor
|
||||
|
||||
SPEC_SEPARATOR = '#'
|
||||
|
||||
|
||||
class SpecPath(AccessorPath):
|
||||
|
||||
@classmethod
|
||||
def from_spec(
|
||||
cls, spec_dict, dereferencer=None, *args,
|
||||
separator=SPEC_SEPARATOR,
|
||||
):
|
||||
accessor = SpecAccessor(spec_dict, dereferencer)
|
||||
return cls(accessor, *args, separator=separator)
|
20
openapi_core/spec/schemas.py
Normal file
20
openapi_core/spec/schemas.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
from six import iteritems
|
||||
|
||||
|
||||
def get_all_properties(schema):
|
||||
properties = schema.get('properties', {})
|
||||
properties_dict = dict(iteritems(properties))
|
||||
|
||||
if 'allOf'not in schema:
|
||||
return properties_dict
|
||||
|
||||
for subschema in schema / 'allOf':
|
||||
subschema_props = get_all_properties(subschema)
|
||||
properties_dict.update(subschema_props)
|
||||
|
||||
return properties_dict
|
||||
|
||||
|
||||
def get_all_properties_names(schema):
|
||||
all_properties = get_all_properties(schema)
|
||||
return set(all_properties.keys())
|
18
openapi_core/spec/servers.py
Normal file
18
openapi_core/spec/servers.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
from six import iteritems
|
||||
|
||||
|
||||
def get_server_default_variables(server):
|
||||
if 'variables' not in server:
|
||||
return {}
|
||||
|
||||
defaults = {}
|
||||
variables = server / 'variables'
|
||||
for name, variable in iteritems(variables):
|
||||
defaults[name] = variable['default']
|
||||
return defaults
|
||||
|
||||
|
||||
def get_server_url(server, **variables):
|
||||
if not variables:
|
||||
variables = get_server_default_variables(server)
|
||||
return server['url'].format(**variables)
|
6
openapi_core/spec/specs.py
Normal file
6
openapi_core/spec/specs.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from openapi_core.spec.servers import get_server_url
|
||||
|
||||
|
||||
def get_spec_url(spec, index=0):
|
||||
servers = spec / 'servers'
|
||||
return get_server_url(servers / 0)
|
|
@ -12,13 +12,11 @@ class MediaTypeFinder(object):
|
|||
self.content = content
|
||||
|
||||
def find(self, request):
|
||||
try:
|
||||
return self.content[request.mimetype]
|
||||
except KeyError:
|
||||
pass
|
||||
if request.mimetype in self.content:
|
||||
return self.content / request.mimetype, request.mimetype
|
||||
|
||||
for key, value in iteritems(self.content):
|
||||
for key, value in self.content.items():
|
||||
if fnmatch.fnmatch(request.mimetype, key):
|
||||
return value
|
||||
return value, key
|
||||
|
||||
raise MediaTypeNotFound(request.mimetype, list(self.content.keys()))
|
||||
|
|
|
@ -3,6 +3,7 @@ from more_itertools import peekable
|
|||
from six import iteritems
|
||||
from six.moves.urllib.parse import urljoin, urlparse
|
||||
|
||||
from openapi_core.schema.servers.utils import is_absolute
|
||||
from openapi_core.templating.datatypes import TemplateResult
|
||||
from openapi_core.templating.util import parse, search
|
||||
from openapi_core.templating.paths.exceptions import (
|
||||
|
@ -40,7 +41,8 @@ class PathFinder(object):
|
|||
|
||||
def _get_paths_iter(self, full_url_pattern):
|
||||
template_paths = []
|
||||
for path_pattern, path in iteritems(self.spec.paths):
|
||||
paths = self.spec / 'paths'
|
||||
for path_pattern, path in paths.items():
|
||||
# simple path.
|
||||
# Return right away since it is always the most concrete
|
||||
if full_url_pattern.endswith(path_pattern):
|
||||
|
@ -59,22 +61,24 @@ class PathFinder(object):
|
|||
|
||||
def _get_operations_iter(self, request_method, paths_iter):
|
||||
for path, path_result in paths_iter:
|
||||
if request_method not in path.operations:
|
||||
if request_method not in path:
|
||||
continue
|
||||
operation = path.operations[request_method]
|
||||
operation = path / request_method
|
||||
yield (path, operation, path_result)
|
||||
|
||||
def _get_servers_iter(self, full_url_pattern, ooperations_iter):
|
||||
for path, operation, path_result in ooperations_iter:
|
||||
servers = path.servers or operation.servers or self.spec.servers
|
||||
servers = path.get('servers', None) or \
|
||||
operation.get('servers', None) or \
|
||||
self.spec.get('servers', [{'url': '/'}])
|
||||
for server in servers:
|
||||
server_url_pattern = full_url_pattern.rsplit(
|
||||
path_result.resolved, 1)[0]
|
||||
server_url = server.url
|
||||
if not server.is_absolute():
|
||||
server_url = server['url']
|
||||
if not is_absolute(server_url):
|
||||
# relative to absolute url
|
||||
if self.base_url is not None:
|
||||
server_url = urljoin(self.base_url, server.url)
|
||||
server_url = urljoin(self.base_url, server['url'])
|
||||
# if no base url check only path part
|
||||
else:
|
||||
server_url_pattern = urlparse(server_url_pattern).path
|
||||
|
@ -82,17 +86,17 @@ class PathFinder(object):
|
|||
server_url = server_url[:-1]
|
||||
# simple path
|
||||
if server_url_pattern == server_url:
|
||||
server_result = TemplateResult(server.url, {})
|
||||
server_result = TemplateResult(server['url'], {})
|
||||
yield (
|
||||
path, operation, server,
|
||||
path_result, server_result,
|
||||
)
|
||||
# template path
|
||||
else:
|
||||
result = parse(server.url, server_url_pattern)
|
||||
result = parse(server['url'], server_url_pattern)
|
||||
if result:
|
||||
server_result = TemplateResult(
|
||||
server.url, result.named)
|
||||
server['url'], result.named)
|
||||
yield (
|
||||
path, operation, server,
|
||||
path_result, server_result,
|
||||
|
|
|
@ -7,17 +7,15 @@ class ResponseFinder(object):
|
|||
self.responses = responses
|
||||
|
||||
def find(self, http_status='default'):
|
||||
try:
|
||||
return self.responses[http_status]
|
||||
except KeyError:
|
||||
pass
|
||||
if http_status in self.responses:
|
||||
return self.responses / http_status
|
||||
|
||||
# try range
|
||||
http_status_range = '{0}XX'.format(http_status[0])
|
||||
if http_status_range in self.responses:
|
||||
return self.responses[http_status_range]
|
||||
return self.responses / http_status_range
|
||||
|
||||
if 'default' not in self.responses:
|
||||
raise ResponseNotFound(http_status, self.responses)
|
||||
|
||||
return self.responses['default']
|
||||
return self.responses / 'default'
|
||||
|
|
|
@ -18,15 +18,15 @@ from openapi_core.unmarshalling.schemas.unmarshallers import (
|
|||
class SchemaUnmarshallersFactory(object):
|
||||
|
||||
PRIMITIVE_UNMARSHALLERS = {
|
||||
SchemaType.STRING: StringUnmarshaller,
|
||||
SchemaType.INTEGER: IntegerUnmarshaller,
|
||||
SchemaType.NUMBER: NumberUnmarshaller,
|
||||
SchemaType.BOOLEAN: BooleanUnmarshaller,
|
||||
'string': StringUnmarshaller,
|
||||
'integer': IntegerUnmarshaller,
|
||||
'number': NumberUnmarshaller,
|
||||
'boolean': BooleanUnmarshaller,
|
||||
}
|
||||
COMPLEX_UNMARSHALLERS = {
|
||||
SchemaType.ARRAY: ArrayUnmarshaller,
|
||||
SchemaType.OBJECT: ObjectUnmarshaller,
|
||||
SchemaType.ANY: AnyUnmarshaller,
|
||||
'array': ArrayUnmarshaller,
|
||||
'object': ObjectUnmarshaller,
|
||||
'any': AnyUnmarshaller,
|
||||
}
|
||||
|
||||
CONTEXT_VALIDATION = {
|
||||
|
@ -46,12 +46,13 @@ class SchemaUnmarshallersFactory(object):
|
|||
|
||||
def create(self, schema, type_override=None):
|
||||
"""Create unmarshaller from the schema."""
|
||||
if not isinstance(schema, Schema):
|
||||
raise TypeError("schema not type of Schema")
|
||||
if schema.deprecated:
|
||||
if schema is None:
|
||||
raise TypeError("Invalid schema")
|
||||
|
||||
if schema.getkey('deprecated', False):
|
||||
warnings.warn("The schema is deprecated", DeprecationWarning)
|
||||
|
||||
schema_type = type_override or schema.type
|
||||
schema_type = type_override or schema.getkey('type', 'any')
|
||||
if schema_type in self.PRIMITIVE_UNMARSHALLERS:
|
||||
klass = self.PRIMITIVE_UNMARSHALLERS[schema_type]
|
||||
kwargs = dict(schema=schema)
|
||||
|
@ -63,10 +64,11 @@ class SchemaUnmarshallersFactory(object):
|
|||
context=self.context,
|
||||
)
|
||||
|
||||
formatter = self.get_formatter(klass.FORMATTERS, schema.format)
|
||||
schema_format = schema.getkey('format')
|
||||
formatter = self.get_formatter(klass.FORMATTERS, schema_format)
|
||||
|
||||
if formatter is None:
|
||||
raise FormatterNotFoundError(schema.format)
|
||||
raise FormatterNotFoundError(schema_format)
|
||||
|
||||
validator = self.get_validator(schema)
|
||||
|
||||
|
@ -87,4 +89,5 @@ class SchemaUnmarshallersFactory(object):
|
|||
}
|
||||
if self.context is not None:
|
||||
kwargs[self.CONTEXT_VALIDATION[self.context]] = True
|
||||
return OAS30Validator(schema.__dict__, **kwargs)
|
||||
with schema.open() as schema_dict:
|
||||
return OAS30Validator(schema_dict, **kwargs)
|
||||
|
|
|
@ -15,6 +15,9 @@ from openapi_core.extensions.models.factories import ModelFactory
|
|||
from openapi_core.schema.schemas.enums import SchemaFormat, SchemaType
|
||||
from openapi_core.schema.schemas.models import Schema
|
||||
from openapi_core.schema.schemas.types import NoValue
|
||||
from openapi_core.spec.schemas import (
|
||||
get_all_properties, get_all_properties_names
|
||||
)
|
||||
from openapi_core.unmarshalling.schemas.enums import UnmarshalContext
|
||||
from openapi_core.unmarshalling.schemas.exceptions import (
|
||||
UnmarshalError, ValidateError, InvalidSchemaValue,
|
||||
|
@ -40,7 +43,7 @@ class PrimitiveTypeUnmarshaller(object):
|
|||
|
||||
def __call__(self, value=NoValue):
|
||||
if value is NoValue:
|
||||
value = self.schema.default
|
||||
value = self.schema.getkey('default')
|
||||
if value is None:
|
||||
return
|
||||
|
||||
|
@ -51,21 +54,24 @@ class PrimitiveTypeUnmarshaller(object):
|
|||
def _formatter_validate(self, value):
|
||||
result = self.formatter.validate(value)
|
||||
if not result:
|
||||
raise InvalidSchemaValue(value, self.schema.type)
|
||||
schema_type = self.schema.getkey('type', 'any')
|
||||
raise InvalidSchemaValue(value, schema_type)
|
||||
|
||||
def validate(self, value):
|
||||
errors_iter = self.validator.iter_errors(value)
|
||||
errors = tuple(errors_iter)
|
||||
if errors:
|
||||
schema_type = self.schema.getkey('type', 'any')
|
||||
raise InvalidSchemaValue(
|
||||
value, self.schema.type, schema_errors=errors)
|
||||
value, schema_type, schema_errors=errors)
|
||||
|
||||
def unmarshal(self, value):
|
||||
try:
|
||||
return self.formatter.unmarshal(value)
|
||||
except ValueError as exc:
|
||||
schema_format = self.schema.getkey('format')
|
||||
raise InvalidSchemaFormatValue(
|
||||
value, self.schema.format, exc)
|
||||
value, schema_format, exc)
|
||||
|
||||
|
||||
class StringUnmarshaller(PrimitiveTypeUnmarshaller):
|
||||
|
@ -140,11 +146,11 @@ class ArrayUnmarshaller(ComplexUnmarshaller):
|
|||
|
||||
@property
|
||||
def items_unmarshaller(self):
|
||||
return self.unmarshallers_factory.create(self.schema.items)
|
||||
return self.unmarshallers_factory.create(self.schema / 'items')
|
||||
|
||||
def __call__(self, value=NoValue):
|
||||
value = super(ArrayUnmarshaller, self).__call__(value)
|
||||
if value is None and self.schema.nullable:
|
||||
if value is None and self.schema.getkey('nullable', False):
|
||||
return None
|
||||
return list(map(self.items_unmarshaller, value))
|
||||
|
||||
|
@ -170,9 +176,9 @@ class ObjectUnmarshaller(ComplexUnmarshaller):
|
|||
return self._unmarshal_object(value)
|
||||
|
||||
def _unmarshal_object(self, value=NoValue):
|
||||
if self.schema.one_of:
|
||||
if 'oneOf' in self.schema:
|
||||
properties = None
|
||||
for one_of_schema in self.schema.one_of:
|
||||
for one_of_schema in self.schema / 'oneOf':
|
||||
try:
|
||||
unmarshalled = self._unmarshal_properties(
|
||||
value, one_of_schema)
|
||||
|
@ -190,46 +196,49 @@ class ObjectUnmarshaller(ComplexUnmarshaller):
|
|||
else:
|
||||
properties = self._unmarshal_properties(value)
|
||||
|
||||
if 'x-model' in self.schema.extensions:
|
||||
extension = self.schema.extensions['x-model']
|
||||
return self.model_factory.create(properties, name=extension.value)
|
||||
if 'x-model' in self.schema:
|
||||
name = self.schema['x-model']
|
||||
return self.model_factory.create(properties, name=name)
|
||||
|
||||
return properties
|
||||
|
||||
def _unmarshal_properties(self, value=NoValue, one_of_schema=None):
|
||||
all_props = self.schema.get_all_properties()
|
||||
all_props_names = self.schema.get_all_properties_names()
|
||||
all_props = get_all_properties(self.schema)
|
||||
all_props_names = get_all_properties_names(self.schema)
|
||||
|
||||
if one_of_schema is not None:
|
||||
all_props.update(one_of_schema.get_all_properties())
|
||||
all_props_names |= one_of_schema.\
|
||||
get_all_properties_names()
|
||||
all_props.update(get_all_properties(one_of_schema))
|
||||
all_props_names |= get_all_properties_names(one_of_schema)
|
||||
|
||||
value_props_names = value.keys()
|
||||
extra_props = set(value_props_names) - set(all_props_names)
|
||||
|
||||
properties = {}
|
||||
if isinstance(self.schema.additional_properties, Schema):
|
||||
additional_properties = self.schema.getkey('additionalProperties', True)
|
||||
if isinstance(additional_properties, dict):
|
||||
additional_prop_schema = self.schema / 'additionalProperties'
|
||||
for prop_name in extra_props:
|
||||
prop_value = value[prop_name]
|
||||
properties[prop_name] = self.unmarshallers_factory.create(
|
||||
self.schema.additional_properties)(prop_value)
|
||||
elif self.schema.additional_properties is True:
|
||||
additional_prop_schema)(prop_value)
|
||||
elif additional_properties is True:
|
||||
for prop_name in extra_props:
|
||||
prop_value = value[prop_name]
|
||||
properties[prop_name] = prop_value
|
||||
|
||||
for prop_name, prop in iteritems(all_props):
|
||||
if self.context == UnmarshalContext.REQUEST and prop.read_only:
|
||||
read_only = prop.getkey('readOnly', False)
|
||||
if self.context == UnmarshalContext.REQUEST and read_only:
|
||||
continue
|
||||
if self.context == UnmarshalContext.RESPONSE and prop.write_only:
|
||||
write_only = prop.getkey('writeOnly', False)
|
||||
if self.context == UnmarshalContext.RESPONSE and write_only:
|
||||
continue
|
||||
try:
|
||||
prop_value = value[prop_name]
|
||||
except KeyError:
|
||||
if prop.default is NoValue:
|
||||
if 'default' not in prop:
|
||||
continue
|
||||
prop_value = prop.default
|
||||
prop_value = prop['default']
|
||||
|
||||
properties[prop_name] = self.unmarshallers_factory.create(
|
||||
prop)(prop_value)
|
||||
|
@ -244,8 +253,8 @@ class AnyUnmarshaller(ComplexUnmarshaller):
|
|||
}
|
||||
|
||||
SCHEMA_TYPES_ORDER = [
|
||||
SchemaType.OBJECT, SchemaType.ARRAY, SchemaType.BOOLEAN,
|
||||
SchemaType.INTEGER, SchemaType.NUMBER, SchemaType.STRING,
|
||||
'object', 'array', 'boolean',
|
||||
'integer', 'number', 'string',
|
||||
]
|
||||
|
||||
def unmarshal(self, value=NoValue):
|
||||
|
@ -272,9 +281,11 @@ class AnyUnmarshaller(ComplexUnmarshaller):
|
|||
return value
|
||||
|
||||
def _get_one_of_schema(self, value):
|
||||
if not self.schema.one_of:
|
||||
if 'oneOf' not in self.schema:
|
||||
return
|
||||
for subschema in self.schema.one_of:
|
||||
|
||||
one_of_schemas = self.schema / 'oneOf'
|
||||
for subschema in one_of_schemas:
|
||||
unmarshaller = self.unmarshallers_factory.create(subschema)
|
||||
try:
|
||||
unmarshaller.validate(value)
|
||||
|
@ -284,10 +295,12 @@ class AnyUnmarshaller(ComplexUnmarshaller):
|
|||
return subschema
|
||||
|
||||
def _get_all_of_schema(self, value):
|
||||
if not self.schema.all_of:
|
||||
if 'allOf' not in self.schema:
|
||||
return
|
||||
for subschema in self.schema.all_of:
|
||||
if subschema.type == SchemaType.ANY:
|
||||
|
||||
all_of_schemas = self.schema / 'allOf'
|
||||
for subschema in all_of_schemas:
|
||||
if 'type' not in subschema:
|
||||
continue
|
||||
unmarshaller = self.unmarshallers_factory.create(subschema)
|
||||
try:
|
||||
|
|
|
@ -9,6 +9,7 @@ from openapi_core.schema.parameters.exceptions import (
|
|||
)
|
||||
from openapi_core.schema.request_bodies.exceptions import MissingRequestBody
|
||||
from openapi_core.security.exceptions import SecurityError
|
||||
from openapi_core.spec.parameters import get_aslist, get_explode
|
||||
from openapi_core.templating.media_types.exceptions import MediaTypeFinderError
|
||||
from openapi_core.templating.paths.exceptions import PathError
|
||||
from openapi_core.unmarshalling.schemas.enums import UnmarshalContext
|
||||
|
@ -38,10 +39,17 @@ class RequestValidator(BaseValidator):
|
|||
|
||||
request.parameters.path = request.parameters.path or \
|
||||
path_result.variables
|
||||
|
||||
operation_params = operation.get('parameters', [])
|
||||
operation_params_iter = operation_params and \
|
||||
iter(operation_params) or []
|
||||
path_params = path.get('parameters', [])
|
||||
params_params_iter = path_params and \
|
||||
iter(path_params) or []
|
||||
params, params_errors = self._get_parameters(
|
||||
request, chain(
|
||||
iteritems(operation.parameters),
|
||||
iteritems(path.parameters)
|
||||
operation_params_iter,
|
||||
params_params_iter,
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -63,10 +71,17 @@ class RequestValidator(BaseValidator):
|
|||
|
||||
request.parameters.path = request.parameters.path or \
|
||||
path_result.variables
|
||||
|
||||
operation_params = operation.get('parameters', [])
|
||||
operation_params_iter = operation_params and \
|
||||
iter(operation_params) or []
|
||||
path_params = path.get('parameters', [])
|
||||
params_params_iter = path_params and \
|
||||
iter(path_params) or []
|
||||
params, params_errors = self._get_parameters(
|
||||
request, chain(
|
||||
iteritems(operation.parameters),
|
||||
iteritems(path.parameters)
|
||||
operation_params_iter,
|
||||
params_params_iter,
|
||||
)
|
||||
)
|
||||
return RequestValidationResult(
|
||||
|
@ -87,9 +102,11 @@ class RequestValidator(BaseValidator):
|
|||
)
|
||||
|
||||
def _get_security(self, request, operation):
|
||||
security = self.spec.security
|
||||
if operation.security is not None:
|
||||
security = operation.security
|
||||
security = None
|
||||
if 'security' in self.spec:
|
||||
security = self.spec / 'security'
|
||||
if 'security' in operation:
|
||||
security = operation / 'security'
|
||||
|
||||
if not security:
|
||||
return {}
|
||||
|
@ -99,7 +116,7 @@ class RequestValidator(BaseValidator):
|
|||
return {
|
||||
scheme_name: self._get_security_value(
|
||||
scheme_name, request)
|
||||
for scheme_name in security_requirement
|
||||
for scheme_name in security_requirement.keys()
|
||||
}
|
||||
except SecurityError:
|
||||
continue
|
||||
|
@ -110,21 +127,26 @@ class RequestValidator(BaseValidator):
|
|||
errors = []
|
||||
seen = set()
|
||||
locations = {}
|
||||
for param_name, param in params:
|
||||
if (param_name, param.location.value) in seen:
|
||||
for param in params:
|
||||
param_name = param['name']
|
||||
param_location = param['in']
|
||||
if (param_name, param_location) in seen:
|
||||
# skip parameter already seen
|
||||
# e.g. overriden path item paremeter on operation
|
||||
continue
|
||||
seen.add((param_name, param.location.value))
|
||||
seen.add((param_name, param_location))
|
||||
try:
|
||||
raw_value = self._get_parameter_value(param, request)
|
||||
except MissingRequiredParameter as exc:
|
||||
errors.append(exc)
|
||||
continue
|
||||
except MissingParameter:
|
||||
if not param.schema or not param.schema.has_default():
|
||||
if 'schema' not in param:
|
||||
continue
|
||||
casted = param.schema.default
|
||||
schema = param / 'schema'
|
||||
if 'default' not in schema:
|
||||
continue
|
||||
casted = schema['default']
|
||||
else:
|
||||
try:
|
||||
deserialised = self._deserialise_parameter(
|
||||
|
@ -144,28 +166,29 @@ class RequestValidator(BaseValidator):
|
|||
except (ValidateError, UnmarshalError) as exc:
|
||||
errors.append(exc)
|
||||
else:
|
||||
locations.setdefault(param.location.value, {})
|
||||
locations[param.location.value][param_name] = unmarshalled
|
||||
locations.setdefault(param_location, {})
|
||||
locations[param_location][param_name] = unmarshalled
|
||||
|
||||
return RequestParameters(**locations), errors
|
||||
|
||||
def _get_body(self, request, operation):
|
||||
if operation.request_body is None:
|
||||
if not 'requestBody' in operation:
|
||||
return None, []
|
||||
|
||||
request_body = operation / 'requestBody'
|
||||
try:
|
||||
media_type = self._get_media_type(
|
||||
operation.request_body.content, request)
|
||||
media_type, mimetype = self._get_media_type(
|
||||
request_body / 'content', request)
|
||||
except MediaTypeFinderError as exc:
|
||||
return None, [exc, ]
|
||||
|
||||
try:
|
||||
raw_body = self._get_body_value(operation.request_body, request)
|
||||
raw_body = self._get_body_value(request_body, request)
|
||||
except MissingRequestBody as exc:
|
||||
return None, [exc, ]
|
||||
|
||||
try:
|
||||
deserialised = self._deserialise_media_type(media_type, raw_body)
|
||||
deserialised = self._deserialise_data(mimetype, raw_body)
|
||||
except DeserializeError as exc:
|
||||
return None, [exc, ]
|
||||
|
||||
|
@ -182,33 +205,37 @@ class RequestValidator(BaseValidator):
|
|||
return body, []
|
||||
|
||||
def _get_security_value(self, scheme_name, request):
|
||||
scheme = self.spec.components.security_schemes.get(scheme_name)
|
||||
if not scheme:
|
||||
security_schemes = self.spec / 'components#securitySchemes'
|
||||
if scheme_name not in security_schemes:
|
||||
return
|
||||
|
||||
scheme = security_schemes[scheme_name]
|
||||
from openapi_core.security.factories import SecurityProviderFactory
|
||||
security_provider_factory = SecurityProviderFactory()
|
||||
security_provider = security_provider_factory.create(scheme)
|
||||
return security_provider(request)
|
||||
|
||||
def _get_parameter_value(self, param, request):
|
||||
location = request.parameters[param.location.value]
|
||||
param_location = param['in']
|
||||
location = request.parameters[param_location]
|
||||
|
||||
if param.name not in location:
|
||||
if param.required:
|
||||
raise MissingRequiredParameter(param.name)
|
||||
if param['name'] not in location:
|
||||
if param.getkey('required', False):
|
||||
raise MissingRequiredParameter(param['name'])
|
||||
|
||||
raise MissingParameter(param.name)
|
||||
raise MissingParameter(param['name'])
|
||||
|
||||
if param.aslist and param.explode:
|
||||
aslist = get_aslist(param)
|
||||
explode = get_explode(param)
|
||||
if aslist and explode:
|
||||
if hasattr(location, 'getall'):
|
||||
return location.getall(param.name)
|
||||
return location.getlist(param.name)
|
||||
return location.getall(param['name'])
|
||||
return location.getlist(param['name'])
|
||||
|
||||
return location[param.name]
|
||||
return location[param['name']]
|
||||
|
||||
def _get_body_value(self, request_body, request):
|
||||
if not request.body and request_body.required:
|
||||
required = request_body.getkey('required', False)
|
||||
if not request.body and required:
|
||||
raise MissingRequestBody(request)
|
||||
return request.body
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ class ResponseValidator(BaseValidator):
|
|||
|
||||
def _get_operation_response(self, operation, response):
|
||||
from openapi_core.templating.responses.finders import ResponseFinder
|
||||
finder = ResponseFinder(operation.responses)
|
||||
finder = ResponseFinder(operation / 'responses')
|
||||
return finder.find(str(response.status_code))
|
||||
|
||||
def _validate_data(self, request, response):
|
||||
|
@ -67,12 +67,12 @@ class ResponseValidator(BaseValidator):
|
|||
)
|
||||
|
||||
def _get_data(self, response, operation_response):
|
||||
if not operation_response.content:
|
||||
if 'content' not in operation_response:
|
||||
return None, []
|
||||
|
||||
try:
|
||||
media_type = self._get_media_type(
|
||||
operation_response.content, response)
|
||||
media_type, mimetype = self._get_media_type(
|
||||
operation_response / 'content', response)
|
||||
except MediaTypeFinderError as exc:
|
||||
return None, [exc, ]
|
||||
|
||||
|
@ -82,7 +82,7 @@ class ResponseValidator(BaseValidator):
|
|||
return None, [exc, ]
|
||||
|
||||
try:
|
||||
deserialised = self._deserialise_media_type(media_type, raw_data)
|
||||
deserialised = self._deserialise_data(mimetype, raw_data)
|
||||
except DeserializeError as exc:
|
||||
return None, [exc, ]
|
||||
|
||||
|
|
|
@ -26,36 +26,38 @@ class BaseValidator(object):
|
|||
finder = MediaTypeFinder(content)
|
||||
return finder.find(request_or_response)
|
||||
|
||||
def _deserialise_media_type(self, media_type, value):
|
||||
def _deserialise_data(self, mimetype, value):
|
||||
from openapi_core.deserializing.media_types.factories import (
|
||||
MediaTypeDeserializersFactory,
|
||||
)
|
||||
deserializers_factory = MediaTypeDeserializersFactory(
|
||||
self.custom_media_type_deserializers)
|
||||
deserializer = deserializers_factory.create(media_type)
|
||||
deserializer = deserializers_factory.create(mimetype)
|
||||
return deserializer(value)
|
||||
|
||||
def _cast(self, param_or_media_type, value):
|
||||
# return param_or_media_type.cast(value)
|
||||
if not param_or_media_type.schema:
|
||||
if not 'schema' in param_or_media_type:
|
||||
return value
|
||||
|
||||
from openapi_core.casting.schemas.factories import SchemaCastersFactory
|
||||
casters_factory = SchemaCastersFactory()
|
||||
caster = casters_factory.create(param_or_media_type.schema)
|
||||
schema = param_or_media_type / 'schema'
|
||||
caster = casters_factory.create(schema)
|
||||
return caster(value)
|
||||
|
||||
def _unmarshal(self, param_or_media_type, value, context):
|
||||
if not param_or_media_type.schema:
|
||||
if not 'schema' in param_or_media_type:
|
||||
return value
|
||||
|
||||
from openapi_core.unmarshalling.schemas.factories import (
|
||||
SchemaUnmarshallersFactory,
|
||||
)
|
||||
spec_resolver = self.spec.accessor.dereferencer.resolver_manager.resolver
|
||||
unmarshallers_factory = SchemaUnmarshallersFactory(
|
||||
self.spec._resolver, self.format_checker,
|
||||
spec_resolver, self.format_checker,
|
||||
self.custom_formatters, context=context,
|
||||
)
|
||||
unmarshaller = unmarshallers_factory.create(
|
||||
param_or_media_type.schema)
|
||||
schema = param_or_media_type / 'schema'
|
||||
unmarshaller = unmarshallers_factory.create(schema)
|
||||
return unmarshaller(value)
|
||||
|
|
|
@ -38,6 +38,7 @@ paths:
|
|||
default: 1
|
||||
- name: limit
|
||||
in: query
|
||||
style: form
|
||||
description: How many items to return at one time (max 100)
|
||||
required: true
|
||||
schema:
|
||||
|
|
|
@ -6,31 +6,32 @@ class TestLinkSpec(object):
|
|||
def test_no_param(self, factory):
|
||||
spec_dict = factory.spec_from_file("data/v3.0/links.yaml")
|
||||
spec = create_spec(spec_dict)
|
||||
resp = spec['/status']['get'].responses['default']
|
||||
resp = spec / 'paths#/status#get#responses#default'
|
||||
|
||||
assert len(resp.links) == 1
|
||||
links = resp / 'links'
|
||||
assert len(links) == 1
|
||||
|
||||
link = resp.links['noParamLink']
|
||||
|
||||
assert link.operationId == 'noParOp'
|
||||
assert link.server is None
|
||||
assert link.request_body is None
|
||||
assert len(link.parameters) == 0
|
||||
link = links / 'noParamLink'
|
||||
assert link['operationId'] == 'noParOp'
|
||||
assert 'server' not in link
|
||||
assert 'requestBody' not in link
|
||||
assert 'parameters' not in link
|
||||
|
||||
def test_param(self, factory):
|
||||
spec_dict = factory.spec_from_file("data/v3.0/links.yaml")
|
||||
spec = create_spec(spec_dict)
|
||||
resp = spec['/status/{resourceId}']['get'].responses['default']
|
||||
resp = spec / 'paths#/status/{resourceId}#get#responses#default'
|
||||
|
||||
assert len(resp.links) == 1
|
||||
links = resp / 'links'
|
||||
assert len(links) == 1
|
||||
|
||||
link = resp.links['paramLink']
|
||||
link = links / 'paramLink'
|
||||
assert link['operationId'] == 'paramOp'
|
||||
assert 'server' not in link
|
||||
assert link['requestBody'] == 'test'
|
||||
|
||||
assert link.operationId == 'paramOp'
|
||||
assert link.server is None
|
||||
assert link.request_body == 'test'
|
||||
assert len(link.parameters) == 1
|
||||
|
||||
param = link.parameters['opParam']
|
||||
parameters = link['parameters']
|
||||
assert len(parameters) == 1
|
||||
|
||||
param = parameters['opParam']
|
||||
assert param == '$request.path.resourceId'
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import pytest
|
||||
|
||||
from openapi_core.schema.parameters.enums import ParameterLocation
|
||||
from openapi_core.shortcuts import create_spec
|
||||
|
||||
|
||||
|
@ -15,9 +14,12 @@ class TestMinimal(object):
|
|||
spec_dict = factory.spec_from_file(spec_path)
|
||||
spec = create_spec(spec_dict)
|
||||
|
||||
path = spec['/resource/{resId}']
|
||||
path = spec / 'paths#/resource/{resId}'
|
||||
|
||||
assert len(path.parameters) == 1
|
||||
param = path.parameters['resId']
|
||||
assert param.required
|
||||
assert param.location == ParameterLocation.PATH
|
||||
parameters = path / 'parameters'
|
||||
assert len(parameters) == 1
|
||||
|
||||
param = parameters[0]
|
||||
assert param['name'] == 'resId'
|
||||
assert param['required']
|
||||
assert param['in'] == 'path'
|
||||
|
|
|
@ -2,18 +2,9 @@ import pytest
|
|||
from base64 import b64encode
|
||||
from six import iteritems, text_type
|
||||
|
||||
from openapi_core.schema.media_types.models import MediaType
|
||||
from openapi_core.schema.operations.models import Operation
|
||||
from openapi_core.schema.parameters.models import Parameter
|
||||
from openapi_core.schema.paths.models import Path
|
||||
from openapi_core.schema.request_bodies.models import RequestBody
|
||||
from openapi_core.schema.responses.models import Response
|
||||
from openapi_core.schema.schemas.models import Schema
|
||||
from openapi_core.schema.security_requirements.models import (
|
||||
SecurityRequirement,
|
||||
)
|
||||
from openapi_core.schema.servers.models import Server, ServerVariable
|
||||
from openapi_core.shortcuts import create_spec
|
||||
from openapi_core.spec.servers import get_server_url
|
||||
from openapi_core.spec.specs import get_spec_url
|
||||
from openapi_core.validation.request.validators import RequestValidator
|
||||
from openapi_core.validation.response.validators import ResponseValidator
|
||||
|
||||
|
@ -51,123 +42,117 @@ class TestPetstore(object):
|
|||
def test_spec(self, spec, spec_dict):
|
||||
url = 'http://petstore.swagger.io/v1'
|
||||
|
||||
info = spec / 'info'
|
||||
info_spec = spec_dict['info']
|
||||
assert spec.info.title == info_spec['title']
|
||||
assert spec.info.description == info_spec['description']
|
||||
assert spec.info.terms_of_service == info_spec['termsOfService']
|
||||
assert spec.info.version == info_spec['version']
|
||||
assert info['title'] == info_spec['title']
|
||||
assert info['description'] == info_spec['description']
|
||||
assert info['termsOfService'] == info_spec['termsOfService']
|
||||
assert info['version'] == info_spec['version']
|
||||
|
||||
contact = info / 'contact'
|
||||
contact_spec = info_spec['contact']
|
||||
assert spec.info.contact.name == contact_spec['name']
|
||||
assert spec.info.contact.url == contact_spec['url']
|
||||
assert spec.info.contact.email == contact_spec['email']
|
||||
assert contact['name'] == contact_spec['name']
|
||||
assert contact['url'] == contact_spec['url']
|
||||
assert contact['email'] == contact_spec['email']
|
||||
|
||||
license = info / 'license'
|
||||
license_spec = info_spec['license']
|
||||
assert spec.info.license.name == license_spec['name']
|
||||
assert spec.info.license.url == license_spec['url']
|
||||
assert license['name'] == license_spec['name']
|
||||
assert license['url'] == license_spec['url']
|
||||
|
||||
security = spec / 'security'
|
||||
security_spec = spec_dict.get('security', [])
|
||||
for idx, security_req in enumerate(spec.security):
|
||||
assert type(security_req) == SecurityRequirement
|
||||
for idx, security_reqs in enumerate(security):
|
||||
security_reqs_spec = security_spec[idx]
|
||||
for scheme_name, security_req in iteritems(security_reqs):
|
||||
security_req == security_reqs_spec[scheme_name]
|
||||
|
||||
security_req_spec = security_spec[idx]
|
||||
for scheme_name in security_req:
|
||||
security_req[scheme_name] == security_req_spec[scheme_name]
|
||||
|
||||
assert spec.get_server_url() == url
|
||||
|
||||
for idx, server in enumerate(spec.servers):
|
||||
assert type(server) == Server
|
||||
assert get_spec_url(spec) == url
|
||||
|
||||
servers = spec / 'servers'
|
||||
for idx, server in enumerate(servers):
|
||||
server_spec = spec_dict['servers'][idx]
|
||||
assert server.url == server_spec['url']
|
||||
assert server.default_url == url
|
||||
|
||||
for variable_name, variable in iteritems(server.variables):
|
||||
assert type(variable) == ServerVariable
|
||||
assert variable.name == variable_name
|
||||
assert server['url'] == server_spec['url']
|
||||
assert get_server_url(server) == url
|
||||
|
||||
variables = server / 'variables'
|
||||
for variable_name, variable in iteritems(variables):
|
||||
variable_spec = server_spec['variables'][variable_name]
|
||||
assert variable.default == variable_spec['default']
|
||||
assert variable.enum == variable_spec.get('enum')
|
||||
|
||||
for path_name, path in iteritems(spec.paths):
|
||||
assert type(path) == Path
|
||||
assert variable['default'] == variable_spec['default']
|
||||
assert variable['enum'] == variable_spec.get('enum')
|
||||
|
||||
paths = spec / 'paths'
|
||||
for path_name, path in iteritems(paths):
|
||||
path_spec = spec_dict['paths'][path_name]
|
||||
assert path.name == path_name
|
||||
assert path.summary == path_spec.get('summary')
|
||||
assert path.description == path_spec.get('description')
|
||||
assert path.getkey('summary') == path_spec.get('summary')
|
||||
assert path.getkey('description') == path_spec.get('description')
|
||||
|
||||
servers = path.get('servers', [])
|
||||
servers_spec = path_spec.get('servers', [])
|
||||
for idx, server in enumerate(path.servers):
|
||||
assert type(server) == Server
|
||||
|
||||
for idx, server in enumerate(servers):
|
||||
server_spec = servers_spec[idx]
|
||||
assert server.url == server_spec['url']
|
||||
assert server.default_url == server_spec['url']
|
||||
assert server.description == server_spec.get('description')
|
||||
|
||||
for variable_name, variable in iteritems(server.variables):
|
||||
assert type(variable) == ServerVariable
|
||||
assert variable.name == variable_name
|
||||
|
||||
variables = server.get('variables', {})
|
||||
for variable_name, variable in iteritems(variables):
|
||||
variable_spec = server_spec['variables'][variable_name]
|
||||
assert variable.default == variable_spec['default']
|
||||
assert variable.enum == variable_spec.get('enum')
|
||||
assert variable['default'] == variable_spec['default']
|
||||
assert variable.getkey('enum') == variable_spec.get('enum')
|
||||
|
||||
for http_method, operation in iteritems(path.operations):
|
||||
operations = [
|
||||
'get', 'put', 'post', 'delete', 'options',
|
||||
'head', 'patch', 'trace',
|
||||
]
|
||||
for http_method in operations:
|
||||
if http_method not in path:
|
||||
continue
|
||||
operation = path / http_method
|
||||
operation_spec = path_spec[http_method]
|
||||
|
||||
assert type(operation) == Operation
|
||||
assert operation.path_name == path_name
|
||||
assert operation.http_method == http_method
|
||||
assert operation.operation_id is not None
|
||||
assert operation.tags == operation_spec['tags']
|
||||
assert operation.summary == operation_spec.get('summary')
|
||||
assert operation.description == operation_spec.get(
|
||||
assert operation['operationId'] is not None
|
||||
assert operation['tags'] == operation_spec['tags']
|
||||
assert operation['summary'] == operation_spec.get('summary')
|
||||
assert operation.getkey('description') == operation_spec.get(
|
||||
'description')
|
||||
|
||||
ext_docs = operation.get('externalDocs')
|
||||
ext_docs_spec = operation_spec.get('externalDocs')
|
||||
assert bool(ext_docs_spec) == bool(ext_docs)
|
||||
if ext_docs_spec:
|
||||
ext_docs = operation.external_docs
|
||||
assert ext_docs.url == ext_docs_spec['url']
|
||||
assert ext_docs.description == ext_docs_spec.get(
|
||||
assert ext_docs['url'] == ext_docs_spec['url']
|
||||
assert ext_docs.getkey('description') == ext_docs_spec.get(
|
||||
'description')
|
||||
|
||||
servers = operation.get('servers', [])
|
||||
servers_spec = operation_spec.get('servers', [])
|
||||
for idx, server in enumerate(operation.servers):
|
||||
assert type(server) == Server
|
||||
|
||||
for idx, server in enumerate(servers):
|
||||
server_spec = servers_spec[idx]
|
||||
assert server.url == server_spec['url']
|
||||
assert server.default_url == server_spec['url']
|
||||
assert server.description == server_spec.get('description')
|
||||
|
||||
for variable_name, variable in iteritems(server.variables):
|
||||
assert type(variable) == ServerVariable
|
||||
assert variable.name == variable_name
|
||||
assert server['url'] == server_spec['url']
|
||||
assert get_server_url(server) == server_spec['url']
|
||||
assert server['description'] == server_spec.get(
|
||||
'description')
|
||||
|
||||
variables = server.get('variables', {})
|
||||
for variable_name, variable in iteritems(variables):
|
||||
variable_spec = server_spec['variables'][variable_name]
|
||||
assert variable.default == variable_spec['default']
|
||||
assert variable.enum == variable_spec.get('enum')
|
||||
assert variable['default'] == variable_spec['default']
|
||||
assert variable.getkey('enum') == variable_spec.get(
|
||||
'enum')
|
||||
|
||||
security = operation.get('security', [])
|
||||
security_spec = operation_spec.get('security')
|
||||
if security_spec is not None:
|
||||
for idx, security_req in enumerate(operation.security):
|
||||
assert type(security_req) == SecurityRequirement
|
||||
|
||||
security_req_spec = security_spec[idx]
|
||||
for scheme_name in security_req:
|
||||
security_req[scheme_name] == security_req_spec[
|
||||
scheme_name]
|
||||
for idx, security_reqs in enumerate(security):
|
||||
security_reqs_spec = security_spec[idx]
|
||||
for scheme_name, security_req in iteritems(
|
||||
security_reqs):
|
||||
security_req == security_reqs_spec[scheme_name]
|
||||
|
||||
responses = operation / 'responses'
|
||||
responses_spec = operation_spec.get('responses')
|
||||
|
||||
for http_status, response in iteritems(operation.responses):
|
||||
assert type(response) == Response
|
||||
assert response.http_status == http_status
|
||||
|
||||
for http_status, response in iteritems(responses):
|
||||
response_spec = responses_spec[http_status]
|
||||
|
||||
if not response_spec:
|
||||
|
@ -179,17 +164,16 @@ class TestPetstore(object):
|
|||
|
||||
description_spec = response_spec['description']
|
||||
|
||||
assert response.description == description_spec
|
||||
|
||||
for parameter_name, parameter in iteritems(
|
||||
response.headers):
|
||||
assert type(parameter) == Parameter
|
||||
assert parameter.name == parameter_name
|
||||
assert response.getkey('description') == description_spec
|
||||
|
||||
headers = response.get('headers', {})
|
||||
for parameter_name, parameter in iteritems(headers):
|
||||
headers_spec = response_spec['headers']
|
||||
parameter_spec = headers_spec[parameter_name]
|
||||
|
||||
schema = parameter.get('schema')
|
||||
schema_spec = parameter_spec.get('schema')
|
||||
assert bool(schema_spec) == bool(parameter.schema)
|
||||
assert bool(schema_spec) == bool(schema)
|
||||
|
||||
if not schema_spec:
|
||||
continue
|
||||
|
@ -198,13 +182,12 @@ class TestPetstore(object):
|
|||
if '$ref' in schema_spec:
|
||||
continue
|
||||
|
||||
assert type(parameter.schema) == Schema
|
||||
assert parameter.schema.type.value ==\
|
||||
assert schema['type'] ==\
|
||||
schema_spec['type']
|
||||
assert parameter.schema.format ==\
|
||||
assert schema.getkey('format') ==\
|
||||
schema_spec.get('format')
|
||||
assert parameter.schema.required == schema_spec.get(
|
||||
'required', [])
|
||||
assert schema.getkey('required') == schema_spec.get(
|
||||
'required')
|
||||
|
||||
content_spec = parameter_spec.get('content')
|
||||
assert bool(content_spec) == bool(parameter.content)
|
||||
|
@ -212,14 +195,12 @@ class TestPetstore(object):
|
|||
if not content_spec:
|
||||
continue
|
||||
|
||||
for mimetype, media_type in iteritems(
|
||||
parameter.content):
|
||||
assert type(media_type) == MediaType
|
||||
assert media_type.mimetype == mimetype
|
||||
|
||||
content = parameter.get('content', {})
|
||||
for mimetype, media_type in iteritems(content):
|
||||
media_spec = parameter_spec['content'][mimetype]
|
||||
schema = media_type.get('schema')
|
||||
schema_spec = media_spec.get('schema')
|
||||
assert bool(schema_spec) == bool(media_type.schema)
|
||||
assert bool(schema_spec) == bool(schema)
|
||||
|
||||
if not schema_spec:
|
||||
continue
|
||||
|
@ -228,30 +209,28 @@ class TestPetstore(object):
|
|||
if '$ref' in schema_spec:
|
||||
continue
|
||||
|
||||
assert type(media_type.schema) == Schema
|
||||
assert media_type.schema.type.value ==\
|
||||
assert schema['type'] ==\
|
||||
schema_spec['type']
|
||||
assert media_type.schema.format ==\
|
||||
assert schema.getkey('format') ==\
|
||||
schema_spec.get('format')
|
||||
assert media_type.schema.required == \
|
||||
schema_spec.get('required', False)
|
||||
assert schema.getkey('required') == \
|
||||
schema_spec.get('required')
|
||||
|
||||
content_spec = response_spec.get('content')
|
||||
|
||||
if not content_spec:
|
||||
continue
|
||||
|
||||
for mimetype, media_type in iteritems(response.content):
|
||||
assert type(media_type) == MediaType
|
||||
assert media_type.mimetype == mimetype
|
||||
|
||||
content = response.get('content', {})
|
||||
for mimetype, media_type in iteritems(content):
|
||||
content_spec = response_spec['content'][mimetype]
|
||||
|
||||
example_spec = content_spec.get('example')
|
||||
assert media_type.example == example_spec
|
||||
assert media_type.getkey('example') == example_spec
|
||||
|
||||
schema = media_type.get('schema')
|
||||
schema_spec = content_spec.get('schema')
|
||||
assert bool(schema_spec) == bool(media_type.schema)
|
||||
assert bool(schema_spec) == bool(schema)
|
||||
|
||||
if not schema_spec:
|
||||
continue
|
||||
|
@ -260,31 +239,24 @@ class TestPetstore(object):
|
|||
if '$ref' in schema_spec:
|
||||
continue
|
||||
|
||||
assert type(media_type.schema) == Schema
|
||||
assert media_type.schema.type.value ==\
|
||||
schema_spec['type']
|
||||
assert media_type.schema.required == schema_spec.get(
|
||||
'required', [])
|
||||
assert schema['type'] == schema_spec['type']
|
||||
assert schema.getkey('required') == schema_spec.get(
|
||||
'required')
|
||||
|
||||
request_body = operation.get('requestBody')
|
||||
request_body_spec = operation_spec.get('requestBody')
|
||||
|
||||
assert bool(request_body_spec) == bool(operation.request_body)
|
||||
assert bool(request_body_spec) == bool(request_body)
|
||||
|
||||
if not request_body_spec:
|
||||
continue
|
||||
|
||||
assert type(operation.request_body) == RequestBody
|
||||
assert bool(operation.request_body.required) ==\
|
||||
request_body_spec.get('required', False)
|
||||
|
||||
for mimetype, media_type in iteritems(
|
||||
operation.request_body.content):
|
||||
assert type(media_type) == MediaType
|
||||
assert media_type.mimetype == mimetype
|
||||
assert bool(request_body.getkey('required')) ==\
|
||||
request_body_spec.get('required')
|
||||
|
||||
content = request_body / 'content'
|
||||
for mimetype, media_type in iteritems(content):
|
||||
content_spec = request_body_spec['content'][mimetype]
|
||||
schema_spec = content_spec.get('schema')
|
||||
assert bool(schema_spec) == bool(media_type.schema)
|
||||
|
||||
if not schema_spec:
|
||||
continue
|
||||
|
@ -293,20 +265,22 @@ class TestPetstore(object):
|
|||
if '$ref' in schema_spec:
|
||||
continue
|
||||
|
||||
assert type(media_type.schema) == Schema
|
||||
assert media_type.schema.type.value ==\
|
||||
schema = content.get('schema')
|
||||
assert bool(schema_spec) == bool(schema)
|
||||
|
||||
assert schema.type.value ==\
|
||||
schema_spec['type']
|
||||
assert media_type.schema.format ==\
|
||||
assert schema.format ==\
|
||||
schema_spec.get('format')
|
||||
assert media_type.schema.required == schema_spec.get(
|
||||
assert schema.required == schema_spec.get(
|
||||
'required', False)
|
||||
|
||||
if not spec.components:
|
||||
components = spec.get('components')
|
||||
if not components:
|
||||
return
|
||||
|
||||
for schema_name, schema in iteritems(spec.components.schemas):
|
||||
assert type(schema) == Schema
|
||||
|
||||
schemas = components.get('schemas', {})
|
||||
for schema_name, schema in iteritems(schemas):
|
||||
schema_spec = spec_dict['components']['schemas'][schema_name]
|
||||
assert schema.read_only == schema_spec.get('readOnly', False)
|
||||
assert schema.write_only == schema_spec.get('writeOnly', False)
|
||||
assert schema.getkey('readOnly') == schema_spec.get('readOnly')
|
||||
assert schema.getkey('writeOnly') == schema_spec.get('writeOnly')
|
||||
|
|
|
@ -15,7 +15,6 @@ from openapi_core.extensions.models.models import BaseModel
|
|||
from openapi_core.schema.parameters.exceptions import (
|
||||
MissingRequiredParameter,
|
||||
)
|
||||
from openapi_core.schema.schemas.enums import SchemaType
|
||||
from openapi_core.shortcuts import (
|
||||
create_spec, validate_parameters, validate_body, validate_data,
|
||||
)
|
||||
|
@ -187,7 +186,7 @@ class TestPetstore(object):
|
|||
schema_errors = response_result.errors[0].schema_errors
|
||||
assert response_result.errors == [
|
||||
InvalidSchemaValue(
|
||||
type=SchemaType.OBJECT,
|
||||
type='object',
|
||||
value=response_data_json,
|
||||
schema_errors=schema_errors,
|
||||
),
|
||||
|
|
|
@ -6,7 +6,6 @@ from openapi_core.deserializing.exceptions import DeserializeError
|
|||
from openapi_core.deserializing.media_types.factories import (
|
||||
MediaTypeDeserializersFactory,
|
||||
)
|
||||
from openapi_core.schema.media_types.models import MediaType
|
||||
|
||||
|
||||
class TestMediaTypeDeserializer(object):
|
||||
|
@ -19,46 +18,46 @@ class TestMediaTypeDeserializer(object):
|
|||
return create_deserializer
|
||||
|
||||
def test_json_empty(self, deserializer_factory):
|
||||
media_type = MediaType('application/json')
|
||||
mimetype = 'application/json'
|
||||
value = ''
|
||||
|
||||
with pytest.raises(DeserializeError):
|
||||
deserializer_factory(media_type)(value)
|
||||
deserializer_factory(mimetype)(value)
|
||||
|
||||
def test_json_empty_object(self, deserializer_factory):
|
||||
media_type = MediaType('application/json')
|
||||
mimetype = 'application/json'
|
||||
value = "{}"
|
||||
|
||||
result = deserializer_factory(media_type)(value)
|
||||
result = deserializer_factory(mimetype)(value)
|
||||
|
||||
assert result == {}
|
||||
|
||||
def test_urlencoded_form_empty(self, deserializer_factory):
|
||||
media_type = MediaType('application/x-www-form-urlencoded')
|
||||
mimetype = 'application/x-www-form-urlencoded'
|
||||
value = ''
|
||||
|
||||
result = deserializer_factory(media_type)(value)
|
||||
result = deserializer_factory(mimetype)(value)
|
||||
|
||||
assert result == {}
|
||||
|
||||
def test_urlencoded_form_simple(self, deserializer_factory):
|
||||
media_type = MediaType('application/x-www-form-urlencoded')
|
||||
mimetype = 'application/x-www-form-urlencoded'
|
||||
value = 'param1=test'
|
||||
|
||||
result = deserializer_factory(media_type)(value)
|
||||
result = deserializer_factory(mimetype)(value)
|
||||
|
||||
assert result == {'param1': 'test'}
|
||||
|
||||
@pytest.mark.parametrize('value', [b(''), u('')])
|
||||
def test_data_form_empty(self, deserializer_factory, value):
|
||||
media_type = MediaType('multipart/form-data')
|
||||
mimetype = 'multipart/form-data'
|
||||
|
||||
result = deserializer_factory(media_type)(value)
|
||||
result = deserializer_factory(mimetype)(value)
|
||||
|
||||
assert result == {}
|
||||
|
||||
def test_data_form_simple(self, deserializer_factory):
|
||||
media_type = MediaType('multipart/form-data')
|
||||
mimetype = 'multipart/form-data'
|
||||
value = b(
|
||||
'Content-Type: multipart/form-data; boundary="'
|
||||
'===============2872712225071193122=="\n'
|
||||
|
@ -69,13 +68,12 @@ class TestMediaTypeDeserializer(object):
|
|||
'--===============2872712225071193122==--\n'
|
||||
)
|
||||
|
||||
result = deserializer_factory(media_type)(value)
|
||||
result = deserializer_factory(mimetype)(value)
|
||||
|
||||
assert result == {'param1': b('test')}
|
||||
|
||||
def test_custom_simple(self, deserializer_factory):
|
||||
custom_mimetype = 'application/custom'
|
||||
media_type = MediaType(custom_mimetype)
|
||||
value = "{}"
|
||||
|
||||
def custom_deserializer(value):
|
||||
|
@ -85,6 +83,6 @@ class TestMediaTypeDeserializer(object):
|
|||
}
|
||||
|
||||
result = deserializer_factory(
|
||||
media_type, custom_deserializers=custom_deserializers)(value)
|
||||
custom_mimetype, custom_deserializers=custom_deserializers)(value)
|
||||
|
||||
assert result == 'custom'
|
||||
|
|
|
@ -6,7 +6,7 @@ from openapi_core.deserializing.parameters.factories import (
|
|||
from openapi_core.deserializing.parameters.exceptions import (
|
||||
EmptyParameterValue,
|
||||
)
|
||||
from openapi_core.schema.parameters.models import Parameter
|
||||
from openapi_core.spec.paths import SpecPath
|
||||
|
||||
|
||||
class TestParameterDeserializer(object):
|
||||
|
@ -18,7 +18,12 @@ class TestParameterDeserializer(object):
|
|||
return create_deserializer
|
||||
|
||||
def test_deprecated(self, deserializer_factory):
|
||||
param = Parameter('param', 'query', deprecated=True)
|
||||
spec = {
|
||||
'name': 'param',
|
||||
'in': 'query',
|
||||
'deprecated': True,
|
||||
}
|
||||
param = SpecPath.from_spec(spec)
|
||||
value = 'test'
|
||||
|
||||
with pytest.warns(DeprecationWarning):
|
||||
|
@ -27,14 +32,22 @@ class TestParameterDeserializer(object):
|
|||
assert result == value
|
||||
|
||||
def test_query_empty(self, deserializer_factory):
|
||||
param = Parameter('param', 'query')
|
||||
spec = {
|
||||
'name': 'param',
|
||||
'in': 'query',
|
||||
}
|
||||
param = SpecPath.from_spec(spec)
|
||||
value = ''
|
||||
|
||||
with pytest.raises(EmptyParameterValue):
|
||||
deserializer_factory(param)(value)
|
||||
|
||||
def test_query_valid(self, deserializer_factory):
|
||||
param = Parameter('param', 'query')
|
||||
spec = {
|
||||
'name': 'param',
|
||||
'in': 'query',
|
||||
}
|
||||
param = SpecPath.from_spec(spec)
|
||||
value = 'test'
|
||||
|
||||
result = deserializer_factory(param)(value)
|
||||
|
|
|
@ -1,15 +1,22 @@
|
|||
import pytest
|
||||
|
||||
from openapi_core.schema.security_schemes.models import SecurityScheme
|
||||
from openapi_core.security.providers import HttpProvider
|
||||
from openapi_core.spec.paths import SpecPath
|
||||
from openapi_core.testing import MockRequest
|
||||
|
||||
|
||||
class TestHttpProvider(object):
|
||||
|
||||
@pytest.fixture
|
||||
def scheme(self):
|
||||
return SecurityScheme('http', scheme='bearer')
|
||||
def spec(self):
|
||||
return {
|
||||
'type': 'http',
|
||||
'scheme': 'bearer',
|
||||
}
|
||||
|
||||
@pytest.fixture
|
||||
def scheme(self, spec):
|
||||
return SpecPath.from_spec(spec)
|
||||
|
||||
@pytest.fixture
|
||||
def provider(self, scheme):
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
import pytest
|
||||
|
||||
from openapi_core.schema.infos.models import Info
|
||||
from openapi_core.schema.operations.models import Operation
|
||||
from openapi_core.schema.parameters.models import Parameter
|
||||
from openapi_core.schema.paths.models import Path
|
||||
from openapi_core.schema.servers.models import Server, ServerVariable
|
||||
from openapi_core.schema.specs.models import Spec
|
||||
from openapi_core.spec.paths import SpecPath
|
||||
from openapi_core.templating.datatypes import TemplateResult
|
||||
from openapi_core.templating.paths.exceptions import (
|
||||
PathNotFound, OperationNotFound, ServerNotFound,
|
||||
|
@ -19,8 +14,25 @@ class BaseTestSimpleServer(object):
|
|||
server_url = 'http://petstore.swagger.io'
|
||||
|
||||
@pytest.fixture
|
||||
def server(self):
|
||||
return Server(self.server_url, {})
|
||||
def server_variable(self):
|
||||
return {}
|
||||
|
||||
@pytest.fixture
|
||||
def server_variables(self, server_variable):
|
||||
if not server_variable:
|
||||
return {}
|
||||
return {
|
||||
self.server_variable_name: server_variable,
|
||||
}
|
||||
|
||||
@pytest.fixture
|
||||
def server(self, server_variables):
|
||||
server = {
|
||||
'url': self.server_url,
|
||||
}
|
||||
if server_variables:
|
||||
server['variables'] = server_variables
|
||||
return server
|
||||
|
||||
@pytest.fixture
|
||||
def servers(self, server):
|
||||
|
@ -36,22 +48,13 @@ class BaseTestVariableServer(BaseTestSimpleServer):
|
|||
|
||||
@pytest.fixture
|
||||
def server_variable(self):
|
||||
return ServerVariable(
|
||||
self.server_variable_name,
|
||||
default=self.server_variable_default,
|
||||
enum=self.server_variable_enum,
|
||||
)
|
||||
|
||||
@pytest.fixture
|
||||
def server_variables(self, server_variable):
|
||||
return {
|
||||
self.server_variable_name: server_variable,
|
||||
self.server_variable_name: {
|
||||
'default': self.server_variable_default,
|
||||
'enum': self.server_variable_enum,
|
||||
}
|
||||
}
|
||||
|
||||
@pytest.fixture
|
||||
def server(self, server_variables):
|
||||
return Server(self.server_url, server_variables)
|
||||
|
||||
|
||||
class BaseTestSimplePath(object):
|
||||
|
||||
|
@ -59,7 +62,7 @@ class BaseTestSimplePath(object):
|
|||
|
||||
@pytest.fixture
|
||||
def path(self, operations):
|
||||
return Path(self.path_name, operations)
|
||||
return operations
|
||||
|
||||
@pytest.fixture
|
||||
def paths(self, path):
|
||||
|
@ -75,28 +78,38 @@ class BaseTestVariablePath(BaseTestSimplePath):
|
|||
|
||||
@pytest.fixture
|
||||
def parameter(self):
|
||||
return Parameter(self.path_parameter_name, 'path')
|
||||
|
||||
@pytest.fixture
|
||||
def parameters(self, parameter):
|
||||
return {
|
||||
self.path_parameter_name: parameter
|
||||
'name': self.path_parameter_name,
|
||||
'in': 'path',
|
||||
}
|
||||
|
||||
@pytest.fixture
|
||||
def parameters(self, parameter):
|
||||
return [parameter, ]
|
||||
|
||||
@pytest.fixture
|
||||
def path(self, operations, parameters):
|
||||
return Path(self.path_name, operations, parameters=parameters)
|
||||
path = operations.copy()
|
||||
path['parameters'] = parameters
|
||||
return path
|
||||
|
||||
|
||||
class BaseTestSpecServer(object):
|
||||
|
||||
location = 'spec'
|
||||
|
||||
@pytest.fixture
|
||||
def info(self):
|
||||
return Info('Test schema', '1.0')
|
||||
return {
|
||||
'title': 'Test schema',
|
||||
'version': '1.0',
|
||||
}
|
||||
|
||||
@pytest.fixture
|
||||
def operation(self):
|
||||
return Operation('get', self.path_name, {}, {})
|
||||
return {
|
||||
'responses': [],
|
||||
}
|
||||
|
||||
@pytest.fixture
|
||||
def operations(self, operation):
|
||||
|
@ -106,7 +119,12 @@ class BaseTestSpecServer(object):
|
|||
|
||||
@pytest.fixture
|
||||
def spec(self, info, paths, servers):
|
||||
return Spec(info, paths, servers)
|
||||
spec = {
|
||||
'info': info,
|
||||
'servers': servers,
|
||||
'paths': paths,
|
||||
}
|
||||
return SpecPath.from_spec(spec)
|
||||
|
||||
@pytest.fixture
|
||||
def finder(self, spec):
|
||||
|
@ -115,24 +133,41 @@ class BaseTestSpecServer(object):
|
|||
|
||||
class BaseTestPathServer(BaseTestSpecServer):
|
||||
|
||||
location = 'path'
|
||||
|
||||
@pytest.fixture
|
||||
def path(self, operations, servers):
|
||||
return Path(self.path_name, operations, servers=servers)
|
||||
path = operations.copy()
|
||||
path['servers'] = servers
|
||||
return path
|
||||
|
||||
@pytest.fixture
|
||||
def spec(self, info, paths):
|
||||
return Spec(info, paths)
|
||||
spec = {
|
||||
'info': info,
|
||||
'paths': paths,
|
||||
}
|
||||
return SpecPath.from_spec(spec)
|
||||
|
||||
|
||||
class BaseTestOperationServer(BaseTestSpecServer):
|
||||
|
||||
location = 'operation'
|
||||
|
||||
@pytest.fixture
|
||||
def operation(self, servers):
|
||||
return Operation('get', self.path_name, {}, {}, servers=servers)
|
||||
return {
|
||||
'responses': [],
|
||||
'servers': servers,
|
||||
}
|
||||
|
||||
@pytest.fixture
|
||||
def spec(self, info, paths):
|
||||
return Spec(info, paths)
|
||||
spec = {
|
||||
'info': info,
|
||||
'paths': paths,
|
||||
}
|
||||
return SpecPath.from_spec(spec)
|
||||
|
||||
|
||||
class BaseTestServerNotFound(object):
|
||||
|
@ -141,6 +176,7 @@ class BaseTestServerNotFound(object):
|
|||
def servers(self):
|
||||
return []
|
||||
|
||||
@pytest.mark.xfail(reason="returns default server")
|
||||
def test_raises(self, finder):
|
||||
request_uri = '/resource'
|
||||
request = MockRequest(
|
||||
|
@ -167,13 +203,17 @@ class BaseTestOperationNotFound(object):
|
|||
|
||||
class BaseTestValid(object):
|
||||
|
||||
def test_simple(self, finder, path, operation, server):
|
||||
def test_simple(self, finder, spec):
|
||||
request_uri = '/resource'
|
||||
method = 'get'
|
||||
request = MockRequest(
|
||||
'http://petstore.swagger.io', 'get', request_uri)
|
||||
'http://petstore.swagger.io', method, request_uri)
|
||||
|
||||
result = finder.find(request)
|
||||
|
||||
path = spec / 'paths' / self.path_name
|
||||
operation = spec / 'paths' / self.path_name / method
|
||||
server = eval(self.location) / 'servers' / 0
|
||||
path_result = TemplateResult(self.path_name, {})
|
||||
server_result = TemplateResult(self.server_url, {})
|
||||
assert result == (
|
||||
|
@ -184,13 +224,17 @@ class BaseTestValid(object):
|
|||
class BaseTestVariableValid(object):
|
||||
|
||||
@pytest.mark.parametrize('version', ['v1', 'v2'])
|
||||
def test_variable(self, finder, path, operation, server, version):
|
||||
def test_variable(self, finder, spec, version):
|
||||
request_uri = '/{0}/resource'.format(version)
|
||||
method = 'get'
|
||||
request = MockRequest(
|
||||
'http://petstore.swagger.io', 'get', request_uri)
|
||||
'http://petstore.swagger.io', method, request_uri)
|
||||
|
||||
result = finder.find(request)
|
||||
|
||||
path = spec / 'paths' / self.path_name
|
||||
operation = spec / 'paths' / self.path_name / method
|
||||
server = eval(self.location) / 'servers' / 0
|
||||
path_result = TemplateResult(self.path_name, {})
|
||||
server_result = TemplateResult(self.server_url, {'version': version})
|
||||
assert result == (
|
||||
|
@ -201,13 +245,17 @@ class BaseTestVariableValid(object):
|
|||
class BaseTestPathVariableValid(object):
|
||||
|
||||
@pytest.mark.parametrize('res_id', ['111', '222'])
|
||||
def test_path_variable(self, finder, path, operation, server, res_id):
|
||||
def test_path_variable(self, finder, spec, res_id):
|
||||
request_uri = '/resource/{0}'.format(res_id)
|
||||
method = 'get'
|
||||
request = MockRequest(
|
||||
'http://petstore.swagger.io', 'get', request_uri)
|
||||
'http://petstore.swagger.io', method, request_uri)
|
||||
|
||||
result = finder.find(request)
|
||||
|
||||
path = spec / 'paths' / self.path_name
|
||||
operation = spec / 'paths' / self.path_name / method
|
||||
server = eval(self.location) / 'servers' / 0
|
||||
path_result = TemplateResult(self.path_name, {'resource_id': res_id})
|
||||
server_result = TemplateResult(self.server_url, {})
|
||||
assert result == (
|
||||
|
@ -396,10 +444,13 @@ class TestSimilarPaths(
|
|||
BaseTestSpecServer, BaseTestSimpleServer):
|
||||
|
||||
path_name = '/tokens'
|
||||
path_2_name = '/keys/{id}/tokens'
|
||||
|
||||
@pytest.fixture
|
||||
def operation_2(self):
|
||||
return Operation('get', '/keys/{id}/tokens', {}, {})
|
||||
return {
|
||||
'responses': [],
|
||||
}
|
||||
|
||||
@pytest.fixture
|
||||
def operations_2(self, operation_2):
|
||||
|
@ -409,28 +460,32 @@ class TestSimilarPaths(
|
|||
|
||||
@pytest.fixture
|
||||
def path(self, operations):
|
||||
return Path('/tokens', operations)
|
||||
return operations
|
||||
|
||||
@pytest.fixture
|
||||
def path_2(self, operations_2):
|
||||
return Path('/keys/{id}/tokens', operations_2)
|
||||
return operations_2
|
||||
|
||||
@pytest.fixture
|
||||
def paths(self, path, path_2):
|
||||
return {
|
||||
path.name: path,
|
||||
path_2.name: path_2,
|
||||
self.path_name: path,
|
||||
self.path_2_name: path_2,
|
||||
}
|
||||
|
||||
def test_valid(self, finder, path_2, operation_2, server):
|
||||
def test_valid(self, finder, spec):
|
||||
token_id = '123'
|
||||
request_uri = '/keys/{0}/tokens'.format(token_id)
|
||||
method = 'get'
|
||||
request = MockRequest(
|
||||
'http://petstore.swagger.io', 'get', request_uri)
|
||||
'http://petstore.swagger.io', method, request_uri)
|
||||
|
||||
result = finder.find(request)
|
||||
|
||||
path_result = TemplateResult(path_2.name, {'id': token_id})
|
||||
path_2 = spec / 'paths' / self.path_2_name
|
||||
operation_2 = spec / 'paths' / self.path_2_name / method
|
||||
server = eval(self.location) / 'servers' / 0
|
||||
path_result = TemplateResult(self.path_2_name, {'id': token_id})
|
||||
server_result = TemplateResult(self.server_url, {})
|
||||
assert result == (
|
||||
path_2, operation_2, server, path_result, server_result,
|
||||
|
@ -441,10 +496,13 @@ class TestConcretePaths(
|
|||
BaseTestSpecServer, BaseTestSimpleServer):
|
||||
|
||||
path_name = '/keys/{id}/tokens'
|
||||
path_2_name = '/keys/master/tokens'
|
||||
|
||||
@pytest.fixture
|
||||
def operation_2(self):
|
||||
return Operation('get', '/keys/master/tokens', {}, {})
|
||||
return {
|
||||
'responses': [],
|
||||
}
|
||||
|
||||
@pytest.fixture
|
||||
def operations_2(self, operation_2):
|
||||
|
@ -454,26 +512,30 @@ class TestConcretePaths(
|
|||
|
||||
@pytest.fixture
|
||||
def path(self, operations):
|
||||
return Path('/keys/{id}/tokens', operations)
|
||||
return operations
|
||||
|
||||
@pytest.fixture
|
||||
def path_2(self, operations_2):
|
||||
return Path('/keys/master/tokens', operations_2)
|
||||
return operations_2
|
||||
|
||||
@pytest.fixture
|
||||
def paths(self, path, path_2):
|
||||
return {
|
||||
path.name: path,
|
||||
path_2.name: path_2,
|
||||
self.path_name: path,
|
||||
self.path_2_name: path_2,
|
||||
}
|
||||
|
||||
def test_valid(self, finder, path_2, operation_2, server):
|
||||
def test_valid(self, finder, spec):
|
||||
request_uri = '/keys/master/tokens'
|
||||
method = 'get'
|
||||
request = MockRequest(
|
||||
'http://petstore.swagger.io', 'get', request_uri)
|
||||
'http://petstore.swagger.io', method, request_uri)
|
||||
result = finder.find(request)
|
||||
|
||||
path_result = TemplateResult(path_2.name, {})
|
||||
path_2 = spec / 'paths' / self.path_2_name
|
||||
operation_2 = spec / 'paths' / self.path_2_name / method
|
||||
server = eval(self.location) / 'servers' / 0
|
||||
path_result = TemplateResult(self.path_2_name, {})
|
||||
server_result = TemplateResult(self.server_url, {})
|
||||
assert result == (
|
||||
path_2, operation_2, server, path_result, server_result,
|
||||
|
@ -484,10 +546,13 @@ class TestTemplateConcretePaths(
|
|||
BaseTestSpecServer, BaseTestSimpleServer):
|
||||
|
||||
path_name = '/keys/{id}/tokens/{id2}'
|
||||
path_2_name = '/keys/{id}/tokens/master'
|
||||
|
||||
@pytest.fixture
|
||||
def operation_2(self):
|
||||
return Operation('get', '/keys/{id}/tokens/master', {}, {})
|
||||
return {
|
||||
'responses': [],
|
||||
}
|
||||
|
||||
@pytest.fixture
|
||||
def operations_2(self, operation_2):
|
||||
|
@ -497,27 +562,31 @@ class TestTemplateConcretePaths(
|
|||
|
||||
@pytest.fixture
|
||||
def path(self, operations):
|
||||
return Path('/keys/{id}/tokens/{id2}', operations)
|
||||
return operations
|
||||
|
||||
@pytest.fixture
|
||||
def path_2(self, operations_2):
|
||||
return Path('/keys/{id}/tokens/master', operations_2)
|
||||
return operations_2
|
||||
|
||||
@pytest.fixture
|
||||
def paths(self, path, path_2):
|
||||
return {
|
||||
path.name: path,
|
||||
path_2.name: path_2,
|
||||
self.path_name: path,
|
||||
self.path_2_name: path_2,
|
||||
}
|
||||
|
||||
def test_valid(self, finder, path_2, operation_2, server):
|
||||
def test_valid(self, finder, spec):
|
||||
token_id = '123'
|
||||
request_uri = '/keys/{0}/tokens/master'.format(token_id)
|
||||
method = 'get'
|
||||
request = MockRequest(
|
||||
'http://petstore.swagger.io', 'get', request_uri)
|
||||
'http://petstore.swagger.io', method, request_uri)
|
||||
result = finder.find(request)
|
||||
|
||||
path_result = TemplateResult(path_2.name, {'id': '123'})
|
||||
path_2 = spec / 'paths' / self.path_2_name
|
||||
operation_2 = spec / 'paths' / self.path_2_name / method
|
||||
server = eval(self.location) / 'servers' / 0
|
||||
path_result = TemplateResult(self.path_2_name, {'id': '123'})
|
||||
server_result = TemplateResult(self.server_url, {})
|
||||
assert result == (
|
||||
path_2, operation_2, server, path_result, server_result,
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import mock
|
||||
import pytest
|
||||
|
||||
from openapi_core.spec.paths import SpecPath
|
||||
from openapi_core.templating.responses.finders import ResponseFinder
|
||||
|
||||
|
||||
class TestResponses(object):
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
def responses(self):
|
||||
def spec(self):
|
||||
return {
|
||||
'200': mock.sentinel.response_200,
|
||||
'299': mock.sentinel.response_299,
|
||||
|
@ -15,6 +16,10 @@ class TestResponses(object):
|
|||
'default': mock.sentinel.response_default,
|
||||
}
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
def responses(self, spec):
|
||||
return SpecPath.from_spec(spec)
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
def finder(self, responses):
|
||||
return ResponseFinder(responses)
|
||||
|
@ -22,14 +27,14 @@ class TestResponses(object):
|
|||
def test_default(self, finder, responses):
|
||||
response = finder.find()
|
||||
|
||||
assert response == responses['default']
|
||||
assert response == responses / 'default'
|
||||
|
||||
def test_range(self, finder, responses):
|
||||
response = finder.find('201')
|
||||
|
||||
assert response == responses['2XX']
|
||||
assert response == responses / '2XX'
|
||||
|
||||
def test_exact(self, finder, responses):
|
||||
response = finder.find('200')
|
||||
|
||||
assert response == responses['200']
|
||||
assert response == responses / '200'
|
||||
|
|
|
@ -4,11 +4,8 @@ import uuid
|
|||
from isodate.tzinfo import UTC, FixedOffset
|
||||
import pytest
|
||||
|
||||
from openapi_core.schema.media_types.models import MediaType
|
||||
from openapi_core.schema.parameters.models import Parameter
|
||||
from openapi_core.schema.schemas.enums import SchemaType
|
||||
from openapi_core.schema.schemas.models import Schema
|
||||
from openapi_core.schema.schemas.types import NoValue
|
||||
from openapi_core.spec.paths import SpecPath
|
||||
from openapi_core.unmarshalling.schemas.enums import UnmarshalContext
|
||||
from openapi_core.unmarshalling.schemas.exceptions import (
|
||||
InvalidSchemaFormatValue, InvalidSchemaValue, UnmarshalError,
|
||||
|
@ -33,22 +30,24 @@ def unmarshaller_factory():
|
|||
return create_unmarshaller
|
||||
|
||||
|
||||
class TestParameterUnmarshal(object):
|
||||
class TestUnmarshal(object):
|
||||
|
||||
def test_no_schema(self, unmarshaller_factory):
|
||||
param = Parameter('param', 'query')
|
||||
schema = None
|
||||
value = 'test'
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
unmarshaller_factory(param.schema).unmarshal(value)
|
||||
unmarshaller_factory(schema).unmarshal(value)
|
||||
|
||||
def test_schema_type_invalid(self, unmarshaller_factory):
|
||||
schema = Schema('integer', _source={'type': 'integer'})
|
||||
param = Parameter('param', 'query', schema=schema)
|
||||
spec = {
|
||||
'type': 'integer',
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = 'test'
|
||||
|
||||
with pytest.raises(InvalidSchemaFormatValue):
|
||||
unmarshaller_factory(param.schema).unmarshal(value)
|
||||
unmarshaller_factory(schema).unmarshal(value)
|
||||
|
||||
def test_schema_custom_format_invalid(self, unmarshaller_factory):
|
||||
|
||||
|
@ -60,59 +59,16 @@ class TestParameterUnmarshal(object):
|
|||
custom_formatters = {
|
||||
custom_format: formatter,
|
||||
}
|
||||
schema = Schema(
|
||||
'string',
|
||||
schema_format=custom_format,
|
||||
_source={'type': 'string', 'format': 'custom'},
|
||||
)
|
||||
param = Parameter('param', 'query', schema=schema)
|
||||
value = 'test'
|
||||
|
||||
with pytest.raises(InvalidSchemaFormatValue):
|
||||
unmarshaller_factory(
|
||||
param.schema,
|
||||
custom_formatters=custom_formatters,
|
||||
).unmarshal(value)
|
||||
|
||||
|
||||
class TestMediaTypeUnmarshal(object):
|
||||
|
||||
def test_no_schema(self, unmarshaller_factory):
|
||||
media_type = MediaType('application/json')
|
||||
value = 'test'
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
unmarshaller_factory(media_type.schema).unmarshal(value)
|
||||
|
||||
def test_schema_type_invalid(self, unmarshaller_factory):
|
||||
schema = Schema('integer', _source={'type': 'integer'})
|
||||
media_type = MediaType('application/json', schema=schema)
|
||||
value = 'test'
|
||||
|
||||
with pytest.raises(InvalidSchemaFormatValue):
|
||||
unmarshaller_factory(media_type.schema).unmarshal(value)
|
||||
|
||||
def test_schema_custom_format_invalid(self, unmarshaller_factory):
|
||||
|
||||
class CustomFormatter(Formatter):
|
||||
def unmarshal(self, value):
|
||||
raise ValueError
|
||||
formatter = CustomFormatter()
|
||||
custom_format = 'custom'
|
||||
custom_formatters = {
|
||||
custom_format: formatter,
|
||||
spec = {
|
||||
'type': 'string',
|
||||
'format': 'custom',
|
||||
}
|
||||
schema = Schema(
|
||||
'string',
|
||||
schema_format=custom_format,
|
||||
_source={'type': 'string', 'format': 'custom'},
|
||||
)
|
||||
media_type = MediaType('application/json', schema=schema)
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = 'test'
|
||||
|
||||
with pytest.raises(InvalidSchemaFormatValue):
|
||||
unmarshaller_factory(
|
||||
media_type.schema,
|
||||
schema,
|
||||
custom_formatters=custom_formatters,
|
||||
).unmarshal(value)
|
||||
|
||||
|
@ -120,7 +76,11 @@ class TestMediaTypeUnmarshal(object):
|
|||
class TestSchemaUnmarshallerCall(object):
|
||||
|
||||
def test_deprecated(self, unmarshaller_factory):
|
||||
schema = Schema('string', deprecated=True)
|
||||
spec = {
|
||||
'type': 'string',
|
||||
'deprecated': True,
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = 'test'
|
||||
|
||||
with pytest.warns(DeprecationWarning):
|
||||
|
@ -132,14 +92,20 @@ class TestSchemaUnmarshallerCall(object):
|
|||
'boolean', 'array', 'integer', 'number',
|
||||
])
|
||||
def test_non_string_empty_value(self, schema_type, unmarshaller_factory):
|
||||
schema = Schema(schema_type)
|
||||
spec = {
|
||||
'type': schema_type,
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = ''
|
||||
|
||||
with pytest.raises(InvalidSchemaValue):
|
||||
unmarshaller_factory(schema)(value)
|
||||
|
||||
def test_string_valid(self, unmarshaller_factory):
|
||||
schema = Schema('string')
|
||||
spec = {
|
||||
'type': 'string',
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = 'test'
|
||||
|
||||
result = unmarshaller_factory(schema)(value)
|
||||
|
@ -147,7 +113,11 @@ class TestSchemaUnmarshallerCall(object):
|
|||
assert result == value
|
||||
|
||||
def test_string_format_uuid_valid(self, unmarshaller_factory):
|
||||
schema = Schema(SchemaType.STRING, schema_format='uuid')
|
||||
spec = {
|
||||
'type': 'string',
|
||||
'format': 'uuid',
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = str(uuid.uuid4())
|
||||
|
||||
result = unmarshaller_factory(schema)(value)
|
||||
|
@ -156,14 +126,22 @@ class TestSchemaUnmarshallerCall(object):
|
|||
|
||||
def test_string_format_uuid_uuid_quirks_invalid(
|
||||
self, unmarshaller_factory):
|
||||
schema = Schema(SchemaType.STRING, schema_format='uuid')
|
||||
spec = {
|
||||
'type': 'string',
|
||||
'format': 'uuid',
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = uuid.uuid4()
|
||||
|
||||
with pytest.raises(InvalidSchemaValue):
|
||||
unmarshaller_factory(schema)(value)
|
||||
|
||||
def test_string_format_password(self, unmarshaller_factory):
|
||||
schema = Schema(SchemaType.STRING, schema_format='password')
|
||||
spec = {
|
||||
'type': 'string',
|
||||
'format': 'password',
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = 'password'
|
||||
|
||||
result = unmarshaller_factory(schema)(value)
|
||||
|
@ -171,7 +149,10 @@ class TestSchemaUnmarshallerCall(object):
|
|||
assert result == 'password'
|
||||
|
||||
def test_string_float_invalid(self, unmarshaller_factory):
|
||||
schema = Schema('string')
|
||||
spec = {
|
||||
'type': 'string',
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = 1.23
|
||||
|
||||
with pytest.raises(InvalidSchemaValue):
|
||||
|
@ -179,7 +160,11 @@ class TestSchemaUnmarshallerCall(object):
|
|||
|
||||
def test_string_default(self, unmarshaller_factory):
|
||||
default_value = 'default'
|
||||
schema = Schema('string', default=default_value)
|
||||
spec = {
|
||||
'type': 'string',
|
||||
'default': default_value,
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = NoValue
|
||||
|
||||
result = unmarshaller_factory(schema)(value)
|
||||
|
@ -189,7 +174,12 @@ class TestSchemaUnmarshallerCall(object):
|
|||
@pytest.mark.parametrize('default_value', ['default', None])
|
||||
def test_string_default_nullable(
|
||||
self, default_value, unmarshaller_factory):
|
||||
schema = Schema('string', default=default_value, nullable=True)
|
||||
spec = {
|
||||
'type': 'string',
|
||||
'default': default_value,
|
||||
'nullable': True,
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = NoValue
|
||||
|
||||
result = unmarshaller_factory(schema)(value)
|
||||
|
@ -197,7 +187,11 @@ class TestSchemaUnmarshallerCall(object):
|
|||
assert result == default_value
|
||||
|
||||
def test_string_format_date(self, unmarshaller_factory):
|
||||
schema = Schema('string', schema_format='date')
|
||||
spec = {
|
||||
'type': 'string',
|
||||
'format': 'date',
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = '2018-01-02'
|
||||
|
||||
result = unmarshaller_factory(schema)(value)
|
||||
|
@ -205,14 +199,22 @@ class TestSchemaUnmarshallerCall(object):
|
|||
assert result == datetime.date(2018, 1, 2)
|
||||
|
||||
def test_string_format_datetime_invalid(self, unmarshaller_factory):
|
||||
schema = Schema('string', schema_format='date-time')
|
||||
spec = {
|
||||
'type': 'string',
|
||||
'format': 'date-time',
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = '2018-01-02T00:00:00'
|
||||
|
||||
with pytest.raises(InvalidSchemaValue):
|
||||
unmarshaller_factory(schema)(value)
|
||||
|
||||
def test_string_format_datetime_utc(self, unmarshaller_factory):
|
||||
schema = Schema('string', schema_format='date-time')
|
||||
spec = {
|
||||
'type': 'string',
|
||||
'format': 'date-time',
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = '2018-01-02T00:00:00Z'
|
||||
|
||||
result = unmarshaller_factory(schema)(value)
|
||||
|
@ -221,7 +223,11 @@ class TestSchemaUnmarshallerCall(object):
|
|||
assert result == datetime.datetime(2018, 1, 2, 0, 0, tzinfo=tzinfo)
|
||||
|
||||
def test_string_format_datetime_tz(self, unmarshaller_factory):
|
||||
schema = Schema('string', schema_format='date-time')
|
||||
spec = {
|
||||
'type': 'string',
|
||||
'format': 'date-time',
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = '2020-04-01T12:00:00+02:00'
|
||||
|
||||
result = unmarshaller_factory(schema)(value)
|
||||
|
@ -236,7 +242,11 @@ class TestSchemaUnmarshallerCall(object):
|
|||
def unmarshal(self, value):
|
||||
return formatted
|
||||
custom_format = 'custom'
|
||||
schema = Schema('string', schema_format=custom_format)
|
||||
spec = {
|
||||
'type': 'string',
|
||||
'format': custom_format,
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = 'x'
|
||||
formatter = CustomFormatter()
|
||||
custom_formatters = {
|
||||
|
@ -254,7 +264,11 @@ class TestSchemaUnmarshallerCall(object):
|
|||
def unmarshal(self, value):
|
||||
raise ValueError
|
||||
custom_format = 'custom'
|
||||
schema = Schema('string', schema_format=custom_format)
|
||||
spec = {
|
||||
'type': 'string',
|
||||
'format': custom_format,
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = 'x'
|
||||
formatter = CustomFormatter()
|
||||
custom_formatters = {
|
||||
|
@ -267,7 +281,11 @@ class TestSchemaUnmarshallerCall(object):
|
|||
|
||||
def test_string_format_unknown(self, unmarshaller_factory):
|
||||
unknown_format = 'unknown'
|
||||
schema = Schema('string', schema_format=unknown_format)
|
||||
spec = {
|
||||
'type': 'string',
|
||||
'format': unknown_format,
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = 'x'
|
||||
|
||||
with pytest.raises(FormatterNotFoundError):
|
||||
|
@ -275,7 +293,11 @@ class TestSchemaUnmarshallerCall(object):
|
|||
|
||||
def test_string_format_invalid_value(self, unmarshaller_factory):
|
||||
custom_format = 'custom'
|
||||
schema = Schema('string', schema_format=custom_format)
|
||||
spec = {
|
||||
'type': 'string',
|
||||
'format': custom_format,
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = 'x'
|
||||
|
||||
with pytest.raises(
|
||||
|
@ -287,7 +309,10 @@ class TestSchemaUnmarshallerCall(object):
|
|||
unmarshaller_factory(schema)(value)
|
||||
|
||||
def test_integer_valid(self, unmarshaller_factory):
|
||||
schema = Schema('integer')
|
||||
spec = {
|
||||
'type': 'integer',
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = 123
|
||||
|
||||
result = unmarshaller_factory(schema)(value)
|
||||
|
@ -295,21 +320,32 @@ class TestSchemaUnmarshallerCall(object):
|
|||
assert result == int(value)
|
||||
|
||||
def test_integer_string_invalid(self, unmarshaller_factory):
|
||||
schema = Schema('integer')
|
||||
spec = {
|
||||
'type': 'integer',
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = '123'
|
||||
|
||||
with pytest.raises(InvalidSchemaValue):
|
||||
unmarshaller_factory(schema)(value)
|
||||
|
||||
def test_integer_enum_invalid(self, unmarshaller_factory):
|
||||
schema = Schema('integer', enum=[1, 2, 3])
|
||||
spec = {
|
||||
'type': 'integer',
|
||||
'enum': [1, 2, 3],
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = '123'
|
||||
|
||||
with pytest.raises(UnmarshalError):
|
||||
unmarshaller_factory(schema)(value)
|
||||
|
||||
def test_integer_enum(self, unmarshaller_factory):
|
||||
schema = Schema('integer', enum=[1, 2, 3])
|
||||
spec = {
|
||||
'type': 'integer',
|
||||
'enum': [1, 2, 3],
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = 2
|
||||
|
||||
result = unmarshaller_factory(schema)(value)
|
||||
|
@ -317,7 +353,11 @@ class TestSchemaUnmarshallerCall(object):
|
|||
assert result == int(value)
|
||||
|
||||
def test_integer_enum_string_invalid(self, unmarshaller_factory):
|
||||
schema = Schema('integer', enum=[1, 2, 3])
|
||||
spec = {
|
||||
'type': 'integer',
|
||||
'enum': [1, 2, 3],
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = '2'
|
||||
|
||||
with pytest.raises(UnmarshalError):
|
||||
|
@ -325,7 +365,11 @@ class TestSchemaUnmarshallerCall(object):
|
|||
|
||||
def test_integer_default(self, unmarshaller_factory):
|
||||
default_value = 123
|
||||
schema = Schema('integer', default=default_value)
|
||||
spec = {
|
||||
'type': 'integer',
|
||||
'default': default_value,
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = NoValue
|
||||
|
||||
result = unmarshaller_factory(schema)(value)
|
||||
|
@ -334,7 +378,12 @@ class TestSchemaUnmarshallerCall(object):
|
|||
|
||||
def test_integer_default_nullable(self, unmarshaller_factory):
|
||||
default_value = 123
|
||||
schema = Schema('integer', default=default_value, nullable=True)
|
||||
spec = {
|
||||
'type': 'integer',
|
||||
'default': default_value,
|
||||
'nullable': True,
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = None
|
||||
|
||||
result = unmarshaller_factory(schema)(value)
|
||||
|
@ -342,14 +391,23 @@ class TestSchemaUnmarshallerCall(object):
|
|||
assert result is None
|
||||
|
||||
def test_integer_invalid(self, unmarshaller_factory):
|
||||
schema = Schema('integer')
|
||||
spec = {
|
||||
'type': 'integer',
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = 'abc'
|
||||
|
||||
with pytest.raises(InvalidSchemaValue):
|
||||
unmarshaller_factory(schema)(value)
|
||||
|
||||
def test_array_valid(self, unmarshaller_factory):
|
||||
schema = Schema('array', items=Schema('integer'))
|
||||
spec = {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'integer',
|
||||
}
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = [1, 2, 3]
|
||||
|
||||
result = unmarshaller_factory(schema)(value)
|
||||
|
@ -357,42 +415,63 @@ class TestSchemaUnmarshallerCall(object):
|
|||
assert result == value
|
||||
|
||||
def test_array_null(self, unmarshaller_factory):
|
||||
schema = Schema(
|
||||
'array',
|
||||
items=Schema('integer'),
|
||||
)
|
||||
spec = {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'integer',
|
||||
}
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = None
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
unmarshaller_factory(schema)(value)
|
||||
|
||||
def test_array_nullable(self, unmarshaller_factory):
|
||||
schema = Schema(
|
||||
'array',
|
||||
items=Schema('integer'),
|
||||
nullable=True,
|
||||
)
|
||||
spec = {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'integer',
|
||||
},
|
||||
'nullable': True,
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = None
|
||||
result = unmarshaller_factory(schema)(value)
|
||||
|
||||
assert result is None
|
||||
|
||||
def test_array_of_string_string_invalid(self, unmarshaller_factory):
|
||||
schema = Schema('array', items=Schema('string'))
|
||||
spec = {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'string',
|
||||
}
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = '123'
|
||||
|
||||
with pytest.raises(InvalidSchemaValue):
|
||||
unmarshaller_factory(schema)(value)
|
||||
|
||||
def test_array_of_integer_string_invalid(self, unmarshaller_factory):
|
||||
schema = Schema('array', items=Schema('integer'))
|
||||
spec = {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'integer',
|
||||
}
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = '123'
|
||||
|
||||
with pytest.raises(InvalidSchemaValue):
|
||||
unmarshaller_factory(schema)(value)
|
||||
|
||||
def test_boolean_valid(self, unmarshaller_factory):
|
||||
schema = Schema('boolean')
|
||||
spec = {
|
||||
'type': 'boolean',
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = True
|
||||
|
||||
result = unmarshaller_factory(schema)(value)
|
||||
|
@ -400,14 +479,20 @@ class TestSchemaUnmarshallerCall(object):
|
|||
assert result == value
|
||||
|
||||
def test_boolean_string_invalid(self, unmarshaller_factory):
|
||||
schema = Schema('boolean')
|
||||
spec = {
|
||||
'type': 'boolean',
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = 'True'
|
||||
|
||||
with pytest.raises(InvalidSchemaValue):
|
||||
unmarshaller_factory(schema)(value)
|
||||
|
||||
def test_number_valid(self, unmarshaller_factory):
|
||||
schema = Schema('number')
|
||||
spec = {
|
||||
'type': 'number',
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = 1.23
|
||||
|
||||
result = unmarshaller_factory(schema)(value)
|
||||
|
@ -415,14 +500,20 @@ class TestSchemaUnmarshallerCall(object):
|
|||
assert result == value
|
||||
|
||||
def test_number_string_invalid(self, unmarshaller_factory):
|
||||
schema = Schema('number')
|
||||
spec = {
|
||||
'type': 'number',
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = '1.23'
|
||||
|
||||
with pytest.raises(InvalidSchemaValue):
|
||||
unmarshaller_factory(schema)(value)
|
||||
|
||||
def test_number_int(self, unmarshaller_factory):
|
||||
schema = Schema('number')
|
||||
spec = {
|
||||
'type': 'number',
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = 1
|
||||
result = unmarshaller_factory(schema)(value)
|
||||
|
||||
|
@ -430,7 +521,10 @@ class TestSchemaUnmarshallerCall(object):
|
|||
assert type(result) == int
|
||||
|
||||
def test_number_float(self, unmarshaller_factory):
|
||||
schema = Schema('number')
|
||||
spec = {
|
||||
'type': 'number',
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = 1.2
|
||||
result = unmarshaller_factory(schema)(value)
|
||||
|
||||
|
@ -438,42 +532,72 @@ class TestSchemaUnmarshallerCall(object):
|
|||
assert type(result) == float
|
||||
|
||||
def test_number_format_float(self, unmarshaller_factory):
|
||||
schema = Schema('number', schema_format='float')
|
||||
spec = {
|
||||
'type': 'number',
|
||||
'format': 'float',
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = 1.2
|
||||
result = unmarshaller_factory(schema)(value)
|
||||
|
||||
assert result == 1.2
|
||||
|
||||
def test_number_format_double(self, unmarshaller_factory):
|
||||
schema = Schema('number', schema_format='double')
|
||||
spec = {
|
||||
'type': 'number',
|
||||
'format': 'double',
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = 1.2
|
||||
result = unmarshaller_factory(schema)(value)
|
||||
|
||||
assert result == 1.2
|
||||
|
||||
def test_object_nullable(self, unmarshaller_factory):
|
||||
schema = Schema(
|
||||
'object',
|
||||
properties={
|
||||
'foo': Schema('object', nullable=True),
|
||||
spec = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'foo': {
|
||||
'type': 'object',
|
||||
'nullable': True,
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = {'foo': None}
|
||||
result = unmarshaller_factory(schema)(value)
|
||||
|
||||
assert result == {'foo': None}
|
||||
|
||||
def test_schema_any_one_of(self, unmarshaller_factory):
|
||||
schema = Schema(one_of=[
|
||||
Schema('string'),
|
||||
Schema('array', items=Schema('string')),
|
||||
])
|
||||
spec = {
|
||||
'oneOf': [
|
||||
{
|
||||
'type': 'string',
|
||||
},
|
||||
{
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'string',
|
||||
}
|
||||
}
|
||||
],
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
assert unmarshaller_factory(schema)(['hello']) == ['hello']
|
||||
|
||||
def test_schema_any_all_of(self, unmarshaller_factory):
|
||||
schema = Schema(all_of=[
|
||||
Schema('array', items=Schema('string')),
|
||||
])
|
||||
spec = {
|
||||
'allOf': [
|
||||
{
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'string',
|
||||
}
|
||||
}
|
||||
],
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
assert unmarshaller_factory(schema)(['hello']) == ['hello']
|
||||
|
||||
@pytest.mark.parametrize('value', [
|
||||
|
@ -499,34 +623,45 @@ class TestSchemaUnmarshallerCall(object):
|
|||
])
|
||||
def test_schema_any_all_of_invalid_properties(
|
||||
self, value, unmarshaller_factory):
|
||||
schema = Schema(
|
||||
all_of=[
|
||||
Schema(
|
||||
'object',
|
||||
required=['somestr'],
|
||||
properties={
|
||||
'somestr': Schema('string'),
|
||||
spec = {
|
||||
'allOf': [
|
||||
{
|
||||
'type': 'object',
|
||||
'required': ['somestr'],
|
||||
'properties': {
|
||||
'somestr': {
|
||||
'type': 'string',
|
||||
},
|
||||
},
|
||||
),
|
||||
Schema(
|
||||
'object',
|
||||
required=['someint'],
|
||||
properties={
|
||||
'someint': Schema('integer'),
|
||||
},
|
||||
{
|
||||
'type': 'object',
|
||||
'required': ['someint'],
|
||||
'properties': {
|
||||
'someint': {
|
||||
'type': 'integer',
|
||||
},
|
||||
},
|
||||
),
|
||||
}
|
||||
],
|
||||
additional_properties=False,
|
||||
)
|
||||
'additionalProperties': False,
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
|
||||
with pytest.raises(InvalidSchemaValue):
|
||||
unmarshaller_factory(schema)(value)
|
||||
|
||||
def test_schema_any_all_of_any(self, unmarshaller_factory):
|
||||
schema = Schema(all_of=[
|
||||
Schema(),
|
||||
Schema('string', schema_format='date'),
|
||||
])
|
||||
spec = {
|
||||
'allOf': [
|
||||
{},
|
||||
{
|
||||
'type': 'string',
|
||||
'format': 'date',
|
||||
},
|
||||
],
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
value = '2018-01-02'
|
||||
|
||||
result = unmarshaller_factory(schema)(value)
|
||||
|
@ -534,7 +669,8 @@ class TestSchemaUnmarshallerCall(object):
|
|||
assert result == datetime.date(2018, 1, 2)
|
||||
|
||||
def test_schema_any(self, unmarshaller_factory):
|
||||
schema = Schema()
|
||||
spec = {}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
assert unmarshaller_factory(schema)('string') == 'string'
|
||||
|
||||
@pytest.mark.parametrize('value', [
|
||||
|
@ -542,21 +678,30 @@ class TestSchemaUnmarshallerCall(object):
|
|||
{'foo': 'bar', 'bar': 'foo'},
|
||||
{'additional': {'bar': 1}},
|
||||
])
|
||||
@pytest.mark.parametrize('additional_properties', [True, Schema()])
|
||||
@pytest.mark.parametrize('additional_properties', [True, {}])
|
||||
def test_schema_free_form_object(
|
||||
self, value, additional_properties, unmarshaller_factory):
|
||||
schema = Schema('object', additional_properties=additional_properties)
|
||||
spec = {
|
||||
'type': 'object',
|
||||
'additionalProperties': additional_properties,
|
||||
}
|
||||
schema = SpecPath.from_spec(spec)
|
||||
|
||||
result = unmarshaller_factory(schema)(value)
|
||||
assert result == value
|
||||
|
||||
def test_read_only_properties(self, unmarshaller_factory):
|
||||
id_property = Schema('integer', read_only=True)
|
||||
|
||||
def properties():
|
||||
yield ('id', id_property)
|
||||
|
||||
obj_schema = Schema('object', properties=properties(), required=['id'])
|
||||
spec = {
|
||||
'type': 'object',
|
||||
'required': ['id'],
|
||||
'properties': {
|
||||
'id': {
|
||||
'type': 'integer',
|
||||
'readOnly': True,
|
||||
}
|
||||
},
|
||||
}
|
||||
obj_schema = SpecPath.from_spec(spec)
|
||||
|
||||
# readOnly properties may be admitted in a Response context
|
||||
result = unmarshaller_factory(
|
||||
|
@ -565,19 +710,36 @@ class TestSchemaUnmarshallerCall(object):
|
|||
'id': 10,
|
||||
}
|
||||
|
||||
# readOnly properties are not admitted on a Request context
|
||||
result = unmarshaller_factory(
|
||||
obj_schema, context=UnmarshalContext.REQUEST)({"id": 10})
|
||||
def test_read_only_properties_invalid(self, unmarshaller_factory):
|
||||
spec = {
|
||||
'type': 'object',
|
||||
'required': ['id'],
|
||||
'properties': {
|
||||
'id': {
|
||||
'type': 'integer',
|
||||
'readOnly': True,
|
||||
}
|
||||
},
|
||||
}
|
||||
obj_schema = SpecPath.from_spec(spec)
|
||||
|
||||
assert result == {}
|
||||
# readOnly properties are not admitted on a Request context
|
||||
with pytest.raises(InvalidSchemaValue):
|
||||
unmarshaller_factory(
|
||||
obj_schema, context=UnmarshalContext.REQUEST)({"id": 10})
|
||||
|
||||
def test_write_only_properties(self, unmarshaller_factory):
|
||||
id_property = Schema('integer', write_only=True)
|
||||
|
||||
def properties():
|
||||
yield ('id', id_property)
|
||||
|
||||
obj_schema = Schema('object', properties=properties(), required=['id'])
|
||||
spec = {
|
||||
'type': 'object',
|
||||
'required': ['id'],
|
||||
'properties': {
|
||||
'id': {
|
||||
'type': 'integer',
|
||||
'writeOnly': True,
|
||||
}
|
||||
},
|
||||
}
|
||||
obj_schema = SpecPath.from_spec(spec)
|
||||
|
||||
# readOnly properties may be admitted in a Response context
|
||||
result = unmarshaller_factory(
|
||||
|
@ -586,8 +748,20 @@ class TestSchemaUnmarshallerCall(object):
|
|||
'id': 10,
|
||||
}
|
||||
|
||||
# readOnly properties are not admitted on a Request context
|
||||
result = unmarshaller_factory(
|
||||
obj_schema, context=UnmarshalContext.RESPONSE)({"id": 10})
|
||||
def test_write_only_properties_invalid(self, unmarshaller_factory):
|
||||
spec = {
|
||||
'type': 'object',
|
||||
'required': ['id'],
|
||||
'properties': {
|
||||
'id': {
|
||||
'type': 'integer',
|
||||
'writeOnly': True,
|
||||
}
|
||||
},
|
||||
}
|
||||
obj_schema = SpecPath.from_spec(spec)
|
||||
|
||||
assert result == {}
|
||||
# readOnly properties are not admitted on a Request context
|
||||
with pytest.raises(InvalidSchemaValue):
|
||||
unmarshaller_factory(
|
||||
obj_schema, context=UnmarshalContext.RESPONSE)({"id": 10})
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue