[One Workflow] Fix the variable type detection within liquidjs for loops#270596
Conversation
|
While running the branch locally, I identified a couple of additional false positives in the Liquid
Can we limit collection-path diagnostics to expressions that are actually workflow context paths, and leave Liquid-local/range/non-path expressions alone unless we can resolve them through template-local type inference? In the meantime, to unblock users from saving workflows with false-positive Liquid validation errors, I’ll reduce the error severity to a warning. --- In the meantime, to unblock users from saving workflows with false-positive Liquid validation errors, I’ll reduce the error severity to a warning. --- Workflows with errors as reference - Why it fails: the validator sees rows as the loop collection and checks it against the workflow context schema. rows is a Liquid-local variable, not a workflow context path, so it reports Collection rows is invalid. Why it fails: Why it fails: users is valid inside Liquid, but the collection validator only validates against the workflow schema and does not account for locals introduced earlier in the template. |
💛 Build succeeded, but was flaky
Failed CI StepsTest Failures
Metrics [docs]Async chunks
Unknown metric groupsESLint disabled line counts
Total ESLint disabled count
History
cc @dej611 |
rosomri
left a comment
There was a problem hiding this comment.
The changes look good overall. I left two comments on concrete edge cases.
Stepping back a bit, and thinking out loudly (let me know wdyt), but this feels like we’re entering a long-term maintenance race with LiquidJS.
The current direction already needs Kibana-side handling for Liquid scoping semantics: assign, reassignment order, for loop scopes, nested loops, range expressions, filters, forloop, block scalar offset mapping, etc.
My main concern is this becomes a never-ending race where each valid Liquid construct needs another local patch, and a future LiquidJS major version could break assumptions we made about the AST.
Longer term, I think the robust fix is to push this kind of logic into LiquidJS static analysis, rather than expanding custom Liquid semantics inside workflows.
What we need from the Liquid side is something like:
analyzeTemplate(template, {
resolveGlobal(path),
getProperty(type, key),
getIterableItem(type),
applyFilter(type, filter, args),
})And it would return typed/resolved references plus locations, for example:
{
references: [
{
sourcePath: ['row', 'name'],
resolvedPath: ['consts', 'items', '*', 'name'],
range,
type,
}
],
locals,
scopes,
diagnostics
}Liquid should own Liquid semantics: scoping, reassignment order, assign, capture, for, ranges, filters, forloop, increment/decrement, nesting, and future syntax changes.
Kibana should own only the host-specific parts: building the workflow context schema for the current step, resolving workflow paths like steps.foo.output, deciding step visibility, and mapping template-relative ranges back to YAML/Monaco positions.
For this PR, maybe we should keep the validation conservative: validate collection expressions only when we can confidently resolve them as workflow context paths, and avoid hard errors for dynamic/unknown Liquid expressions. Then we can separately explore contributing a schema-aware static analysis API upstream to LiquidJS.
| /** Liquid for-loop range literal, e.g. `(1..3)`. */ | ||
| export const LIQUID_RANGE_LITERAL_REGEX = /^\(\s*\d+\s*\.\.\s*\d+\s*\)$/; |
There was a problem hiding this comment.
Dynamic Liquid ranges are still treated as invalid collection paths. LiquidJS accepts {% for i in (1..n) %} and {% for i in (start..finish) %}, but LIQUID_RANGE_LITERAL_REGEX only exempts numeric literals like (1..3). Those valid templates fall through to validateCollectionPath() and get Invalid collection path.
Consider detecting range expressions structurally via LiquidJS token/AST shape, or at least broadening the range exemption beyond numeric literals...
There was a problem hiding this comment.
That's expected for now. I've avoided to blow up the scope here. When a range is encountered it will just early exit and fallback to any validation
|
|
||
| while (SINGLE_IDENTIFIER_REGEX.test(path) && !seen.has(path)) { | ||
| seen.add(path); | ||
| const assign = assignVars.find((a) => a.name === path); |
There was a problem hiding this comment.
resolveAssignChain() uses the first assignment for a local name, but Liquid assignment semantics should use the latest in-scope assignment before the use. This can produce false errors or false passes after reassignment:
{% assign rows = steps.missing.output %}
{% assign rows = consts.items %}
{% for row in rows %}{{ row.name }}{% endfor %}There was a problem hiding this comment.
Yeah, I guess this type of things may be incorrectly detected. It is a very bad pattern tho. Even TS engine would be fooled about it.
|
Starting backport for target branches: 9.4 |
💔 All backports failed
Manual backportTo create the backport manually run: Questions ?Please refer to the Backport tool documentation |
…ops (elastic#270596) ## Summary Liquid `{% for %}` loops in workflow YAML were not validated or autocompleted reliably. Invalid collection paths could slip through, and loop variables inside block-folded (`>-`) message fields were often treated as unknown because offset mapping used the wrong source text. This change validates for-loop collection paths against the step context schema (same rules as `foreach` steps) and reports dedicated diagnostics, separate from variable validation. Loop variables in invalid collections stay permissive in the variable pass so users get one clear error on the collection, not duplicate markers. Block literal and folded scalars now map cursor offsets using the editor’s YAML source, so template-local context (assign, capture, for-loop scope) lines up with what the user actually typed. Liquid syntax and for-loop collection checks share a single YAML pass when the workflow graph is available, and template parsing skips work when a scalar has no `{%` tags. ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
|
Friendly reminder: Looks like this PR hasn’t been backported yet. |
…ops (elastic#270596) ## Summary Liquid `{% for %}` loops in workflow YAML were not validated or autocompleted reliably. Invalid collection paths could slip through, and loop variables inside block-folded (`>-`) message fields were often treated as unknown because offset mapping used the wrong source text. This change validates for-loop collection paths against the step context schema (same rules as `foreach` steps) and reports dedicated diagnostics, separate from variable validation. Loop variables in invalid collections stay permissive in the variable pass so users get one clear error on the collection, not duplicate markers. Block literal and folded scalars now map cursor offsets using the editor’s YAML source, so template-local context (assign, capture, for-loop scope) lines up with what the user actually typed. Liquid syntax and for-loop collection checks share a single YAML pass when the workflow graph is available, and template parsing skips work when a scalar has no `{%` tags. ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
|
Friendly reminder: Looks like this PR hasn’t been backported yet. |
|
Friendly reminder: Looks like this PR hasn’t been backported yet. |
1 similar comment
|
Friendly reminder: Looks like this PR hasn’t been backported yet. |
…ops (elastic#270596) ## Summary Liquid `{% for %}` loops in workflow YAML were not validated or autocompleted reliably. Invalid collection paths could slip through, and loop variables inside block-folded (`>-`) message fields were often treated as unknown because offset mapping used the wrong source text. This change validates for-loop collection paths against the step context schema (same rules as `foreach` steps) and reports dedicated diagnostics, separate from variable validation. Loop variables in invalid collections stay permissive in the variable pass so users get one clear error on the collection, not duplicate markers. Block literal and folded scalars now map cursor offsets using the editor’s YAML source, so template-local context (assign, capture, for-loop scope) lines up with what the user actually typed. Liquid syntax and for-loop collection checks share a single YAML pass when the workflow graph is available, and template parsing skips work when a scalar has no `{%` tags. ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
|
Friendly reminder: Looks like this PR hasn’t been backported yet. |
1 similar comment
|
Friendly reminder: Looks like this PR hasn’t been backported yet. |
…ops (elastic#270596) ## Summary Liquid `{% for %}` loops in workflow YAML were not validated or autocompleted reliably. Invalid collection paths could slip through, and loop variables inside block-folded (`>-`) message fields were often treated as unknown because offset mapping used the wrong source text. This change validates for-loop collection paths against the step context schema (same rules as `foreach` steps) and reports dedicated diagnostics, separate from variable validation. Loop variables in invalid collections stay permissive in the variable pass so users get one clear error on the collection, not duplicate markers. Block literal and folded scalars now map cursor offsets using the editor’s YAML source, so template-local context (assign, capture, for-loop scope) lines up with what the user actually typed. Liquid syntax and for-loop collection checks share a single YAML pass when the workflow graph is available, and template parsing skips work when a scalar has no `{%` tags. ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
|
Friendly reminder: Looks like this PR hasn’t been backported yet. |
Summary
Liquid
{% for %}loops in workflow YAML were not validated or autocompleted reliably. Invalid collection paths could slip through, and loop variables inside block-folded (>-) message fields were often treated as unknown because offset mapping used the wrong source text.This change validates for-loop collection paths against the step context schema (same rules as
foreachsteps) and reports dedicated diagnostics, separate from variable validation. Loop variables in invalid collections stay permissive in the variable pass so users get one clear error on the collection, not duplicate markers.Block literal and folded scalars now map cursor offsets using the editor’s YAML source, so template-local context (assign, capture, for-loop scope) lines up with what the user actually typed. Liquid syntax and for-loop collection checks share a single YAML pass when the workflow graph is available, and template parsing skips work when a scalar has no
{%tags.Checklist