Exemplo n.º 1
0
 def validate(self):
     errors = {}
     try:
         self._run, self._arguments_api = get_app_action_api(
             self.app_name, self.action_name)
         if is_app_action_bound(self.app_name,
                                self._run) and not self.device_id:
             message = 'App action is bound but no device ID was provided.'.format(
                 self.name)
             errors['executable'] = message
         validate_app_action_parameters(self._arguments_api, self.arguments,
                                        self.app_name, self.action_name)
     except UnknownApp:
         errors['executable'] = 'Unknown app {}'.format(self.app_name)
     except UnknownAppAction:
         errors['executable'] = 'Unknown app action {}'.format(
             self.action_name)
     except InvalidArgument as e:
         errors['arguments'] = e.errors
     if errors:
         raise InvalidExecutionElement(self.id,
                                       self.action_name,
                                       'Invalid action {}'.format(
                                           self.id or self.action_name),
                                       errors=[errors])
Exemplo n.º 2
0
 def set_arguments(self, new_arguments):
     """Updates the arguments for an Action object.
     Args:
         new_arguments ([Argument]): The new Arguments for the Action object.
     """
     validate_app_action_parameters(self._arguments_api, new_arguments,
                                    self.app_name, self.action_name)
     self.arguments = new_arguments
Exemplo n.º 3
0
 def validate(self):
     """Validates the object"""
     errors = []
     try:
         self._run, self._arguments_api = get_app_action_api(self.app_name, self.action_name)
         self._action_executable = get_app_action(self.app_name, self._run)
         if is_app_action_bound(self.app_name, self._run) and not self.device_id:
             message = 'App action is bound but no device ID was provided.'.format(self.name)
             errors.append(message)
         validate_app_action_parameters(self._arguments_api, self.arguments, self.app_name, self.action_name)
     except UnknownApp:
         errors.append('Unknown app {}'.format(self.app_name))
     except UnknownAppAction:
         errors.append('Unknown app action {}'.format(self.action_name))
     except InvalidArgument as e:
         errors.extend(e.errors)
     self.errors = errors
Exemplo n.º 4
0
 def validate(self):
     """Validates the object"""
     errors = []
     try:
         self._run, self._arguments_api = get_app_action_api(self.app_name, self.action_name)
         get_app_action(self.app_name, self._run)
         if is_app_action_bound(self.app_name, self._run) and not self.device_id:
             message = 'App action is bound but no device ID was provided.'.format(self.name)
             errors.append(message)
         validate_app_action_parameters(self._arguments_api, self.arguments, self.app_name, self.action_name)
     except UnknownApp:
         errors.append('Unknown app {}'.format(self.app_name))
     except UnknownAppAction:
         errors.append('Unknown app action {}'.format(self.action_name))
     except InvalidArgument as e:
         errors.extend(e.errors)
     self.errors = errors
Exemplo n.º 5
0
    def execute(self, accumulator, instance=None, arguments=None, resume=False):
        """Executes an Action by calling the associated app function.

        Args:
            accumulator (dict): Dict containing the results of the previous actions
            instance (App, optional): The instance of an App object to be used to execute the associated function.
                This field is required if the Action is a bounded action. Otherwise, it defaults to None.
            arguments (list[Argument], optional): List of Arguments to be used if the Action is the starting step of
                the Workflow. Defaults to None.
            resume (bool, optional): Optional boolean to resume a previously paused workflow. Defaults to False.

        Returns:
            (ActionResult): The result of the executed function.
        """
        logger.info('Executing action {} (id={})'.format(self.name, str(self.name)))
        self._execution_id = str(uuid.uuid4())

        if self.device_id:
            self._resolved_device_id = self.device_id.get_value(accumulator)
            logger.debug('Device resolved to {} for action {}'.format(self._resolved_device_id, str(self.id)))

        if arguments:
            WalkoffEvent.CommonWorkflowSignal.send(self, event=WalkoffEvent.ActionStarted,
                                                   data={'start_arguments': arguments})
        else:
            WalkoffEvent.CommonWorkflowSignal.send(self, event=WalkoffEvent.ActionStarted)

        if self.trigger and not resume:
            WalkoffEvent.CommonWorkflowSignal.send(self, event=WalkoffEvent.TriggerActionAwaitingData)
            logger.debug('Trigger Action {} is awaiting data'.format(self.name))
            self._output = None
            return ActionResult("trigger", "trigger")

        arguments = arguments if arguments else self.arguments

        try:
            args = validate_app_action_parameters(self._arguments_api, arguments, self.app_name, self.action_name,
                                                  accumulator=accumulator)
            if is_app_action_bound(self.app_name, self._run):
                result = self._action_executable(instance, **args)
            else:
                result = self._action_executable(**args)
            result.set_default_status(self.app_name, self.action_name)
            if result.is_failure(self.app_name, self.action_name):
                WalkoffEvent.CommonWorkflowSignal.send(self, event=WalkoffEvent.ActionExecutionError,
                                                       data=result.as_json())
            else:
                WalkoffEvent.CommonWorkflowSignal.send(self, event=WalkoffEvent.ActionExecutionSuccess,
                                                       data=result.as_json())
        except Exception as e:
            logger.exception('Error executing action {} (id={})'.format(self.name, str(self.id)))
            self.__handle_execution_error(e)
        else:
            self._output = result
            logger.debug(
                'Action {0}-{1} (id {2}) executed successfully'.format(self.app_name, self.action_name, self.id))
            return result
