Skip to content

Feature/support prior return value in when clause - #3990

Open
yippie wants to merge 5 commits into
SFDO-Tooling:devfrom
yippie:feature/support-prior-return-for-when
Open

Feature/support prior return value in when clause#3990
yippie wants to merge 5 commits into
SFDO-Tooling:devfrom
yippie:feature/support-prior-return-for-when

Conversation

@yippie

@yippie yippie commented May 28, 2026

Copy link
Copy Markdown
Contributor

Adds the ability to reference output of tasks to determine if subsequent tasks should be run or not.

This pull request enhances the flow runner to support dynamic substitution of prior step return values within a step's when clause, allowing for more flexible and data-driven conditional execution of steps. It also introduces comprehensive tests to validate this new behavior.

Enhancements to flow runner conditional logic:

  • Added support for referencing prior step return values in a step's when clause using the ^^path.key syntax. The flow runner now parses and substitutes these references with the actual return values from previous steps before evaluating the condition. [1] [2] [3]

Testing and validation:

  • Introduced a new task class _TaskReturnsBoolStr in the test suite to facilitate testing of return value substitution.
  • Registered the new return_bool test task in the test project configuration.
  • Added multiple tests to verify the new substitution logic, including:
    • Step runs or is skipped based on the substituted value.
    • Multiple substitutions in a single when clause.
    • Proper error handling when a referenced prior step does not exist.

@yippie
yippie requested a review from a team as a code owner May 28, 2026 18:45
@salesforce-cla

Copy link
Copy Markdown

Thanks for the contribution! It looks like @davidmreed is an internal user so signing the CLA is not required. However, we need to confirm this.

@jstvz jstvz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Useful feature, but the substitution as written has correctness gaps that surface for any return value other than the literal strings "True" and "False".

Blocking (inline):

  1. result.return_values.get(name) returns None when the key is missing, and str.replace(needle, None) raises TypeError. Non-string return values hit the same path.
  2. Raw str.replace into a Jinja2 expression has no quoting contract. The tests pass only because "True"/"False" are Jinja boolean literals; a string return like "Good to Go!" produces invalid Jinja and fails to compile. Pick a contract (repr-quote, or bind values into the Jinja context).

Non-blocking (inline):

  1. Regex \^\^\S+ swallows trailing punctuation; tighten to [\w.]+.
  2. Mutating step.when corrupts the skip-log output.

Other notes (no inline):

  • if prior_return_values is not None: is dead defensive code; re.findall always returns a list. The for handles empty.
  • The new ^^ syntax in when: is user-facing and undocumented. Please add to the cookbook / when-clause docs.
  • The new logic duplicates resolve_return_value_options (line 751). A shared helper would prevent these two paths from drifting.
  • CLA check is missing on this PR; needs to be signed before merge.
  • Test coverage for the path-not-found NameError is good; please extend with a string return value, a missing key on an existing path, and a parenthesized expression.

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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

result.return_values.get(name) returns None when the key is missing, and str.replace(needle, None) raises TypeError. Non-string return values (bool, int) hit the same path. Either default to "", raise a clear error, or build a Jinja context dict instead of string-replacing.

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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Raw textual .replace() into a Jinja2 expression has no quoting contract. The current tests only pass because "True" and "False" happen to be Jinja boolean literals. A return value like "Good to Go!" (the exact case from issue #3663) substitutes into when: Good to Go! and fails to compile. A string with whitespace or operators is a syntax error. Pick a contract: either repr()-quote the substituted value, or bind the values into the Jinja2 context as e.g. tasks['some.task'].return_value and document the syntax.



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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

r"\^\^\S+" greedily captures trailing punctuation. Expressions like ^^task.flag=='ok' match the entire token, then rsplit(".", 1) splits on the wrong dot. Tighten to r"\^\^[\w.]+" to stop at non-word boundaries.

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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

step.when = step.when.replace(...) mutates the StepSpec. Subsequent skip-log output prints the post-substitution text instead of the author's ^^… reference. Assign to a local expr_str before evaluation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants