def validate_dict( self, change_dict: Mapping[str, AcceptableChangeDictTypes] ) -> None: """Checks that the command in change dict is valid for the domain object. Args: change_dict: dict. A dict of changes with keys as a cmd and the attributes of a command. Raises: ValidationError. The change dict does not contain the cmd key, or the cmd name is not allowed for the Change domain object or the command attributes are missing or extra. DeprecatedCommandError. The change dict contains a deprecated command or the value for the command attribute is deprecated. """ if 'cmd' not in change_dict: raise utils.ValidationError('Missing cmd key in change dict') cmd_name = change_dict['cmd'] # Ruling out the possibility of different types for mypy type checking. assert isinstance(cmd_name, str) valid_cmd_attribute_specs = None all_allowed_commands = ( self.ALLOWED_COMMANDS + self.COMMON_ALLOWED_COMMANDS) for cmd in all_allowed_commands: if cmd['name'] == cmd_name: valid_cmd_attribute_specs = copy.deepcopy(cmd) break if cmd_name in self.DEPRECATED_COMMANDS: raise utils.DeprecatedCommandError( 'Command %s is deprecated' % cmd_name) if not valid_cmd_attribute_specs: raise utils.ValidationError('Command %s is not allowed' % cmd_name) # Here we are deleting the 'name' key which cause MyPy to throw error, # because MyPy does not allow key deletion from TypedDict. So to silent # the error, we added an ignore here. valid_cmd_attribute_specs.pop('name', None) # type: ignore[misc] actual_cmd_attributes = copy.deepcopy(change_dict) # Here, `actual_cmd_attributes` is of type Mapping and Mapping does not # contain extra methods (e.g: .pop()). But here we are accessing `pop()` # method, which causes MyPy to throw error. Thus to avoid the error, # we used ignore here. actual_cmd_attributes.pop('cmd', None) # type: ignore[attr-defined] validate_cmd( cmd_name, valid_cmd_attribute_specs, actual_cmd_attributes)
def validate_dict(self, change_dict): """Checks that the command in change dict is valid for the domain object. Args: change_dict: dict. A dict of changes with keys as a cmd and the attributes of a command. Raises: ValidationError. The change dict does not contain the cmd key, or the cmd name is not allowed for the Change domain object or the command attributes are missing or extra. DeprecatedCommandError. The change dict contains a deprecated command or the value for the command attribute is deprecated. """ if 'cmd' not in change_dict: raise utils.ValidationError('Missing cmd key in change dict') cmd_name = change_dict['cmd'] valid_cmd_attribute_specs = None all_allowed_commands = (self.ALLOWED_COMMANDS + self.COMMON_ALLOWED_COMMANDS) for cmd in all_allowed_commands: if cmd['name'] == cmd_name: valid_cmd_attribute_specs = copy.deepcopy(cmd) break if cmd_name in self.DEPRECATED_COMMANDS: raise utils.DeprecatedCommandError('Command %s is deprecated' % cmd_name) if not valid_cmd_attribute_specs: raise utils.ValidationError('Command %s is not allowed' % cmd_name) valid_cmd_attribute_specs.pop('name', None) actual_cmd_attributes = copy.deepcopy(change_dict) actual_cmd_attributes.pop('cmd', None) validate_cmd(cmd_name, valid_cmd_attribute_specs, actual_cmd_attributes)
def validate_cmd(cmd_name, valid_cmd_attribute_specs, actual_cmd_attributes): """Validates that the attributes of a command contain all the required attributes and some/all of optional attributes. It also checks that the values of attributes belong to a set of allowed values if any. Args: cmd_name: str. The command for which validation process is being done. valid_cmd_attribute_specs: dict. A dict containing the required and optional attributes for a command along with allowed values for attributes if any. actual_cmd_attributes: dict. A dict containing the actual attributes of a command with values for the attributes. Raises: ValidationError. Any required attribute is missing or an extra attribute exists or the value of an attribute is not allowed. DeprecatedCommandError. The value of any attribute is deprecated. """ required_attribute_names = valid_cmd_attribute_specs[ 'required_attribute_names'] optional_attribute_names = valid_cmd_attribute_specs[ 'optional_attribute_names'] actual_attribute_names = list(actual_cmd_attributes.keys()) missing_attribute_names = [ key for key in required_attribute_names if key not in (actual_attribute_names) ] extra_attribute_names = [ key for key in actual_attribute_names if key not in (required_attribute_names + optional_attribute_names) ] error_msg_list = [] if missing_attribute_names: error_msg_list.append( 'The following required attributes are missing: %s' % ((', ').join(sorted(missing_attribute_names)))) if extra_attribute_names: error_msg_list.append( 'The following extra attributes are present: %s' % ((', ').join(sorted(extra_attribute_names)))) if error_msg_list: raise utils.ValidationError((', ').join(error_msg_list)) deprecated_values = valid_cmd_attribute_specs.get('deprecated_values', {}) for attribute_name, attribute_values in deprecated_values.items(): actual_value = actual_cmd_attributes.get(attribute_name) if actual_value in attribute_values: raise utils.DeprecatedCommandError( 'Value for %s in cmd %s: %s is deprecated' % (attribute_name, cmd_name, actual_value)) allowed_values = valid_cmd_attribute_specs.get('allowed_values') if not allowed_values: return for attribute_name, attribute_values in allowed_values.items(): actual_value = actual_cmd_attributes[attribute_name] if actual_value not in attribute_values: raise utils.ValidationError( 'Value for %s in cmd %s: %s is not allowed' % (attribute_name, cmd_name, actual_value))