Exemplo n.º 6
0
 def _update_json(self, updated_json):
     self.action_name = updated_json['action_name']
     self.app_name = updated_json['app_name']
     self.device_id = updated_json[
         'device_id'] if 'device_id' in updated_json else None
     arguments = {}
     if 'arguments' in updated_json:
         for argument_json in updated_json['arguments']:
             argument = Argument(**argument_json)
             arguments[argument.name] = argument
     if arguments is not None:
         if not self.templated:
             validate_app_action_parameters(self._arguments_api, arguments,
                                            self.app_name, self.action_name)
     else:
         validate_app_action_parameters(self._arguments_api, [],
                                        self.app_name, self.action_name)
     self.arguments = arguments
Exemplo n.º 7
0
    def execute(self, instance, accumulator):
        """Executes an Action by calling the associated app function.

        Args:
            instance (App): The instance of an App object to be used to execute the associated function.
            accumulator (dict): Dict containing the results of the previous actions

        Returns:
            The result of the executed function.
        """
        self._execution_uid = str(uuid.uuid4())

        WalkoffEvent.CommonWorkflowSignal.send(
            self, event=WalkoffEvent.ActionStarted)

        if self.triggers:
            WalkoffEvent.CommonWorkflowSignal.send(
                self, event=WalkoffEvent.TriggerActionAwaitingData)
            logger.debug('Trigger Action {} is awaiting data'.format(
                self.name))
            self._wait_for_trigger(accumulator)

        try:
            args = validate_app_action_parameters(self._arguments_api,
                                                  self.arguments,
                                                  self.app_name,
                                                  self.action_name,
                                                  accumulator=accumulator)
            if is_app_action_bound(self.app_name, self._run):
                result = self._action_executable(instance, **args)
            else:
                result = self._action_executable(**args)
            result.set_default_status(self.app_name, self.action_name)
            if result.is_failure(self.app_name, self.action_name):
                WalkoffEvent.CommonWorkflowSignal.send(
                    self,
                    event=WalkoffEvent.ActionExecutionError,
                    data=result.as_json())
            else:
                WalkoffEvent.CommonWorkflowSignal.send(
                    self,
                    event=WalkoffEvent.ActionExecutionSuccess,
                    data=result.as_json())
        except Exception as e:
            self.__handle_execution_error(e)
        else:
            self._output = result
            logger.debug(
                'Action {0}-{1} (uid {2}) executed successfully'.format(
                    self.app_name, self.action_name, self.uid))
            return result
