From 3ff5ee07a37afeea73b545a37de26f1d70f444d1 Mon Sep 17 00:00:00 2001 From: Steffen Neubauer Date: Fri, 6 Dec 2024 17:20:52 +0100 Subject: [PATCH] fix: improve error message if filter expression in foreach cannot be resolved (#6694) fix: improve error message if filter expression in foreach cannot be resolved --- core/src/template-string/template-string.ts | 9 +++++-- core/test/unit/src/template-string.ts | 27 ++++++++++++++++++++- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/core/src/template-string/template-string.ts b/core/src/template-string/template-string.ts index 13c1d53a8a..e60beb5b7d 100644 --- a/core/src/template-string/template-string.ts +++ b/core/src/template-string/template-string.ts @@ -449,7 +449,12 @@ function handleForEachObject({ const filterResult = resolveTemplateStrings({ value: value[arrayForEachFilterKey], context: loopContext, - contextOpts, + contextOpts: { + ...contextOpts, + // filter expression must be completely resolvable + // TODO: In a future iteration of this code, we should leave the entire $forEach expression unresolved if filter cannot be resolved yet and allowPartial=true. + allowPartial: false, + }, source: { ...source, path: source.path && [...source.path, arrayForEachFilterKey], @@ -460,7 +465,7 @@ function handleForEachObject({ continue } else if (filterResult !== true) { throw new TemplateError({ - message: `${arrayForEachFilterKey} clause in ${arrayForEachKey} loop must resolve to a boolean value (got ${typeof resolvedInput})`, + message: `${arrayForEachFilterKey} clause in ${arrayForEachKey} loop must resolve to a boolean value (got ${typeof filterResult})`, path: source.path && [...source.path, arrayForEachFilterKey], value, resolved: undefined, diff --git a/core/test/unit/src/template-string.ts b/core/test/unit/src/template-string.ts index 8e278f1286..b7af9f9893 100644 --- a/core/test/unit/src/template-string.ts +++ b/core/test/unit/src/template-string.ts @@ -2183,7 +2183,32 @@ describe("resolveTemplateStrings", () => { void expectError( () => resolveTemplateStrings({ source: undefined, value: obj, context: new GenericContext({}) }), { - contains: "$filter clause in $forEach loop must resolve to a boolean value (got object)", + contains: "$filter clause in $forEach loop must resolve to a boolean value (got string)", + } + ) + }) + + // TODO: In a future iteration of this code, we should leave the entire $forEach expression unresolved if filter cannot be resolved yet and allowPartial=true. + it("throws if $filter can't be resolved, even if allowPartial=true", () => { + const obj = { + foo: { + $forEach: ["a", "b", "c"], + $filter: "${var.doesNotExist}", + $return: "${item.value}", + }, + } + + void expectError( + () => + resolveTemplateStrings({ + source: undefined, + value: obj, + context: new GenericContext({ var: { fruit: "banana" } }), + contextOpts: { allowPartial: true }, + }), + { + contains: + "invalid template string (${var.doesnotexist}) at path foo.$filter: could not find key doesnotexist under var. available keys: fruit.", } ) })