Fixing Subschema Required Properties Validation

Currently if valid swagger syntax is used for model composition an
error will be thrown due to the lack of a type property. This was
corrected by making object the default type.

schema_type = schema_deref.get('type', 'object')

I changed the swagger definition to test for this. Now PetCreate is a
composite of PetCreatePartOne and PetCreatePartTwo. However, this
caused `test_post_pets_empty_body` to fail, which turned out to be a
bug in the required properties.

In `_unmarshal_object` the `get_all_properties` method is called to get
all properties from the subschemas. However, this is not done for
required properties, meaning that only top level required properties
will be correctly validated. I have added a
`get_all_required_properties’ to fix this.

This caused `test_get_pets` to fail. In this case the bug allowed an
incorrect test case to be introduced. Pet requires `id`, but it also
requires name because it inherits from PetCreate. I have fixed this
test case by adding the missing required property.

After these changes `test_get_pet_not_found` failed due to a string
formatting error (double quotes vs single quotes). I fixed this by
switching to dictionary comparisons.
This commit is contained in:
amcmanigal 2018-04-04 10:15:45 -04:00
parent 8d92c25c11
commit 56be4b10eb
5 changed files with 27 additions and 4 deletions

3
.gitignore vendored
View file

@ -99,3 +99,6 @@ ENV/
# mypy
.mypy_cache/
# Jetbrains project files
.idea/

View file

@ -63,6 +63,15 @@ class Schema(object):
return properties
def get_all_required_properties(self):
required = self.required.copy()
for subschema in self.all_of:
subschema_req = subschema.get_all_required_properties()
required += subschema_req
return required
def get_cast_mapping(self):
mapping = DEFAULT_CAST_CALLABLE_GETTER.copy()
mapping.update({
@ -121,6 +130,7 @@ class Schema(object):
value = loads(value)
all_properties = self.get_all_properties()
all_required_properties = self.get_all_required_properties()
all_properties_keys = all_properties.keys()
value_keys = value.keys()
@ -135,7 +145,7 @@ class Schema(object):
try:
prop_value = value[prop_name]
except KeyError:
if prop_name in self.required:
if prop_name in all_required_properties:
raise MissingProperty(
"Missing schema property {0}".format(prop_name))
if not prop.nullable and not prop.default:
@ -167,7 +177,7 @@ class SchemaFactory(object):
def create(self, schema_spec):
schema_deref = self.dereferencer.dereference(schema_spec)
schema_type = schema_deref.get('type')
schema_type = schema_deref.get('type', 'object')
schema_format = schema_deref.get('format')
model = schema_deref.get('x-model', None)
required = schema_deref.get('required', False)

View file

@ -144,8 +144,13 @@ components:
type: integer
format: int64
PetCreate:
type: object
x-model: PetCreate
allOf:
- $ref: "#/components/schemas/PetCreatePartOne"
- $ref: "#/components/schemas/PetCreatePartTwo"
PetCreatePartOne:
type: object
x-model: PetCreatePartOne
required:
- name
properties:
@ -155,6 +160,10 @@ components:
$ref: "#/components/schemas/Tag"
address:
$ref: "#/components/schemas/Address"
PetCreatePartTwo:
type: object
x-model: PetCreatePartTwo
properties:
position:
$ref: "#/components/schemas/Position"
healthy:

View file

@ -654,4 +654,4 @@ class TestPetstore(object):
response_result = response_validator.validate(request, response)
assert response_result.errors == []
assert response_result.data == data
assert response_result.data == data_json

View file

@ -245,6 +245,7 @@ class TestResponseValidator(object):
'data': [
{
'id': 1,
'name': 'Sparky'
},
],
}