diff --git a/openapi_core/media_types.py b/openapi_core/media_types.py index a4a34f2..5c52834 100644 --- a/openapi_core/media_types.py +++ b/openapi_core/media_types.py @@ -1,9 +1,17 @@ """OpenAPI core mediaTypes module""" +from collections import defaultdict + +from json import loads from six import iteritems from openapi_core.exceptions import InvalidValueType, InvalidMediaTypeValue +MEDIA_TYPE_DESERIALIZERS = { + 'application/json': loads, +} + + class MediaType(object): """Represents an OpenAPI MediaType.""" @@ -11,12 +19,29 @@ class MediaType(object): self.mimetype = mimetype self.schema = schema + def get_deserializer_mapping(self): + mapping = MEDIA_TYPE_DESERIALIZERS.copy() + return defaultdict(lambda: lambda x: x, mapping) + + def get_dererializer(self): + mapping = self.get_deserializer_mapping() + return mapping[self.mimetype] + + def deserialize(self, value): + deserializer = self.get_dererializer() + return deserializer(value) + def unmarshal(self, value): if not self.schema: return value try: - return self.schema.unmarshal(value) + deserialized = self.deserialize(value) + except ValueError as exc: + raise InvalidMediaTypeValue(str(exc)) + + try: + return self.schema.unmarshal(deserialized) except InvalidValueType as exc: raise InvalidMediaTypeValue(str(exc)) diff --git a/openapi_core/schemas.py b/openapi_core/schemas.py index 66793a6..a96c705 100644 --- a/openapi_core/schemas.py +++ b/openapi_core/schemas.py @@ -6,7 +6,6 @@ import warnings from distutils.util import strtobool from functools import lru_cache -from json import loads from six import iteritems from openapi_core.enums import SchemaType, SchemaFormat @@ -126,8 +125,8 @@ class Schema(object): return list(map(self.items.unmarshal, value)) def _unmarshal_object(self, value): - if isinstance(value, (str, bytes)): - value = loads(value) + if not isinstance(value, (dict, )): + raise InvalidValueType("Value of {0} not an object".format(value)) all_properties = self.get_all_properties() all_required_properties = self.get_all_required_properties() diff --git a/tests/integration/data/v3.0/petstore.yaml b/tests/integration/data/v3.0/petstore.yaml index 81fe7b1..ca54905 100644 --- a/tests/integration/data/v3.0/petstore.yaml +++ b/tests/integration/data/v3.0/petstore.yaml @@ -108,6 +108,21 @@ paths: $ref: "#/components/schemas/PetData" default: $ref: "#/components/responses/ErrorResponse" + /tags: + get: + summary: List all tags + operationId: listTags + tags: + - tags + responses: + '200': + description: Expected response to a valid request + content: + application/json: + schema: + $ref: "#/components/schemas/TagList" + default: + $ref: "#/components/responses/ErrorResponse" components: schemas: Address: @@ -186,6 +201,10 @@ components: properties: data: $ref: "#/components/schemas/Pet" + TagList: + type: array + items: + $ref: "#/components/schemas/Tag" Error: type: object required: diff --git a/tests/integration/test_petstore.py b/tests/integration/test_petstore.py index e67a6ab..c181589 100644 --- a/tests/integration/test_petstore.py +++ b/tests/integration/test_petstore.py @@ -655,3 +655,27 @@ class TestPetstore(object): assert response_result.errors == [] assert response_result.data == data_json + + def test_get_tags(self, spec, response_validator): + host_url = 'http://petstore.swagger.io/v1' + path_pattern = '/v1/tags' + + request = MockRequest( + host_url, 'GET', '/tags', + path_pattern=path_pattern, + ) + + parameters = request.get_parameters(spec) + body = request.get_body(spec) + + assert parameters == {} + assert body is None + + data_json = [] + data = json.dumps(data_json) + response = MockResponse(data) + + response_result = response_validator.validate(request, response) + + assert response_result.errors == [] + assert response_result.data == data_json diff --git a/tests/integration/test_validators.py b/tests/integration/test_validators.py index ab08bb5..d9816e2 100644 --- a/tests/integration/test_validators.py +++ b/tests/integration/test_validators.py @@ -4,6 +4,7 @@ import pytest from openapi_core.exceptions import ( InvalidServer, InvalidOperation, MissingParameter, MissingBody, InvalidContentType, InvalidResponse, InvalidMediaTypeValue, + InvalidValue, ) from openapi_core.shortcuts import create_spec from openapi_core.validators import RequestValidator, ResponseValidator @@ -239,6 +240,26 @@ class TestResponseValidator(object): assert result.data is None assert result.headers == {} + def test_invalid_value(self, validator): + request = MockRequest(self.host_url, 'get', '/v1/tags') + response_json = { + 'data': [ + { + 'id': 1, + 'name': 'Sparky' + }, + ], + } + response_data = json.dumps(response_json) + response = MockResponse(response_data) + + result = validator.validate(request, response) + + assert len(result.errors) == 1 + assert type(result.errors[0]) == InvalidValue + assert result.data is None + assert result.headers == {} + def test_get_pets(self, validator): request = MockRequest(self.host_url, 'get', '/v1/pets') response_json = {