Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions cumulusci/core/flowrunner.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@

import copy
import logging
import re
from collections import defaultdict
from operator import attrgetter
from typing import (
Expand Down Expand Up @@ -85,6 +86,7 @@


RETURN_VALUE_OPTION_PREFIX = "^^"
RETURN_VALUE_SUBS_RE = re.compile(r"\^\^\S+")

jinja2_env = ImmutableSandboxedEnvironment()

Expand Down Expand Up @@ -508,6 +510,15 @@ def _run_step(self, step: StepSpec):
return

if step.when:

### get prior return values
prior_return_values = RETURN_VALUE_SUBS_RE.findall(step.when)
if prior_return_values is not None:
for value in prior_return_values:
path, name = value[len(RETURN_VALUE_OPTION_PREFIX) :].rsplit(".", 1)
result = self._find_result_by_path(path)
step.when = step.when.replace(value, result.return_values.get(name))

jinja2_context = {
"project_config": step.project_config,
"org_config": self.org_config,
Expand Down
70 changes: 70 additions & 0 deletions cumulusci/core/tests/test_flowrunner.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ def _run_task(self):
raise self.options["exception"](self.options["message"])


class _TaskReturnsBoolStr(BaseTask):
task_options = {"flag": {"description": "String value to store in return_values"}}

def _run_task(self):
self.return_values = {"flag": self.options.get("flag", "True")}


class _SfdcTask(BaseTask):
salesforce_task = True

Expand Down Expand Up @@ -121,6 +128,10 @@ def _setup_project_config(self):
"description": "An sfdc task",
"class_path": "cumulusci.core.tests.test_flowrunner._SfdcTask",
},
"return_bool": {
"description": "Returns a configurable string flag in return_values",
"class_path": "cumulusci.core.tests.test_flowrunner._TaskReturnsBoolStr",
},
}
self.project_config.config["flows"] = {
"nested_flow": {
Expand Down Expand Up @@ -542,6 +553,65 @@ def test_run__skip_conditional_step(self):
flow.run(self.org_config)
assert len(flow.results) == 0

def test_run__when_substitutes_prior_return_value__step_runs(self):
"""A when clause with ^^path.key runs the step when the substituted value is truthy."""
flow_config = FlowConfig(
{
"steps": {
1: {"task": "return_bool", "options": {"flag": "True"}},
2: {"task": "pass_name", "when": "^^return_bool.flag"},
},
}
)
flow = FlowCoordinator(self.project_config, flow_config)
flow.run(self.org_config)
assert len(flow.results) == 2

def test_run__when_substitutes_prior_return_value__step_skipped(self):
"""A when clause with ^^path.key skips the step when the substituted value is falsy."""
flow_config = FlowConfig(
{
"steps": {
1: {"task": "return_bool", "options": {"flag": "False"}},
2: {"task": "pass_name", "when": "^^return_bool.flag"},
},
}
)
flow = FlowCoordinator(self.project_config, flow_config)
flow.run(self.org_config)
assert len(flow.results) == 1

def test_run__when_substitutes_multiple_prior_return_values(self):
"""Multiple ^^path.key references in a single when clause are each substituted."""
flow_config = FlowConfig(
{
"steps": {
1: {"task": "return_bool", "options": {"flag": "True"}},
2: {
"task": "pass_name",
"when": "^^return_bool.flag and ^^return_bool.flag",
},
},
}
)
flow = FlowCoordinator(self.project_config, flow_config)
flow.run(self.org_config)
assert len(flow.results) == 2

def test_run__when_substitutes_prior_return_value__path_not_found(self):
"""A NameError is raised when the ^^path in a when clause has no matching prior result."""
flow_config = FlowConfig(
{
"steps": {
1: {"task": "return_bool"},
2: {"task": "pass_name", "when": "^^no_such_task.flag"},
},
}
)
flow = FlowCoordinator(self.project_config, flow_config)
with pytest.raises(NameError):
flow.run(self.org_config)

def test_run__task_raises_exception_fail(self):
"""A flow aborts when a task raises an exception"""

Expand Down
Loading