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])
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
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
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
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
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
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
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
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'