From 1480ac3af2dae6af7fd1a2a224d34f1d6ceebc50 Mon Sep 17 00:00:00 2001 From: Ryan Murphy Date: Wed, 6 Sep 2023 12:58:28 -0600 Subject: [PATCH] feature support nested jsonpath expressions --- .../conductor/core/utils/ParametersUtils.java | 124 +++++++++++------- .../core/utils/ParametersUtilsTest.java | 24 ++++ 2 files changed, 104 insertions(+), 44 deletions(-) diff --git a/core/src/main/java/com/netflix/conductor/core/utils/ParametersUtils.java b/core/src/main/java/com/netflix/conductor/core/utils/ParametersUtils.java index e76344ff8a..1d8422bab1 100644 --- a/core/src/main/java/com/netflix/conductor/core/utils/ParametersUtils.java +++ b/core/src/main/java/com/netflix/conductor/core/utils/ParametersUtils.java @@ -19,6 +19,8 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; +import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -44,6 +46,9 @@ public class ParametersUtils { private static final Logger LOGGER = LoggerFactory.getLogger(ParametersUtils.class); + private static final Pattern PATTERN = + Pattern.compile( + "(?=(?> map = new TypeReference<>() {}; @@ -221,56 +226,58 @@ private Object replaceList(List values, String taskId, DocumentContext io) { private Object replaceVariables( String paramString, DocumentContext documentContext, String taskId) { - String[] values = paramString.split("(?=(?(); + while (matcher.find()) { + var start = matcher.start(); + var end = matcher.end(); + var match = paramString.substring(start, end); + String paramPath = match.substring(2, match.length() - 1); + paramPath = replaceVariables(paramPath, documentContext, taskId, depth + 1).toString(); + // if the paramPath is blank, meaning no value in between ${ and } + // like ${}, ${ } etc, set the value to empty string + if (StringUtils.isBlank(paramPath)) { + replacements.add(new Replacement("", start, end)); + continue; } - } - - Object retObj = convertedValues[0]; - // If the parameter String was "v1 v2 v3" then make sure to stitch it back - if (convertedValues.length > 1) { - for (int i = 0; i < convertedValues.length; i++) { - Object val = convertedValues[i]; - if (val == null) { - val = ""; + if (EnvUtils.isEnvironmentVariable(paramPath)) { + String sysValue = EnvUtils.getSystemParametersValue(paramPath, taskId); + if (sysValue != null) { + replacements.add(new Replacement(sysValue, start, end)); } - if (i == 0) { - retObj = val; - } else { - retObj = retObj + "" + val.toString(); + } else { + try { + replacements.add(new Replacement(documentContext.read(paramPath), start, end)); + } catch (Exception e) { + LOGGER.warn( + "Error reading documentContext for paramPath: {}. Exception: {}", + paramPath, + e); + replacements.add(new Replacement(null, start, end)); } } } - return retObj; + if (replacements.size() == 1 + && replacements.getFirst().getStartIndex() == 0 + && replacements.getFirst().getEndIndex() == paramString.length() + && depth == 0) { + return replacements.get(0).getReplacement(); + } + Collections.sort(replacements); + var builder = new StringBuilder(paramString); + for (int i = replacements.size() - 1; i >= 0; i--) { + var replacement = replacements.get(i); + builder.replace( + replacement.getStartIndex(), + replacement.getEndIndex(), + Objects.toString(replacement.getReplacement())); + } + return builder.toString().replaceAll("\\$\\$\\{", "\\${"); } @Deprecated @@ -321,4 +328,33 @@ public Map getWorkflowInput( } return inputParams; } + + private static class Replacement implements Comparable { + private final int startIndex; + private final int endIndex; + private final Object replacement; + + public Replacement(Object replacement, int startIndex, int endIndex) { + this.replacement = replacement; + this.startIndex = startIndex; + this.endIndex = endIndex; + } + + public Object getReplacement() { + return replacement; + } + + public int getStartIndex() { + return startIndex; + } + + public int getEndIndex() { + return endIndex; + } + + @Override + public int compareTo(Replacement o) { + return Long.compare(startIndex, o.startIndex); + } + } } diff --git a/core/src/test/java/com/netflix/conductor/core/utils/ParametersUtilsTest.java b/core/src/test/java/com/netflix/conductor/core/utils/ParametersUtilsTest.java index 9e5234001f..eb2f239219 100644 --- a/core/src/test/java/com/netflix/conductor/core/utils/ParametersUtilsTest.java +++ b/core/src/test/java/com/netflix/conductor/core/utils/ParametersUtilsTest.java @@ -233,6 +233,30 @@ public void testReplaceInputWithMapAndList() throws Exception { assertEquals("${version}", inputList.get(1)); } + @Test + public void testNestedPathExpressions() throws Exception { + Map map = new HashMap<>(); + map.put("name", "conductor"); + map.put("index", 1); + map.put("mapValue", "a"); + map.put("recordIds", List.of(1, 2, 3)); + map.put("map", Map.of("a", List.of(1, 2, 3), "b", List.of(2, 4, 5), "c", List.of(3, 7, 8))); + + Map input = new HashMap<>(); + input.put("k1", "${recordIds[${index}]}"); + input.put("k2", "${map.${mapValue}[${index}]}"); + input.put("k3", "${map.b[${map.${mapValue}[${index}]}]}"); + + Object jsonObj = objectMapper.readValue(objectMapper.writeValueAsString(map), Object.class); + + Map replaced = parametersUtils.replace(input, jsonObj); + assertNotNull(replaced); + + assertEquals(2, replaced.get("k1")); + assertEquals(2, replaced.get("k2")); + assertEquals(5, replaced.get("k3")); + } + @Test public void testReplaceWithEscapedTags() throws Exception { Map map = new HashMap<>();