diff --git a/doc/source/user/wf_lang_v2.rst b/doc/source/user/wf_lang_v2.rst index 36ce1a76d..92e939d34 100644 --- a/doc/source/user/wf_lang_v2.rst +++ b/doc/source/user/wf_lang_v2.rst @@ -36,10 +36,10 @@ workflow context variables and thereby implements passing data between workflow tasks. It's also referred to as Data Flow mechanism. YAQL is a simple but powerful query language that allows to extract needed information from JSON structured data. Although Jinja2 is primarily a templating technology, Mistral -also uses it for evaluating expression so user have a choice between YAQL and +also uses it for evaluating expressions so users have a choice between YAQL and Jinja2. It's also possible to combine both expression languages within one workflow definition. The only limitation is that it's impossible to use both -types of expression within one line. As long as there are YAQL and Jinja2 +types of expressions within one line. As long as there are YAQL and Jinja2 expressions on different lines of the workflow definition text, it is valid. It is allowed to use YAQL/Jinja2 in the following sections of Mistral Workflow Language: @@ -941,16 +941,25 @@ a special plugin mechanism. Currently, built-in system actions are: std.fail '''''''' -Fail the current workflow. This action can be used to manually set the workflow -state to error. - -Example: +This action always fails. It can be used to manually fail a workflow task.. .. code-block:: mistral - manual_fail: - action: std.fail + wf: + tasks: + manual_fail: + action: std.fail +The action can be passed the `error_data` parameter. This data will be used as +the action return value. + +.. code-block:: mistral + + wf: + tasks: + manual_fail: + action: std.fail + input: error_data={x:1,y:2} std.http '''''''' diff --git a/mistral/actions/std_actions.py b/mistral/actions/std_actions.py index 3583f09ea..a4638d7d9 100644 --- a/mistral/actions/std_actions.py +++ b/mistral/actions/std_actions.py @@ -80,20 +80,31 @@ class AsyncNoOpAction(NoOpAction): class FailAction(actions.Action): """'Always fail' action. - This action just always throws an instance of ActionException. + If you pass the `error_data` parameter, this action will be failed and + return this data as error data. Otherwise, the action just throws an + instance of ActionException. + This behavior is useful in a number of cases, especially if we need to test a scenario where some of workflow tasks fail. + + :param error_data: Action will be failed with this data """ - def __init__(self): - pass + def __init__(self, error_data=None): + self.error_data = error_data def run(self, context): LOG.info('Running fail action.') + if self.error_data: + return actions.Result(error=self.error_data) + raise exc.ActionException('Fail action expected exception.') def test(self, context): + if self.error_data: + return actions.Result(error=self.error_data) + raise exc.ActionException('Fail action expected exception.') diff --git a/mistral/tests/unit/actions/test_std_fail_action.py b/mistral/tests/unit/actions/test_std_fail_action.py index 47f8689d0..be4e50650 100644 --- a/mistral/tests/unit/actions/test_std_fail_action.py +++ b/mistral/tests/unit/actions/test_std_fail_action.py @@ -23,3 +23,15 @@ class FailActionTest(base.BaseTest): action = std.FailAction() self.assertRaises(exc.ActionException, action.run, mock.Mock) + + def test_fail_with_data(self): + data = { + "x": 1, + "y": 2, + } + action = std.FailAction(error_data=data) + + action_result = action.run(context={}) + + self.assertTrue(action_result.is_error()) + self.assertDictEqual(data, action_result.to_dict()['result'])