Example #1
0
    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)
Example #2
0
    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)
Example #3
0
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))