Skip to content

Commit 552827e

Browse files
committed
Unmarshal any schema type
1 parent ce3c40f commit 552827e

File tree

4 files changed

+69
-6
lines changed

4 files changed

+69
-6
lines changed

openapi_core/schema/schemas/exceptions.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ class OpenAPISchemaError(OpenAPIMappingError):
55
pass
66

77

8+
class NoValidSchema(OpenAPISchemaError):
9+
pass
10+
11+
12+
class UndefinedItemsSchema(OpenAPISchemaError):
13+
pass
14+
15+
816
class InvalidSchemaValue(OpenAPISchemaError):
917
pass
1018

openapi_core/schema/schemas/models.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
from openapi_core.schema.schemas.enums import SchemaFormat, SchemaType
1010
from openapi_core.schema.schemas.exceptions import (
1111
InvalidSchemaValue, UndefinedSchemaProperty, MissingSchemaProperty,
12-
OpenAPISchemaError, NoOneOfSchema, MultipleOneOfSchema,
12+
OpenAPISchemaError, NoOneOfSchema, MultipleOneOfSchema, NoValidSchema,
13+
UndefinedItemsSchema,
1314
)
1415
from openapi_core.schema.schemas.util import forcebool, format_date
1516
from openapi_core.schema.schemas.validators import (
@@ -23,7 +24,6 @@ class Schema(object):
2324
"""Represents an OpenAPI Schema."""
2425

2526
DEFAULT_CAST_CALLABLE_GETTER = {
26-
SchemaType.ANY: lambda x: x,
2727
SchemaType.INTEGER: int,
2828
SchemaType.NUMBER: float,
2929
SchemaType.BOOLEAN: forcebool,
@@ -34,7 +34,7 @@ class Schema(object):
3434
})
3535

3636
VALIDATOR_CALLABLE_GETTER = {
37-
None: lambda x: x,
37+
SchemaType.ANY: lambda x: x,
3838
SchemaType.BOOLEAN: TypeValidator(bool),
3939
SchemaType.INTEGER: TypeValidator(integer_types, exclude=bool),
4040
SchemaType.NUMBER: TypeValidator(integer_types, float, exclude=bool),
@@ -94,7 +94,7 @@ def _get_all_required_properties(self):
9494

9595
return dict(
9696
(prop_name, val)
97-
for prop_name, val in all_properties.items()
97+
for prop_name, val in iteritems(all_properties)
9898
if prop_name in required
9999
)
100100

@@ -111,6 +111,7 @@ def get_cast_mapping(self):
111111
mapping = self.DEFAULT_CAST_CALLABLE_GETTER.copy()
112112
mapping.update({
113113
SchemaType.STRING: self._unmarshal_string,
114+
SchemaType.ANY: self._unmarshal_any,
114115
SchemaType.ARRAY: self._unmarshal_collection,
115116
SchemaType.OBJECT: self._unmarshal_object,
116117
})
@@ -165,7 +166,26 @@ def _unmarshal_string(self, value):
165166
value, self.format)
166167
)
167168

169+
def _unmarshal_any(self, value):
170+
types_resolve_order = [
171+
SchemaType.OBJECT, SchemaType.ARRAY, SchemaType.BOOLEAN,
172+
SchemaType.INTEGER, SchemaType.NUMBER, SchemaType.STRING,
173+
]
174+
cast_mapping = self.get_cast_mapping()
175+
for schema_type in types_resolve_order:
176+
cast_callable = cast_mapping[schema_type]
177+
try:
178+
return cast_callable(value)
179+
# @todo: remove ValueError when validation separated
180+
except (OpenAPISchemaError, ValueError):
181+
pass
182+
raise NoValidSchema(
183+
"No valid schema found for value {0}".format(value))
184+
168185
def _unmarshal_collection(self, value):
186+
if self.items is None:
187+
raise UndefinedItemsSchema("Undefined items' schema")
188+
169189
return list(map(self.items.unmarshal, value))
170190

171191
def _unmarshal_object(self, value):

tests/integration/data/v3.0/petstore.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,7 @@ components:
172172
oneOf:
173173
- type: string
174174
enum: [always, now]
175-
- type: string
176-
format: date-time
175+
- type: integer
177176
Address:
178177
type: object
179178
x-model: Address

tests/integration/test_petstore.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,3 +1080,39 @@ def test_post_tags_created_datetime(
10801080

10811081
assert response_result.errors == []
10821082
assert response_result.data == data_json
1083+
1084+
def test_post_tags_created_invalid_type(
1085+
self, spec, response_validator):
1086+
host_url = 'http://petstore.swagger.io/v1'
1087+
path_pattern = '/v1/tags'
1088+
pet_name = 'Dog'
1089+
data_json = {
1090+
'created': 'long time ago',
1091+
'name': pet_name,
1092+
}
1093+
data = json.dumps(data_json)
1094+
1095+
request = MockRequest(
1096+
host_url, 'POST', '/tags',
1097+
path_pattern=path_pattern, data=data,
1098+
)
1099+
1100+
parameters = request.get_parameters(spec)
1101+
body = request.get_body(spec)
1102+
1103+
assert parameters == {}
1104+
assert body == data_json
1105+
1106+
data_json = {
1107+
'code': 400,
1108+
'message': 'Bad request',
1109+
'rootCause': 'Tag already exist',
1110+
'additionalinfo': 'Tag Dog already exist',
1111+
}
1112+
data = json.dumps(data_json)
1113+
response = MockResponse(data, status_code=404)
1114+
1115+
response_result = response_validator.validate(request, response)
1116+
1117+
assert response_result.errors == []
1118+
assert response_result.data == data_json

0 commit comments

Comments
 (0)