Skip to content

Commit 0350562

Browse files
committed
Response JSON text as array fix
1 parent 4fc02af commit 0350562

File tree

5 files changed

+92
-4
lines changed

5 files changed

+92
-4
lines changed

openapi_core/media_types.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,47 @@
11
"""OpenAPI core mediaTypes module"""
2+
from collections import defaultdict
3+
4+
from json import loads
25
from six import iteritems
36

47
from openapi_core.exceptions import InvalidValueType, InvalidMediaTypeValue
58

69

10+
MEDIA_TYPE_DESERIALIZERS = {
11+
'application/json': loads,
12+
}
13+
14+
715
class MediaType(object):
816
"""Represents an OpenAPI MediaType."""
917

1018
def __init__(self, mimetype, schema=None):
1119
self.mimetype = mimetype
1220
self.schema = schema
1321

22+
def get_deserializer_mapping(self):
23+
mapping = MEDIA_TYPE_DESERIALIZERS.copy()
24+
return defaultdict(lambda: lambda x: x, mapping)
25+
26+
def get_dererializer(self):
27+
mapping = self.get_deserializer_mapping()
28+
return mapping[self.mimetype]
29+
30+
def deserialize(self, value):
31+
deserializer = self.get_dererializer()
32+
return deserializer(value)
33+
1434
def unmarshal(self, value):
1535
if not self.schema:
1636
return value
1737

1838
try:
19-
return self.schema.unmarshal(value)
39+
deserialized = self.deserialize(value)
40+
except ValueError as exc:
41+
raise InvalidMediaTypeValue(str(exc))
42+
43+
try:
44+
return self.schema.unmarshal(deserialized)
2045
except InvalidValueType as exc:
2146
raise InvalidMediaTypeValue(str(exc))
2247

openapi_core/schemas.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from distutils.util import strtobool
77
from functools import lru_cache
88

9-
from json import loads
109
from six import iteritems
1110

1211
from openapi_core.enums import SchemaType, SchemaFormat
@@ -126,8 +125,8 @@ def _unmarshal_collection(self, value):
126125
return list(map(self.items.unmarshal, value))
127126

128127
def _unmarshal_object(self, value):
129-
if isinstance(value, (str, bytes)):
130-
value = loads(value)
128+
if not isinstance(value, (dict, )):
129+
raise InvalidValueType("Value of {0} not an object".format(value))
131130

132131
all_properties = self.get_all_properties()
133132
all_required_properties = self.get_all_required_properties()

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,21 @@ paths:
108108
$ref: "#/components/schemas/PetData"
109109
default:
110110
$ref: "#/components/responses/ErrorResponse"
111+
/tags:
112+
get:
113+
summary: List all tags
114+
operationId: listTags
115+
tags:
116+
- tags
117+
responses:
118+
'200':
119+
description: Expected response to a valid request
120+
content:
121+
application/json:
122+
schema:
123+
$ref: "#/components/schemas/TagList"
124+
default:
125+
$ref: "#/components/responses/ErrorResponse"
111126
components:
112127
schemas:
113128
Address:
@@ -186,6 +201,10 @@ components:
186201
properties:
187202
data:
188203
$ref: "#/components/schemas/Pet"
204+
TagList:
205+
type: array
206+
items:
207+
$ref: "#/components/schemas/Tag"
189208
Error:
190209
type: object
191210
required:

tests/integration/test_petstore.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,3 +655,27 @@ def test_get_pet_not_found(self, spec, response_validator):
655655

656656
assert response_result.errors == []
657657
assert response_result.data == data_json
658+
659+
def test_get_tags(self, spec, response_validator):
660+
host_url = 'http://petstore.swagger.io/v1'
661+
path_pattern = '/v1/tags'
662+
663+
request = MockRequest(
664+
host_url, 'GET', '/tags',
665+
path_pattern=path_pattern,
666+
)
667+
668+
parameters = request.get_parameters(spec)
669+
body = request.get_body(spec)
670+
671+
assert parameters == {}
672+
assert body is None
673+
674+
data_json = []
675+
data = json.dumps(data_json)
676+
response = MockResponse(data)
677+
678+
response_result = response_validator.validate(request, response)
679+
680+
assert response_result.errors == []
681+
assert response_result.data == data_json

tests/integration/test_validators.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from openapi_core.exceptions import (
55
InvalidServer, InvalidOperation, MissingParameter,
66
MissingBody, InvalidContentType, InvalidResponse, InvalidMediaTypeValue,
7+
InvalidValue,
78
)
89
from openapi_core.shortcuts import create_spec
910
from openapi_core.validators import RequestValidator, ResponseValidator
@@ -239,6 +240,26 @@ def test_invalid_media_type_value(self, validator):
239240
assert result.data is None
240241
assert result.headers == {}
241242

243+
def test_invalid_value(self, validator):
244+
request = MockRequest(self.host_url, 'get', '/v1/tags')
245+
response_json = {
246+
'data': [
247+
{
248+
'id': 1,
249+
'name': 'Sparky'
250+
},
251+
],
252+
}
253+
response_data = json.dumps(response_json)
254+
response = MockResponse(response_data)
255+
256+
result = validator.validate(request, response)
257+
258+
assert len(result.errors) == 1
259+
assert type(result.errors[0]) == InvalidValue
260+
assert result.data is None
261+
assert result.headers == {}
262+
242263
def test_get_pets(self, validator):
243264
request = MockRequest(self.host_url, 'get', '/v1/pets')
244265
response_json = {

0 commit comments

Comments
 (0)