mirror of
https://github.com/correl/openapi-core.git
synced 2024-11-26 03:00:10 +00:00
AllOf support
This commit is contained in:
parent
cffa0b4528
commit
1739828559
3 changed files with 78 additions and 17 deletions
|
@ -28,22 +28,32 @@ class Schema(object):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, schema_type, model=None, properties=None, items=None,
|
self, schema_type, model=None, properties=None, items=None,
|
||||||
spec_format=None, required=False, default=None, nullable=False,
|
spec_format=None, required=None, default=None, nullable=False,
|
||||||
enum=None, deprecated=False):
|
enum=None, deprecated=False, all_of=None):
|
||||||
self.type = schema_type
|
self.type = schema_type
|
||||||
self.model = model
|
self.model = model
|
||||||
self.properties = properties and dict(properties) or {}
|
self.properties = properties and dict(properties) or {}
|
||||||
self.items = items
|
self.items = items
|
||||||
self.format = spec_format
|
self.format = spec_format
|
||||||
self.required = required
|
self.required = required or []
|
||||||
self.default = default
|
self.default = default
|
||||||
self.nullable = nullable
|
self.nullable = nullable
|
||||||
self.enum = enum
|
self.enum = enum
|
||||||
self.deprecated = deprecated
|
self.deprecated = deprecated
|
||||||
|
self.all_of = all_of and list(all_of) or []
|
||||||
|
|
||||||
def __getitem__(self, name):
|
def __getitem__(self, name):
|
||||||
return self.properties[name]
|
return self.properties[name]
|
||||||
|
|
||||||
|
def get_all_properties(self):
|
||||||
|
properties = self.properties.copy()
|
||||||
|
|
||||||
|
for subschema in self.all_of:
|
||||||
|
subschema_props = subschema.get_all_properties()
|
||||||
|
properties.update(subschema_props)
|
||||||
|
|
||||||
|
return properties
|
||||||
|
|
||||||
def get_cast_mapping(self):
|
def get_cast_mapping(self):
|
||||||
mapping = DEFAULT_CAST_CALLABLE_GETTER.copy()
|
mapping = DEFAULT_CAST_CALLABLE_GETTER.copy()
|
||||||
mapping.update({
|
mapping.update({
|
||||||
|
@ -101,17 +111,18 @@ class Schema(object):
|
||||||
if isinstance(value, (str, bytes)):
|
if isinstance(value, (str, bytes)):
|
||||||
value = loads(value)
|
value = loads(value)
|
||||||
|
|
||||||
properties_keys = self.properties.keys()
|
all_properties = self.get_all_properties()
|
||||||
|
all_properties_keys = all_properties.keys()
|
||||||
value_keys = value.keys()
|
value_keys = value.keys()
|
||||||
|
|
||||||
extra_props = set(value_keys) - set(properties_keys)
|
extra_props = set(value_keys) - set(all_properties_keys)
|
||||||
|
|
||||||
if extra_props:
|
if extra_props:
|
||||||
raise UndefinedSchemaProperty(
|
raise UndefinedSchemaProperty(
|
||||||
"Undefined properties in schema: {0}".format(extra_props))
|
"Undefined properties in schema: {0}".format(extra_props))
|
||||||
|
|
||||||
properties = {}
|
properties = {}
|
||||||
for prop_name, prop in iteritems(self.properties):
|
for prop_name, prop in iteritems(all_properties):
|
||||||
try:
|
try:
|
||||||
prop_value = value[prop_name]
|
prop_value = value[prop_name]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -156,11 +167,16 @@ class SchemaFactory(object):
|
||||||
nullable = schema_deref.get('nullable', False)
|
nullable = schema_deref.get('nullable', False)
|
||||||
enum = schema_deref.get('enum', None)
|
enum = schema_deref.get('enum', None)
|
||||||
deprecated = schema_deref.get('deprecated', False)
|
deprecated = schema_deref.get('deprecated', False)
|
||||||
|
all_of_spec = schema_deref.get('allOf', None)
|
||||||
|
|
||||||
properties = None
|
properties = None
|
||||||
if properties_spec:
|
if properties_spec:
|
||||||
properties = self.properties_generator.generate(properties_spec)
|
properties = self.properties_generator.generate(properties_spec)
|
||||||
|
|
||||||
|
all_of = []
|
||||||
|
if all_of_spec:
|
||||||
|
all_of = map(self.create, all_of_spec)
|
||||||
|
|
||||||
items = None
|
items = None
|
||||||
if items_spec:
|
if items_spec:
|
||||||
items = self._create_items(items_spec)
|
items = self._create_items(items_spec)
|
||||||
|
@ -168,7 +184,7 @@ class SchemaFactory(object):
|
||||||
return Schema(
|
return Schema(
|
||||||
schema_type, model=model, properties=properties, items=items,
|
schema_type, model=model, properties=properties, items=items,
|
||||||
required=required, default=default, nullable=nullable, enum=enum,
|
required=required, default=default, nullable=nullable, enum=enum,
|
||||||
deprecated=deprecated,
|
deprecated=deprecated, all_of=all_of,
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -60,7 +60,7 @@ paths:
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/Pets"
|
$ref: "#/components/schemas/PetsData"
|
||||||
post:
|
post:
|
||||||
summary: Create a pet
|
summary: Create a pet
|
||||||
operationId: createPets
|
operationId: createPets
|
||||||
|
@ -101,7 +101,7 @@ paths:
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/Pets"
|
$ref: "#/components/schemas/PetData"
|
||||||
default:
|
default:
|
||||||
$ref: "#/components/responses/ErrorResponse"
|
$ref: "#/components/responses/ErrorResponse"
|
||||||
components:
|
components:
|
||||||
|
@ -154,12 +154,23 @@ components:
|
||||||
position:
|
position:
|
||||||
$ref: "#/components/schemas/Position"
|
$ref: "#/components/schemas/Position"
|
||||||
Pets:
|
Pets:
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
data:
|
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: "#/components/schemas/Pet"
|
$ref: "#/components/schemas/Pet"
|
||||||
|
PetsData:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- data
|
||||||
|
properties:
|
||||||
|
data:
|
||||||
|
$ref: "#/components/schemas/Pets"
|
||||||
|
PetData:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- data
|
||||||
|
properties:
|
||||||
|
data:
|
||||||
|
$ref: "#/components/schemas/Pet"
|
||||||
Error:
|
Error:
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
|
|
|
@ -16,7 +16,8 @@ from openapi_core.responses import Response
|
||||||
from openapi_core.schemas import Schema
|
from openapi_core.schemas import Schema
|
||||||
from openapi_core.servers import Server, ServerVariable
|
from openapi_core.servers import Server, ServerVariable
|
||||||
from openapi_core.shortcuts import create_spec
|
from openapi_core.shortcuts import create_spec
|
||||||
from openapi_core.wrappers import MockRequest
|
from openapi_core.validators import RequestValidator, ResponseValidator
|
||||||
|
from openapi_core.wrappers import MockRequest, MockResponse
|
||||||
|
|
||||||
|
|
||||||
class TestPetstore(object):
|
class TestPetstore(object):
|
||||||
|
@ -29,6 +30,14 @@ class TestPetstore(object):
|
||||||
def spec(self, spec_dict):
|
def spec(self, spec_dict):
|
||||||
return create_spec(spec_dict)
|
return create_spec(spec_dict)
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def request_validator(self, spec):
|
||||||
|
return RequestValidator(spec)
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def response_validator(self, spec):
|
||||||
|
return ResponseValidator(spec)
|
||||||
|
|
||||||
def test_spec(self, spec, spec_dict):
|
def test_spec(self, spec, spec_dict):
|
||||||
url = 'http://petstore.swagger.io/v1'
|
url = 'http://petstore.swagger.io/v1'
|
||||||
assert spec.info.title == spec_dict['info']['title']
|
assert spec.info.title == spec_dict['info']['title']
|
||||||
|
@ -99,7 +108,7 @@ class TestPetstore(object):
|
||||||
assert type(media_type.schema) == Schema
|
assert type(media_type.schema) == Schema
|
||||||
assert media_type.schema.type == schema_spec['type']
|
assert media_type.schema.type == schema_spec['type']
|
||||||
assert media_type.schema.required == schema_spec.get(
|
assert media_type.schema.required == schema_spec.get(
|
||||||
'required', False)
|
'required', [])
|
||||||
|
|
||||||
for parameter_name, parameter in iteritems(
|
for parameter_name, parameter in iteritems(
|
||||||
response.headers):
|
response.headers):
|
||||||
|
@ -121,7 +130,7 @@ class TestPetstore(object):
|
||||||
assert type(parameter.schema) == Schema
|
assert type(parameter.schema) == Schema
|
||||||
assert parameter.schema.type == schema_spec['type']
|
assert parameter.schema.type == schema_spec['type']
|
||||||
assert parameter.schema.required == schema_spec.get(
|
assert parameter.schema.required == schema_spec.get(
|
||||||
'required', False)
|
'required', [])
|
||||||
|
|
||||||
request_body_spec = operation_spec.get('requestBody')
|
request_body_spec = operation_spec.get('requestBody')
|
||||||
|
|
||||||
|
@ -161,7 +170,7 @@ class TestPetstore(object):
|
||||||
for schema_name, schema in iteritems(spec.components.schemas):
|
for schema_name, schema in iteritems(spec.components.schemas):
|
||||||
assert type(schema) == Schema
|
assert type(schema) == Schema
|
||||||
|
|
||||||
def test_get_pets(self, spec):
|
def test_get_pets(self, spec, response_validator):
|
||||||
host_url = 'http://petstore.swagger.io/v1'
|
host_url = 'http://petstore.swagger.io/v1'
|
||||||
path_pattern = '/v1/pets'
|
path_pattern = '/v1/pets'
|
||||||
query_params = {
|
query_params = {
|
||||||
|
@ -187,6 +196,17 @@ class TestPetstore(object):
|
||||||
}
|
}
|
||||||
assert body is None
|
assert body is None
|
||||||
|
|
||||||
|
data_json = {
|
||||||
|
'data': [],
|
||||||
|
}
|
||||||
|
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
|
||||||
|
|
||||||
def test_get_pets_wrong_parameter_type(self, spec):
|
def test_get_pets_wrong_parameter_type(self, spec):
|
||||||
host_url = 'http://petstore.swagger.io/v1'
|
host_url = 'http://petstore.swagger.io/v1'
|
||||||
path_pattern = '/v1/pets'
|
path_pattern = '/v1/pets'
|
||||||
|
@ -439,7 +459,7 @@ class TestPetstore(object):
|
||||||
with pytest.raises(InvalidServer):
|
with pytest.raises(InvalidServer):
|
||||||
request.get_body(spec)
|
request.get_body(spec)
|
||||||
|
|
||||||
def test_get_pet(self, spec):
|
def test_get_pet(self, spec, response_validator):
|
||||||
host_url = 'http://petstore.swagger.io/v1'
|
host_url = 'http://petstore.swagger.io/v1'
|
||||||
path_pattern = '/v1/pets/{petId}'
|
path_pattern = '/v1/pets/{petId}'
|
||||||
view_args = {
|
view_args = {
|
||||||
|
@ -461,3 +481,17 @@ class TestPetstore(object):
|
||||||
body = request.get_body(spec)
|
body = request.get_body(spec)
|
||||||
|
|
||||||
assert body is None
|
assert body is None
|
||||||
|
|
||||||
|
data_json = {
|
||||||
|
'data': {
|
||||||
|
'id': 1,
|
||||||
|
'name': 'test',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
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
|
||||||
|
|
Loading…
Reference in a new issue