From b45a44bd35d32b45ca66e107dad14a88fdd306e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Mathieu?= Date: Wed, 12 Feb 2025 09:53:00 +0100 Subject: [PATCH] fix(core): render list Fixes #7253 --- .../kestra/core/models/property/Property.java | 23 ++++++++++++++++-- .../core/models/property/PropertyTest.java | 24 +++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/io/kestra/core/models/property/Property.java b/core/src/main/java/io/kestra/core/models/property/Property.java index 9677f6336d4..328ae2c9423 100644 --- a/core/src/main/java/io/kestra/core/models/property/Property.java +++ b/core/src/main/java/io/kestra/core/models/property/Property.java @@ -22,6 +22,8 @@ import java.util.Map; import java.util.Objects; +import static io.kestra.core.utils.Rethrow.throwFunction; + /** * Define a plugin properties that will be rendered and converted to a target type at use time. * @@ -136,12 +138,29 @@ public static T asList(Property property, RunContext runContext, Class * * @see io.kestra.core.runners.RunContextProperty#asList(Class, Map) */ + @SuppressWarnings("unchecked") public static T asList(Property property, RunContext runContext, Class itemClazz, Map variables) throws IllegalVariableEvaluationException { if (property.value == null) { - String rendered = runContext.render(property.expression, variables); JavaType type = MAPPER.getTypeFactory().constructCollectionLikeType(List.class, itemClazz); try { - property.value = MAPPER.readValue(rendered, type); + String trimmedExpression = property.expression.trim(); + // We need to detect if the expression is already a list or if it's a pebble expression (for eg. referencing a variable containing a list). + // Doing that allows us to, if it's an expression, first render then read it as a list. + if (trimmedExpression.startsWith("{{") && trimmedExpression.endsWith("}}")) { + property.value = MAPPER.readValue(runContext.render(property.expression, variables), type); + } + // Otherwise if it's already a list, we read it as a list first then render it from run context which handle list rendering by rendering each item of the list + else { + List asRawList = MAPPER.readValue(runContext.render(property.expression, variables), List.class); + property.value = (T) asRawList.stream() + .map(throwFunction(item -> { + if (item instanceof String str) { + return MAPPER.convertValue(str, itemClazz); + } + return item; + })) + .toList(); + } } catch (JsonProcessingException e) { throw new IllegalVariableEvaluationException(e); } diff --git a/core/src/test/java/io/kestra/core/models/property/PropertyTest.java b/core/src/test/java/io/kestra/core/models/property/PropertyTest.java index 8cb51ab6b05..72e9fc16a31 100644 --- a/core/src/test/java/io/kestra/core/models/property/PropertyTest.java +++ b/core/src/test/java/io/kestra/core/models/property/PropertyTest.java @@ -298,6 +298,30 @@ void arrayAndMapToRender() throws Exception { assertThat(output.getMap().get("mapKey2"), is("mapValue2")); } + @Test + void aListToRender() throws Exception { + var task = DynamicPropertyExampleTask.builder() + .items(new Property<>(""" + ["python test.py --input1 \\"{{ item1 }}\\" --input2 \\"{{ item2 }}\\""]""")) + .properties(new Property<>(""" + { + "key1": "{{value1}}", + "key2": "{{value2}}" + }""")) + .build(); + var runContext = runContextFactory.of(Map.ofEntries( + entry("item1", "item1"), + entry("item2", "item2"), + entry("value1", "value1"), + entry("value2", "value2") + )); + + var output = task.run(runContext); + + assertThat(output, notNullValue()); + assertThat(output.getList(), containsInAnyOrder("python test.py --input1 \"item1\" --input2 \"item2\"")); + } + @Builder @Getter private static class TestObj {