Source code for messes.convert.user_input_checking

# -*- coding: utf-8 -*-
"""
Validates user input, erroring early and allowing the rest of the program to assume inputs are sanitized.
"""

import re
import sys
import pathlib

import jsonschema


[docs]def validate_conversion_directives(conversion_directives: dict, schema: dict): """Validate conversion directives. Wraps around jsonschema.validate() to give more human readable errors for most validation errors. Args: dict_to_validate: instance to validate. schema: JSON Schema to validate the instance with. Raises: jsonschema.ValidationError: any validation errors that aren't handled reraise the original. """ try: jsonschema.validate(conversion_directives, schema) except jsonschema.ValidationError as e: message = "ValidationError: An error was found in the " + schema["title"] + ".\n" custom_message = "" if e.validator == "minProperties": message += "The entry " + "[%s]" % "][".join(repr(index) for index in e.relative_path) + " cannot be empty." elif e.validator == "required": required_property = re.match(r"(\'.*\')", e.message).group(1) special_names = ["'code'", "'table'", "'fields'", "'headers'", "'record_id'", "'for_each'", "'override'"] if required_property in special_names: value_type_map = ["str", "matrix", "section"] value_type = value_type_map[e.schema_path[3]] if value_type == "str": message += "'str' type directives have 3 valid configurations:\n" +\ "\t1. They have an 'override' property.\n" +\ "\t2. They have a 'code' property.\n" +\ "\t3. They have the 'table' and 'fields' properties.\n" +\ "The entry "+ "[%s]" % "][".join(repr(index) for index in e.relative_path) +\ " is not one of the valid configurations." if value_type == "matrix": message += "'matrix' type directives must either have a 'code' property or 'headers' and 'table' properties.\n" +\ "The entry "+ "[%s]" % "][".join(repr(index) for index in e.relative_path) +\ " is missing one of these properties." if value_type == "section": message += "The entry " + "[%s]" % "][".join(repr(index) for index in e.relative_path) + " is missing the required property " + required_property + "." else: if len(e.relative_path) == 0: message += "The required property " + required_property + " is missing." else: message += "The entry " + "[%s]" % "][".join(repr(index) for index in e.relative_path) + " is missing the required property " + required_property + "." elif e.validator == "minLength": custom_message = " cannot be an empty string." elif e.validator == "maxLength": custom_message = " is too long." elif e.validator == "minItems": custom_message = " cannot be empty." elif e.validator == "type": if type(e.validator_value) == list: custom_message = " is not any of the allowed types: [" for allowed_type in e.validator_value: custom_message += "\'" + allowed_type + "\', " custom_message = custom_message[:-2] custom_message += "]." else: custom_message = " is not of type \"" + e.validator_value + "\"." elif e.validator == "enum": custom_message = " is not one of [" + "%s" % ", ".join(repr(index) for index in e.validator_value) + "]." elif e.validator == "format": custom_message = " is not a valid " + e.validator_value + "." elif e.validator == "minimum": custom_message = " must be greater than or equal to " + str(e.validator_value) + "." elif e.validator == "maximum": custom_message = " must be less than or equal to " + str(e.validator_value) + "." elif e.validator == "pattern": ## All of the properties except 'headers' are strings, so path will be 3, but headers is a list so path will be 4. property_name = e.relative_path[-2] if len(e.relative_path) == 4 else e.relative_path[-1] properties_with_patterns = ["for_each", "required", "fields_to_headers", "values_to_str"] if property_name in properties_with_patterns: message += "The '" + property_name + "' property for entry " + \ "[%s]" % "][".join(repr(index) for index in list(e.relative_path)[0:-1]) + \ " must be 'True' or 'False'." elif property_name == "test": message += "The '" + property_name + "' property for entry " + \ "[%s]" % "][".join(repr(index) for index in list(e.relative_path)[0:-1]) + \ " must have an '=' in the middle. Ex. type=MS" elif property_name == "headers": message += "Each element in the '" + property_name + "' property for entry " + \ "[%s]" % "][".join(repr(index) for index in list(e.relative_path)[0:-2]) + \ " must have an '=' in the middle. Ex. type=MS" elif property_name == "sort_order": message += "The '" + property_name + "' property for entry " + \ "[%s]" % "][".join(repr(index) for index in list(e.relative_path)[0:-1]) + \ " must be 'ascending' or 'descending'" else: raise e else: raise e if custom_message: message = message + "The value for " + "[%s]" % "][".join(repr(index) for index in e.relative_path) + custom_message print(message, file=sys.stderr) sys.exit()
# def additional_args_checks(args: dict): # """Run some checks on args that jsonschema can't do. # This assumes that args has been validated with a JSON schema and does some # further checking to make sure the values entered by the user make sense. # Prints a message and exits the program if problems are found. # Args: # args: the arguments entered into the program by the user. # """ # file_path_properties = ["--update", "--override", "<conversion_directives>", "<input_JSON>"] # for path in file_path_properties: # if args[path] and not pathlib.Path(args[path]).exists(): # print("Error: The value entered for " + path + " is not a valid file path or does not exist.", file=sys.stderr) # sys.exit()