Skip to content

remove

trestle.core.commands.remove ¤

Trestle Remove Command.

logger ¤

Classes¤

RemoveCmd (CommandPlusDocs) ¤

Remove a subcomponent from an existing model.

Source code in trestle/core/commands/remove.py
class RemoveCmd(CommandPlusDocs):
    """Remove a subcomponent from an existing model."""

    name = 'remove'

    def _init_arguments(self) -> None:
        self.add_argument(
            f'-{const.ARG_FILE_SHORT}',
            f'--{const.ARG_FILE}',
            help=const.ARG_DESC_FILE + ' to remove component/subcomponent to.',
            required=True
        )
        self.add_argument(
            f'-{const.ARG_ELEMENT_SHORT}',
            f'--{const.ARG_ELEMENT}',
            help=const.ARG_DESC_ELEMENT + ' to remove.',
            required=True
        )

    def _run(self, args: argparse.Namespace) -> int:
        """Remove an OSCAL component/subcomponent to the specified component.

        This method takes input a filename and a list of comma-seperated element path. Element paths are field aliases.
        The method first finds the parent model from the file and loads the file into the model.
        Then the method executes 'remove' for each of the element paths specified.
        """
        try:
            log.set_log_level_from_args(args)
            args_dict = args.__dict__

            file_path = pathlib.Path(args_dict[const.ARG_FILE]).resolve()
            relative_path = file_path.relative_to(args.trestle_root)
            # Get parent model and then load json into parent model
            parent_model, parent_alias = ModelUtils.get_relative_model_type(relative_path)

            parent_object = parent_model.oscal_read(file_path)
            parent_element = Element(parent_object, parent_alias)

            add_plan = Plan()

            # Do _remove for each element_path specified in args
            element_paths: List[str] = str(args_dict[const.ARG_ELEMENT]).split(',')
            for elm_path_str in element_paths:
                element_path = ElementPath(elm_path_str)
                remove_action, parent_element = self.remove(element_path, parent_element)
                add_plan.add_action(remove_action)

            create_action = CreatePathAction(file_path, True)
            write_action = WriteFileAction(file_path, parent_element, FileContentType.to_content_type(file_path.suffix))
            add_plan.add_action(remove_action)
            add_plan.add_action(create_action)
            add_plan.add_action(write_action)

            add_plan.execute()

            return CmdReturnCodes.SUCCESS.value

        except Exception as e:
            return err.handle_generic_command_exception(e, logger, 'Error while removing OSCAL component')

    @classmethod
    def remove(cls, element_path: ElementPath, parent_element: Element) -> Tuple[RemoveAction, Element]:
        """For the element_path, remove a model from the parent_element of a given parent_model.

        First we check if there is an existing element at that path
        If not, we complain.
        Then we set up an action plan to update the model (specified by file_path) in memory,
        return the action and return the parent_element.

        LIMITATIONS:
        1. This does not remove elements of a list or dict. Instead, the entire list or dict is removed.
        2. This cannot remove arbitrarily named elements that are not specified in the schema.
        For example, "responsible-parties" contains named elements, e.g., "organisation". The tool will not
        remove the "organisation" as it is not in the schema, but one can remove its elements, e.g., "party-uuids".
        """
        element_path_list = element_path.get_full_path_parts()
        if '*' in element_path_list:
            raise err.TrestleError('trestle remove does not support Wildcard element path.')

        deleting_element = parent_element.get_at(element_path)

        if deleting_element is not None:
            # The element already exists
            if type(deleting_element) is list:
                logger.warning(
                    'Warning: trestle remove does not support removing elements of a list: '
                    'this removes the entire list'
                )
            elif type(deleting_element) is dict:
                logger.warning(
                    'Warning: trestle remove does not support removing dict elements: '
                    'this removes the entire dict element'
                )
        else:
            raise err.TrestleError(f'Bad element path: {str(element_path)}')

        remove_action = RemoveAction(parent_element, element_path)

        return remove_action, parent_element
name ¤
Methods¤
remove(element_path, parent_element) classmethod ¤

For the element_path, remove a model from the parent_element of a given parent_model.

First we check if there is an existing element at that path If not, we complain. Then we set up an action plan to update the model (specified by file_path) in memory, return the action and return the parent_element.

LIMITATIONS: 1. This does not remove elements of a list or dict. Instead, the entire list or dict is removed. 2. This cannot remove arbitrarily named elements that are not specified in the schema. For example, "responsible-parties" contains named elements, e.g., "organisation". The tool will not remove the "organisation" as it is not in the schema, but one can remove its elements, e.g., "party-uuids".

Source code in trestle/core/commands/remove.py
@classmethod
def remove(cls, element_path: ElementPath, parent_element: Element) -> Tuple[RemoveAction, Element]:
    """For the element_path, remove a model from the parent_element of a given parent_model.

    First we check if there is an existing element at that path
    If not, we complain.
    Then we set up an action plan to update the model (specified by file_path) in memory,
    return the action and return the parent_element.

    LIMITATIONS:
    1. This does not remove elements of a list or dict. Instead, the entire list or dict is removed.
    2. This cannot remove arbitrarily named elements that are not specified in the schema.
    For example, "responsible-parties" contains named elements, e.g., "organisation". The tool will not
    remove the "organisation" as it is not in the schema, but one can remove its elements, e.g., "party-uuids".
    """
    element_path_list = element_path.get_full_path_parts()
    if '*' in element_path_list:
        raise err.TrestleError('trestle remove does not support Wildcard element path.')

    deleting_element = parent_element.get_at(element_path)

    if deleting_element is not None:
        # The element already exists
        if type(deleting_element) is list:
            logger.warning(
                'Warning: trestle remove does not support removing elements of a list: '
                'this removes the entire list'
            )
        elif type(deleting_element) is dict:
            logger.warning(
                'Warning: trestle remove does not support removing dict elements: '
                'this removes the entire dict element'
            )
    else:
        raise err.TrestleError(f'Bad element path: {str(element_path)}')

    remove_action = RemoveAction(parent_element, element_path)

    return remove_action, parent_element

handler: python