Reorganize how appliance creation is validated against JSON schemas.

This allows for clearer error messages when validation fails.
This commit is contained in:
grossmj committed 2018-11-17 18:12:46 +07:00
1 parent 71fcf855b4
commit 499ab9844a
19 files changed
+1524 -1325

No files matched your search

+6 -49
View File
@@ -36,30 +36,7 @@ from ..crash_report import CrashReport
from ..config import Config
# Add default values for missing entries in a request, largely taken from jsonschema documentation example
# https://python-jsonschema.readthedocs.io/en/latest/faq/#why-doesn-t-my-schema-s-default-property-set-the-default-on-my-instance
def extend_with_default(validator_class):
validate_properties = validator_class.VALIDATORS["properties"]
def set_defaults(validator, properties, instance, schema):
if jsonschema.Draft4Validator(schema).is_valid(instance):
# only add default for the matching sub-schema (e.g. when using 'oneOf')
for property, subschema in properties.items():
if "default" in subschema:
instance.setdefault(property, subschema["default"])
for error in validate_properties(validator, properties, instance, schema,):
yield error
return jsonschema.validators.extend(
validator_class, {"properties" : set_defaults},
)
ValidatorWithDefaults = extend_with_default(jsonschema.Draft4Validator)
async def parse_request(request, input_schema, raw, set_input_schema_defaults=False):
async def parse_request(request, input_schema, raw):
"""Parse body of request and raise HTTP errors in case of problems"""
request.json = {}
@@ -78,37 +55,18 @@ async def parse_request(request, input_schema, raw, set_input_schema_defaults=Fa
request.json[k] = v[0]
if input_schema:
if set_input_schema_defaults:
validator = ValidatorWithDefaults(input_schema)
else:
validator = jsonschema.Draft4Validator(input_schema)
try:
validator.validate(request.json)
jsonschema.validate(request.json, input_schema)
except jsonschema.ValidationError as e:
message = "JSON schema error with API request '{}': {}".format(request.path_qs, e.message)
if "is not valid under any of the given schemas" not in message:
best_match = jsonschema.exceptions.best_match(validator.iter_errors(request.json))
message += " (best matched error: {})".format(best_match.message)
message = "JSON schema error with API request '{}' and JSON data '{}': {}".format(request.path_qs,
request.json,
e.message)
log.error(message)
log.debug("Input schema: {}".format(json.dumps(input_schema)))
raise aiohttp.web.HTTPBadRequest(text=message)
return request
# if set_input_schema_defaults:
# validator = ValidatorWithDefaults(input_schema)
# else:
# validator = jsonschema.Draft4Validator(input_schema)
# error = jsonschema.exceptions.best_match(validator.iter_errors(request.json))
# if error:
# message = "JSON schema error with API request '{}' while validating JSON data '{}': {}".format(request.path_qs, request.json, error.message)
# log.error(message)
# log.debug("Input schema: {}".format(json.dumps(input_schema)))
# raise aiohttp.web.HTTPBadRequest(text=message)
#
# return request
class Route(object):
@@ -176,7 +134,6 @@ class Route(object):
input_schema = kw.get("input", {})
api_version = kw.get("api_version", 2)
raw = kw.get("raw", False)
set_input_schema_defaults = kw.get("set_input_schema_defaults", False)
def register(func):
# Add the type of server to the route
@@ -225,7 +182,7 @@ class Route(object):
return response
# API call
request = await parse_request(request, input_schema, raw, set_input_schema_defaults)
request = await parse_request(request, input_schema, raw)
record_file = server_config.get("record")
if record_file:
try: