Add example verification when (possibly multiple) global examples are provided below the schema level

Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
DL6ER
2023-01-03 22:08:55 +01:00
parent f2d68f20d7
commit 6d7239dbc8
+30 -16
View File
@@ -38,8 +38,10 @@ class ResponseVerifyer():
self.errors.append("Method " + method + " not found in OpenAPI specs (" + endpoint + ")")
return self.errors
# Get YAML response schema
YAMLresponseSchema = self.openapi.paths[endpoint][method]['responses'][str(rcode)]['content']['application/json']['schema']
# Get YAML response schema and examples (if applicable)
jsonData = self.openapi.paths[endpoint][method]['responses'][str(rcode)]['content']['application/json']
YAMLresponseSchema = jsonData['schema']
YAMLresponseExamples = jsonData['examples'] if 'examples' in jsonData else None
# Prepare required parameters (if any)
FTLparameters = []
if 'parameters' in self.openapi.paths[endpoint][method]:
@@ -53,26 +55,32 @@ class ResponseVerifyer():
continue
FTLparameters.append(param['name'] + "=" + urllib.parse.quote_plus(str(param['example'])))
self.openapi.paths[endpoint][method]['responses'][str(rcode)]['content']['application/json']['schema']
FTLresponse = self.ftl.getFTLresponse("/api" + endpoint, FTLparameters)
if FTLresponse is None:
return self.ftl.errors
if 'type' in YAMLresponseSchema and YAMLresponseSchema['type'] == 'object':
for prop in YAMLresponseSchema['properties']:
self.verify_property(YAMLresponseSchema['properties'], FTLresponse, [prop])
self.verify_property(YAMLresponseSchema['properties'], YAMLresponseExamples, FTLresponse, [prop])
elif 'allOf' in YAMLresponseSchema and len(YAMLresponseSchema['allOf']) > 0:
for i in range(len(YAMLresponseSchema['allOf'])):
for prop in YAMLresponseSchema['allOf'][i]['properties']:
self.verify_property(YAMLresponseSchema['allOf'][i]['properties'], FTLresponse, [prop])
self.verify_property(YAMLresponseSchema['allOf'][i]['properties'], YAMLresponseExamples, FTLresponse, [prop])
else:
self.errors.append("Top-level response should be either an object or a non-empty allOf/anyOf/oneOf")
return self.errors
def verify_property(self, YAMLprops: dict, FTLprops: dict, props: list):
def verify_type(self, prop_type: any, yaml_type: str, yaml_nullable: bool):
# None is an acceptable reply when this is specified in the API specs
if prop_type == NoneType and yaml_nullable:
return True
return prop_type in self.YAML_TYPES[yaml_type]
def verify_property(self, YAMLprops: dict, YAMLexamples: dict, FTLprops: dict, props: list):
all_okay = True
if props[-1] not in YAMLprops:
@@ -80,34 +88,40 @@ class ResponseVerifyer():
return False
YAMLprop = YAMLprops[props[-1]]
if props[-1] not in FTLprops:
self.errors.append("Property " + props[-1] + " missing in FTL' response")
self.errors.append("Property " + props[-1] + " missing in FTL's response")
return False
FTLprop = FTLprops[props[-1]]
# If this is another object, we have to dive deeper
if YAMLprop['type'] == 'object':
for prop in YAMLprop['properties']:
if not self.verify_property(YAMLprop['properties'], FTLprop, props + [prop]):
if not self.verify_property(YAMLprop['properties'], YAMLexamples, FTLprop, props + [prop]):
all_okay = False
else:
# Check this property
full_path = " => ".join(props)
yaml_type = YAMLprop['type']
yaml_nullable = 'nullable' in YAMLprop and YAMLprop['nullable'] == True
# Check type of YAML example (if defined)
if 'example' in YAMLprop:
example_type = type(YAMLprop['example'])
if example_type not in self.YAML_TYPES[yaml_type]:
self.errors.append(f"API example ({str(yaml_type)}) does not match defined type ({yaml_type}) in {full_path}")
if not self.verify_type(example_type, yaml_type, yaml_nullable):
self.errors.append(f"API example ({str(example_type)}) does not match defined type ({yaml_type}) in {full_path}")
return False
elif YAMLexamples is not None:
for t in YAMLexamples:
example = YAMLexamples[t]['value']
for p in props:
example = example[p]
example_type = type(example)
if not self.verify_type(example_type, yaml_type, yaml_nullable):
self.errors.append(f"API example ({str(example_type)}) does not match defined type ({yaml_type}) in {full_path}")
return False
ftl_type = type(FTLprop)
# None is an acceptable reply when this is specified in the API specs
if ftl_type == NoneType and 'nullable' in YAMLprop and YAMLprop['nullable'] == True:
pass
# Compare type of FTL's reply against what we defined in the API specs
elif ftl_type not in self.YAML_TYPES[yaml_type]:
ftl_type = type(FTLprop)
if not self.verify_type(ftl_type, yaml_type, yaml_nullable):
self.errors.append(f"FTL's reply ({str(ftl_type)}) does not match defined type ({yaml_type}) in {full_path}")
return False
return all_okay