def test_execute_sends_callbacks(self): action = Action(app_name='HelloWorld', action_name='Add Three', arguments=[ Argument('num1', value='-5.6'), Argument('num2', value='4.3'), Argument('num3', value='10.2') ]) instance = AppInstance.create(app_name='HelloWorld', device_name='device1') result = {'started_triggered': False, 'result_triggered': False} def callback_is_sent(sender, **kwargs): if isinstance(sender, Action): self.assertIn('event', kwargs) self.assertIn(kwargs['event'], (WalkoffEvent.ActionStarted, WalkoffEvent.ActionExecutionSuccess)) if kwargs['event'] == WalkoffEvent.ActionStarted: result['started_triggered'] = True else: self.assertIn('data', kwargs) data = kwargs['data'] self.assertEqual(data['status'], 'Success') self.assertAlmostEqual(data['result'], 8.9) result['result_triggered'] = True WalkoffEvent.CommonWorkflowSignal.connect(callback_is_sent) action.execute(instance.instance, {}) self.assertTrue(result['started_triggered']) self.assertTrue(result['result_triggered'])
def test_execute_default_return_success(self): action = Action(app_name='HelloWorld', action_name='dummy action', arguments=[ Argument('status', value=True), Argument('other', value=True) ]) instance = AppInstance.create(app_name='HelloWorld', device_name='device1') result = {'started_triggered': False, 'result_triggered': False} def callback_is_sent(sender, **kwargs): if isinstance(sender, Action): self.assertIn('event', kwargs) self.assertIn(kwargs['event'], (WalkoffEvent.ActionStarted, WalkoffEvent.ActionExecutionSuccess)) if kwargs['event'] == WalkoffEvent.ActionStarted: result['started_triggered'] = True else: self.assertIn('data', kwargs) data = kwargs['data'] self.assertEqual(data['status'], 'Success') self.assertEqual(data['result'], None) result['result_triggered'] = True WalkoffEvent.CommonWorkflowSignal.connect(callback_is_sent) action.execute(instance.instance, {}) self.assertTrue(result['started_triggered']) self.assertTrue(result['result_triggered'])
def test_validate_parameters_missing_required_without_default(self): parameter_apis = [{ 'name': 'name1', 'type': 'string', 'minLength': 1, 'maxLength': 25, 'enum': ['test', 'test3'] }, { 'name': 'name2', 'type': 'integer', 'minimum': -3, 'maximum': 25 }, { 'name': 'name3', 'type': 'number', 'required': True, 'minimum': -10.5, 'maximum': 30.725 }] arguments = { 'name1': Argument('name1', value='test'), 'name2': Argument('name2', value='5') } with self.assertRaises(InvalidArgument): validate_parameters(parameter_apis, arguments, self.message)
def test_validate_parameters_skip_action_references_inputs_non_string( self): parameter_apis = [{ 'name': 'name1', 'type': 'string', 'minLength': 1, 'maxLength': 25, 'enum': ['test', 'test3'] }, { 'name': 'name2', 'type': 'integer', 'minimum': -3, 'maximum': 25 }, { 'name': 'name3', 'type': 'number', 'required': True, 'minimum': -10.5, 'maximum': 30.725 }] arguments = { 'name1': Argument('name1', value='test'), 'name2': Argument('name2', value=5), 'name3': Argument('name3', reference='action1') } expected = {'name1': 'test', 'name2': 5} self.assertDictEqual( validate_parameters(parameter_apis, arguments, self.message), expected)
def test_init_with_arguments_with_routing(self): condition = Condition( 'HelloWorld', action_name='mod1_flag2', arguments=[Argument('arg1', reference='action2')]) self.__compare_init(condition, 'HelloWorld', 'mod1_flag2', [], [Argument('arg1', reference="action2")])
def test_validate_parameters_missing_without_default(self): parameter_apis = [{ 'name': 'name1', 'type': 'string', 'minLength': 1, 'maxLength': 25, 'enum': ['test', 'test3'] }, { 'name': 'name2', 'type': 'integer', 'minimum': -3, 'maximum': 25 }, { 'name': 'name3', 'type': 'number', 'minimum': -10.5, 'maximum': 30.725 }] arguments = { 'name1': Argument('name1', value='test'), 'name2': Argument('name2', value='5') } expected = {'name1': 'test', 'name2': 5, 'name3': None} self.assertAlmostEqual( validate_parameters(parameter_apis, arguments, self.message), expected)
def test_execute_with_accumulator_missing_action_callbacks(self): action = Action(app_name='HelloWorld', action_name='Add Three', arguments=[ Argument('num1', reference='1'), Argument('num2', reference='action2'), Argument('num3', value='10.2') ]) accumulator = {'1': '-5.6', 'missing': '4.3', '3': '45'} instance = AppInstance.create(app_name='HelloWorld', device_name='device1') result = {'started_triggered': False, 'result_triggered': False} def callback_is_sent(sender, **kwargs): if isinstance(sender, Action): self.assertIn('event', kwargs) self.assertIn(kwargs['event'], (WalkoffEvent.ActionStarted, WalkoffEvent.ActionArgumentsInvalid)) if kwargs['event'] == WalkoffEvent.ActionStarted: result['started_triggered'] = True else: result['result_triggered'] = True WalkoffEvent.CommonWorkflowSignal.connect(callback_is_sent) action.execute(instance.instance, accumulator) self.assertTrue(result['started_triggered']) self.assertTrue(result['result_triggered'])
def test_execute(self): conditions1 = [ Condition('HelloWorld', action_name='regMatch', arguments=[Argument('regex', value='(.*)')]) ] conditions2 = [ Condition('HelloWorld', action_name='regMatch', arguments=[Argument('regex', value='(.*)')]), Condition('HelloWorld', action_name='regMatch', arguments=[Argument('regex', value='a')]) ] inputs = [ ('name1', [], ActionResult('aaaa', 'Success'), True), ('name2', conditions1, ActionResult('anyString', 'Success'), True), ('name3', conditions2, ActionResult('anyString', 'Success'), True), ('name4', conditions2, ActionResult('bbbb', 'Success'), False), ('name4', conditions2, ActionResult('aaaa', 'Custom'), False) ] for name, conditions, input_str, expect_name in inputs: branch = Branch(source_uid="1", destination_uid="2", conditions=conditions) if expect_name: expected_name = branch.destination_uid self.assertEqual(branch.execute(input_str, {}), expected_name) else: self.assertIsNone(branch.execute(input_str, {}))
def test_get_value_reference_and_selection(self): arg = Argument('test', reference='a', selection=['a', 0, '1', 'b']) input_ = {'a': [[{'one': 1}, {'three': 3, 'b': 4}], [{'one': 1}, {'two': 2}]], 'b': 15, 'c': 'something'} self.assertEqual(arg.get_value({'a': input_, 'b': 2}), 4)
def test_select(self): arg = Argument('test', reference='some_uid', selection=['a', 0, '1', 'b']) input_ = {'a': [[{'one': 1}, {'three': 3, 'b': 4}], [{'one': 1}, {'two': 2}]], 'b': 15, 'c': 'something'} self.assertEqual(arg._select(input_), 4)
def test_init_with_args(self): filter_elem = Transform('HelloWorld', action_name='mod1_filter2', arguments=[Argument('arg1', value='5.4')]) self.__compare_init(filter_elem, 'HelloWorld', 'mod1_filter2', arguments=[Argument('arg1', value='5.4')])
def test_get_value_reference_and_bad_selection(self): arg = Argument('test', reference='a', selection=['a', 0, '1', 'invalid']) input_ = {'a': [[{'one': 1}, {'three': 3, 'b': 4}], [{'one': 1}, {'two': 2}]], 'b': 15, 'c': 'something'} with self.assertRaises(InvalidArgument): arg.get_value({'a': input_, 'b': 2})
def test_init_with_arguments_with_conversion(self): action = Action('HelloWorld', 'returnPlusOne', arguments=[Argument('number', value='-5.6')]) self.__compare_init(action, '', 'returnPlusOne', 'HelloWorld', arguments=[Argument('number', value='-5.6')])
def test_init_with_args_with_routing(self): filter_elem = Transform( 'HelloWorld', action_name='mod1_filter2', arguments=[Argument('arg1', reference="action1")]) self.__compare_init(filter_elem, 'HelloWorld', 'mod1_filter2', arguments=[Argument('arg1', reference="action1")])
def test_select_selection_too_deep(self): arg = Argument('test', reference='some_uid', selection=['a', 0, '1', 'b', 10]) input_ = {'a': [[{'one': 1}, {'three': 3, 'b': 4}], [{'one': 1}, {'two': 2}]], 'b': 15, 'c': 'something'} with self.assertRaises(InvalidArgument): arg._select(input_)
def test_execute_with_accumulator_missing_action(self): action = Action(app_name='HelloWorld', action_name='Add Three', arguments=[ Argument('num1', reference='1'), Argument('num2', reference='action2'), Argument('num3', value='10.2') ]) accumulator = {'1': '-5.6', 'missing': '4.3', '3': '45'} instance = AppInstance.create(app_name='HelloWorld', device_name='device1') action.execute(instance.instance, accumulator)
def test_execute_with_args(self): action = Action(app_name='HelloWorld', action_name='Add Three', arguments=[ Argument('num1', value='-5.6'), Argument('num2', value='4.3'), Argument('num3', value='10.2') ]) instance = AppInstance.create(app_name='HelloWorld', device_name='device1') result = action.execute(instance.instance, {}) self.assertAlmostEqual(result.result, 8.9) self.assertEqual(result.status, 'Success') self.assertEqual(action._output, result)
def test_execute_action_with_valid_arguments_and_transforms_valid_data( self): transforms = [ Transform('HelloWorld', action_name='mod1_filter2', arguments=[Argument('arg1', value='5')]), Transform('HelloWorld', action_name='Top Transform') ] # should go <input = 1> -> <mod1_filter2 = 5+1 = 6> -> <Top Transform 6=6> -> <mod1_flag2 4+6%2==0> -> True self.assertTrue( Condition('HelloWorld', action_name='mod1_flag2', arguments=[Argument('arg1', value=4)], transforms=transforms).execute('1', {}))
def test_init_with_flags(self): triggers = [ Condition('HelloWorld', action_name='regMatch', arguments=[Argument('regex', value='(.*)')]), Condition('HelloWorld', action_name='regMatch', arguments=[Argument('regex', value='a')]) ] action = Action('HelloWorld', 'helloWorld', triggers=triggers) self.__compare_init(action, '', 'helloWorld', 'HelloWorld', triggers=['regMatch', 'regMatch'])
def test_execute_action_with_valid_arguments_and_transforms_invalid_data( self): transforms = [ Transform('HelloWorld', action_name='mod1_filter2', arguments=[Argument('arg1', value='5')]), Transform('HelloWorld', action_name='Top Transform') ] # should go <input = invalid> -> <mod1_filter2 with error = invalid> -> <Top Transform with error = invalid> # -> <mod1_flag2 4+invalid throws error> -> False self.assertFalse( Condition('HelloWorld', action_name='mod1_flag2', arguments=[Argument('arg1', value=4)], transforms=transforms).execute('invalid', {}))
def test_execute_with_accumulator_with_extra_actions(self): action = Action(app_name='HelloWorld', action_name='Add Three', arguments=[ Argument('num1', reference='1'), Argument('num2', reference='action2'), Argument('num3', value='10.2') ]) accumulator = {'1': '-5.6', 'action2': '4.3', '3': '45'} instance = AppInstance.create(app_name='HelloWorld', device_name='device1') result = action.execute(instance.instance, accumulator) self.assertAlmostEqual(result.result, 8.9) self.assertEqual(result.status, 'Success') self.assertEqual(action._output, result)
def test_execute_action_with_valid_arguments_and_transforms_invalid_data_and_routing( self): transforms = [ Transform('HelloWorld', action_name='mod1_filter2', arguments=[Argument('arg1', reference='action1')]), Transform('HelloWorld', action_name='Top Transform') ] # should go <input = invalid> -> <mod1_filter2 with error = invalid> -> <Top Transform with error = invalid> # -> <mod1_flag2 4+invalid throws error> -> False accumulator = {'action1': '5', 'action2': 4} self.assertFalse( Condition('HelloWorld', action_name='mod1_flag2', arguments=[Argument('arg1', value=4)], transforms=transforms).execute('invalid', accumulator))
def execute(self, data_in, accumulator): """Executes the transform. Args: data_in: The input to the condition, the last executed action of the workflow or the input to a trigger. accumulator (dict): A record of executed actions and their results. Of form {action_name: result}. Returns: (obj): The transformed data """ original_data_in = deepcopy(data_in) try: self.arguments.update({self._data_param_name: Argument(self._data_param_name, value=data_in)}) args = validate_transform_parameters(self._api, self.arguments, self.action_name, accumulator=accumulator) result = self._transform_executable(**args) WalkoffEvent.CommonWorkflowSignal.send(self, event=WalkoffEvent.TransformSuccess) return result except InvalidArgument as e: WalkoffEvent.CommonWorkflowSignal.send(self, event=WalkoffEvent.TransformError) logger.error('Transform {0} has invalid input {1}. Error: {2}. ' 'Returning unmodified data'.format(self.action_name, original_data_in, str(e))) except Exception as e: WalkoffEvent.CommonWorkflowSignal.send(self, event=WalkoffEvent.TransformError) logger.error( 'Transform {0} encountered an error: {1}. Returning unmodified data'.format(self.action_name, str(e))) return original_data_in
def test_save_workflow_invalid_app_reload_actions(self): initial_workflow = flask_server.running_context.controller.get_workflow('test', 'helloWorldWorkflow') workflow_name = initial_workflow.name initial_actions = [action.read() for action in initial_workflow.actions.values()] actions_unmod = deepcopy(initial_actions) initial_actions[0]['position']['x'] = 0.0 initial_actions[0]['position']['y'] = 0.0 added_action = Action(name='new_id', app_name='HelloWorld', action_name='pause', arguments=[Argument("seconds", value=5)], position={'x': 0, 'y': 0}).read() added_action['app_name'] = 'Invalid' initial_actions.append(added_action) data = {"actions": initial_actions} self.post_with_status_check('/api/playbooks/test/workflows/{0}/save'.format(workflow_name), data=json.dumps(data), headers=self.headers, content_type='application/json', status_code=INVALID_INPUT_ERROR) workflow = flask_server.running_context.controller.get_workflow('test', 'helloWorldWorkflow') new_actions = [action.read() for action in workflow.actions.values()] for action in actions_unmod: action.pop('position') for action in new_actions: action.pop('position') action.pop('event') self.assertListEqual(actions_unmod, new_actions)
def test_execute_with_args_with_conversion(self): self.assertAlmostEqual( Transform('HelloWorld', action_name='mod1_filter2', arguments=[Argument('arg1', value='10.3')]).execute(5.4, {}), 15.7)
def receive_data(self): """Constantly receives data from the ZMQ socket and handles it accordingly. """ while True: if self.thread_exit: break try: message_bytes = self.comm_sock.recv() except zmq.ZMQError: continue message = CommunicationPacket() message.ParseFromString(message_bytes) if message.type == CommunicationPacket.EXIT: break workflow = self.__get_workflow_by_execution_uid(message.workflow_execution_uid) if workflow: if message.type == CommunicationPacket.PAUSE: workflow.pause() elif message.type == CommunicationPacket.RESUME: workflow.resume() elif message.type == CommunicationPacket.TRIGGER: trigger_data = json.loads(message.data_in) if len(message.arguments) > 0: arguments = [] for arg in message.arguments: arguments.append(Argument(**(MessageToDict(arg, preserving_proto_field_name=True)))) trigger_data["arguments"] = arguments workflow.send_data_to_action(trigger_data) return
def test_execute_with_complex_args(self): action = Action(app_name='HelloWorld', action_name='Json Sample', arguments=[ Argument('json_in', value={ 'a': '-5.6', 'b': { 'a': '4.3', 'b': 5.3 }, 'c': ['1', '2', '3'], 'd': [{ 'a': '', 'b': 3 }, { 'a': '', 'b': -1.5 }, { 'a': '', 'b': -0.5 }] }) ]) instance = AppInstance.create(app_name='HelloWorld', device_name='device1') result = action.execute(instance.instance, {}) self.assertAlmostEqual(result.result, 11.0) self.assertEqual(result.status, 'Success') self.assertEqual(action._output, result)
def test_execute_multiple_triggers(self): triggers = [ Condition('HelloWorld', action_name='regMatch', arguments=[Argument('regex', value='aaa')]) ] action = Action(app_name='HelloWorld', action_name='helloWorld', triggers=triggers) instance = AppInstance.create(app_name='HelloWorld', device_name='device1') action.send_data_to_trigger({"data_in": {"data": 'a'}}) trigger_taken = {'triggered': 0} trigger_not_taken = {'triggered': 0} def callback_is_sent(sender, **kwargs): if kwargs['event'] == WalkoffEvent.TriggerActionTaken: trigger_taken['triggered'] += 1 elif kwargs['event'] == WalkoffEvent.TriggerActionNotTaken: action.send_data_to_trigger({"data_in": {"data": 'aaa'}}) trigger_not_taken['triggered'] += 1 WalkoffEvent.CommonWorkflowSignal.connect(callback_is_sent) action.execute(instance.instance, {}) self.assertEqual(trigger_taken['triggered'], 1) self.assertEqual(trigger_not_taken['triggered'], 1)
def recreate_workflow(workflow_json): """Recreates a workflow from a JSON to prepare for it to be executed. Args: workflow_json (JSON dict): The input workflow JSON, with some other fields as well. Returns: (Workflow object, start_arguments): A tuple containing the reconstructed Workflow object, and the arguments to the start action. """ uid = workflow_json['uid'] del workflow_json['uid'] execution_uid = workflow_json['execution_uid'] del workflow_json['execution_uid'] start = workflow_json['start'] start_arguments = {} if 'start_arguments' in workflow_json: start_arguments = [Argument(**arg) for arg in workflow_json['start_arguments']] workflow_json.pop("start_arguments") workflow = Workflow.create(workflow_json) workflow.uid = uid workflow.set_execution_uid(execution_uid) workflow.start = start return workflow, start_arguments
def test_execute_action_with_valid_complex_arguments_valid_data(self): self.assertTrue( Condition('HelloWorld', action_name='mod1_flag3', arguments=[Argument('arg1', value={ 'a': '1', 'b': '5' })]).execute('some_long_string', {}))