Files
notifications-api/app/schema_validation/__init__.py
Leo Hemsted 5ec4829d00 fix v2 schema phone/email validation when non-str passed in
jsonschema states:

> A format attribute can generally only validate a given set of
> instance types. If the type of the instance to validate is not in
> this set, validation for this format attribute and instance SHOULD
> succeed.

We were not checking for the type of the input, and our validators were
behaving in unexpected manners (throwing TypeErrors etc etc). Despite
us declaring that the phone_number field is of type `str`, we still
need to make sure the validator passes gracefully, so that the inbuilt
type check can be the bit that catches if someone passes in a non-str
value. We've seen this with people passing in integers instead of strs
for phone numbers. This'll make them receive a nice 400 error
(e.g. "phone_number 12345 is not of type string"), rather than us
having a 500 internal server error
2017-05-10 11:04:12 +01:00

68 lines
2.2 KiB
Python

import json
from jsonschema import (Draft4Validator, ValidationError, FormatChecker)
from notifications_utils.recipients import (validate_phone_number, validate_email_address, InvalidPhoneError,
InvalidEmailError)
def validate(json_to_validate, schema):
format_checker = FormatChecker()
@format_checker.checks('phone_number', raises=InvalidPhoneError)
def validate_schema_phone_number(instance):
if isinstance(instance, str):
validate_phone_number(instance, international=True)
return True
@format_checker.checks('email_address', raises=InvalidEmailError)
def validate_schema_email_address(instance):
if isinstance(instance, str):
validate_email_address(instance)
return True
validator = Draft4Validator(schema, format_checker=format_checker)
errors = list(validator.iter_errors(json_to_validate))
if errors.__len__() > 0:
raise ValidationError(build_error_message(errors))
return json_to_validate
def build_error_message(errors):
fields = []
for e in errors:
field = (
"{} {}".format(e.path[0], e.schema['validationMessage'])
if 'validationMessage' in e.schema else __format_message(e)
)
fields.append({"error": "ValidationError", "message": field})
message = {
"status_code": 400,
"errors": fields
}
return json.dumps(message)
def __format_message(e):
def get_path(e):
error_path = None
try:
error_path = e.path.popleft()
# no need to catch IndexError exception explicity as
# error_path is None if e.path has no items
finally:
return error_path
def get_error_message(e):
# e.cause is an exception (such as InvalidPhoneError). if it's not present it was a standard jsonschema error
# such as a required field not being present
error_message = str(e.cause) if e.cause else e.message
return error_message.replace("'", '')
path = get_path(e)
message = get_error_message(e)
if path:
return "{} {}".format(path, message)
else:
return "{}".format(message)