Exemplo n.º 8
0
    def execute(self, action_execution_strategy, accumulator, instance=None, arguments=None, resume=False):
        """Executes an Action by calling the associated app function.

        Args:
            action_execution_strategy: The strategy used to execute the action (e.g. LocalActionExecutionStrategy)
            accumulator (dict): Dict containing the results of the previous actions
            instance (App, optional): The instance of an App object to be used to execute the associated function.
                This field is required if the Action is a bounded action. Otherwise, it defaults to None.
            arguments (list[Argument], optional): List of Arguments to be used if the Action is the starting step of
                the Workflow. Defaults to None.
            resume (bool, optional): Optional boolean to resume a previously paused workflow. Defaults to False.

        Returns:
            (ActionResult): The result of the executed function.
        """
        logger.info('Executing action {} (id={})'.format(self.name, str(self.name)))
        self._execution_id = str(uuid.uuid4())

        if self.device_id:
            self._resolved_device_id = self.device_id.get_value(accumulator)
            logger.debug('Device resolved to {} for action {}'.format(self._resolved_device_id, str(self.id)))

        if arguments:
            WalkoffEvent.CommonWorkflowSignal.send(self, event=WalkoffEvent.ActionStarted,
                                                   data={'start_arguments': arguments})
        else:
            WalkoffEvent.CommonWorkflowSignal.send(self, event=WalkoffEvent.ActionStarted)

        if self.trigger and not resume:
            WalkoffEvent.CommonWorkflowSignal.send(self, event=WalkoffEvent.TriggerActionAwaitingData)
            logger.debug('Trigger Action {} is awaiting data'.format(self.name))
            return ActionResult("trigger", "trigger")

        arguments = arguments if arguments else self.arguments

        try:
            args = validate_app_action_parameters(self._arguments_api, arguments, self.app_name, self.action_name,
                                                  accumulator=accumulator)
        except InvalidArgument as e:
            result = ActionResult.from_exception(e, 'InvalidArguments')
            accumulator[self.id] = result.result
            WalkoffEvent.CommonWorkflowSignal.send(self, event=WalkoffEvent.ActionArgumentsInvalid,
                                                   data=result.as_json())
            return result.status

        if is_app_action_bound(self.app_name, self._run):
            result = action_execution_strategy.execute(self, accumulator, args, instance=instance)
        else:
            result = action_execution_strategy.execute(self, accumulator, args)

        if result.status == 'UnhandledException':
            logger.error('Error executing action {} (id={})'.format(self.name, str(self.id)))
        else:
            logger.debug(
                'Action {0}-{1} (id {2}) executed successfully'.format(self.app_name, self.action_name, self.id))

        result.set_default_status(self.app_name, self.action_name)
        if result.is_failure(self.app_name, self.action_name):
            WalkoffEvent.CommonWorkflowSignal.send(self, event=WalkoffEvent.ActionExecutionError,
                                                   data=result.as_json())
        else:
            WalkoffEvent.CommonWorkflowSignal.send(self, event=WalkoffEvent.ActionExecutionSuccess,
                                                   data=result.as_json())
        return result.status
Exemplo n.º 9
0
    def __init__(self,
                 app_name,
                 action_name,
                 name='',
                 device_id=None,
                 arguments=None,
                 triggers=None,
                 position=None,
                 uid=None,
                 templated=False,
                 raw_representation=None):
        """Initializes a new Action object. A Workflow has many actions that it executes.

        Args:
            app_name (str): The name of the app associated with the Action
            action_name (str): The name of the action associated with a Action
            name (str, optional): The name of the Action object. Defaults to an empty string.
            device_id (int, optional): The id of the device associated with the app associated with the Action. Defaults
                to None.
            arguments ([Argument], optional): A list of Argument objects that are parameters to the action.
                Defaults to None.
            triggers (list[Flag], optional): A list of Flag objects for the Action. If a Action should wait for data
                before continuing, then include these Trigger objects in the Action init. Defaults to None.
            position (dict, optional): A dictionary with the x and y coordinates of the Action object. This is used
                for UI display purposes. Defaults to None.
            uid (str, optional): A universally unique identifier for this object.
                Created from uuid.uuid4().hex in Python
            templated (bool, optional): Whether or not the Action is templated. Used for Jinja templating.
            raw_representation (dict, optional): JSON representation of this object. Used for Jinja templating.
        """
        ExecutionElement.__init__(self, uid)

        self.triggers = triggers if triggers is not None else []
        self._incoming_data = None
        self._event = threading.Event()

        self.name = name
        self.device_id = device_id
        self.app_name = app_name
        self.action_name = action_name
        self._run, self._arguments_api = get_app_action_api(
            self.app_name, self.action_name)

        if is_app_action_bound(self.app_name,
                               self._run) and not self.device_id:
            raise InvalidArgument(
                "Cannot initialize Action {}. App action is bound but no device ID was provided."
                .format(self.name))

        self._action_executable = get_app_action(self.app_name, self._run)

        arguments = {argument.name: argument
                     for argument in arguments
                     } if arguments is not None else {}

        self.templated = templated
        if not self.templated:
            validate_app_action_parameters(self._arguments_api, arguments,
                                           self.app_name, self.action_name)
        self.arguments = arguments
        self.position = position if position is not None else {}

        self._output = None
        self._raw_representation = raw_representation if raw_representation is not None else {}
        self._execution_uid = 'default'