This is an automated email from the ASF dual-hosted git repository.

hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git


The following commit(s) were added to refs/heads/main by this push:
     new c5414eced3 Run Options variables in pipeline do not appear anymore, 
fixes  #2481 (#7627)
c5414eced3 is described below

commit c5414eced361e796c4a673391714542514d314e2
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Fri Jul 24 17:43:57 2026 +0200

    Run Options variables in pipeline do not appear anymore, fixes  #2481 
(#7627)
---
 .../java/org/apache/hop/core/util/StringUtil.java  |  24 +++-
 .../org/apache/hop/core/util/StringUtilTest.java   |  35 +++++
 .../apache/hop/core/reflection/StringSearcher.java |   6 +
 .../java/org/apache/hop/pipeline/PipelineMeta.java |   4 +-
 .../java/org/apache/hop/workflow/WorkflowMeta.java |   4 +-
 .../hop/core/reflection/StringSearcherTest.java    | 145 +++++++++++++--------
 6 files changed, 159 insertions(+), 59 deletions(-)

diff --git a/core/src/main/java/org/apache/hop/core/util/StringUtil.java 
b/core/src/main/java/org/apache/hop/core/util/StringUtil.java
index 3688528bc4..0668ec52c5 100644
--- a/core/src/main/java/org/apache/hop/core/util/StringUtil.java
+++ b/core/src/main/java/org/apache/hop/core/util/StringUtil.java
@@ -369,9 +369,31 @@ public class StringUtil {
 
   public static void getUsedVariables(
       String aString, List<String> list, boolean includeSystemVariables) {
+    // Include variable resolvers by default. This is what the encryption code 
relies on: a value
+    // like #{vault:hop/data/some-db:password} must be recognized as "contains 
variables" so it is
+    // not encrypted (see #7293).
+    getUsedVariables(aString, list, includeSystemVariables, true);
+  }
+
+  /**
+   * Collect the variables used in the given string.
+   *
+   * @param aString the string to scan
+   * @param list the list to add the used variable names to
+   * @param includeSystemVariables whether to include already-set system 
variables
+   * @param includeResolvers whether to also collect variable resolver 
references (the {@code
+   *     #{name:arguments}} syntax). These are not plain, user-settable 
variables, so callers that
+   *     build a list of variables to present to the user (e.g. the run 
options dialog) should pass
+   *     {@code false} to avoid polluting the list with resolver names and 
secret paths. Real {@code
+   *     ${...}} variables nested inside resolver arguments are still 
collected by the UNIX scan.
+   */
+  public static void getUsedVariables(
+      String aString, List<String> list, boolean includeSystemVariables, 
boolean includeResolvers) {
     getUsedVariables(aString, UNIX_OPEN, UNIX_CLOSE, list, 
includeSystemVariables);
     getUsedVariables(aString, WINDOWS_OPEN, WINDOWS_CLOSE, list, 
includeSystemVariables);
-    getUsedVariables(aString, RESOLVER_OPEN, RESOLVER_CLOSE, list, 
includeSystemVariables);
+    if (includeResolvers) {
+      getUsedVariables(aString, RESOLVER_OPEN, RESOLVER_CLOSE, list, 
includeSystemVariables);
+    }
   }
 
   public static String generateRandomString(
diff --git a/core/src/test/java/org/apache/hop/core/util/StringUtilTest.java 
b/core/src/test/java/org/apache/hop/core/util/StringUtilTest.java
index fa4222d476..41013a8a23 100644
--- a/core/src/test/java/org/apache/hop/core/util/StringUtilTest.java
+++ b/core/src/test/java/org/apache/hop/core/util/StringUtilTest.java
@@ -23,7 +23,9 @@ import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.fail;
 
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import org.apache.hop.core.exception.HopRuntimeException;
 import org.junit.jupiter.api.Test;
@@ -220,4 +222,37 @@ class StringUtilTest {
     assertEquals("", StringUtil.trimEnd("/", '/'));
     assertEquals("", StringUtil.trimEnd("///", '/'));
   }
+
+  /** By default (and for the encryption path) resolvers count as "used 
variables". */
+  @Test
+  void testGetUsedVariablesIncludesResolversByDefault() {
+    List<String> list = new ArrayList<>();
+    StringUtil.getUsedVariables("#{vault:hop/data/some-db:password}", list, 
true);
+    assertTrue(
+        list.contains("vault:hop/data/some-db:password"),
+        "resolver reference should be collected by default");
+  }
+
+  /**
+   * The run options variable list excludes resolvers: a {@code #{...}} 
reference must not show up
+   * as a settable variable, but a real {@code ${...}} variable nested in the 
resolver arguments
+   * still must.
+   */
+  @Test
+  void testGetUsedVariablesExcludesResolvers() {
+    List<String> list = new ArrayList<>();
+    StringUtil.getUsedVariables("#{vault:${env}/data/some-db:password}", list, 
false, false);
+    assertFalse(
+        list.stream().anyMatch(v -> v.startsWith("vault:")),
+        "resolver reference should not be collected when 
includeResolvers=false");
+    assertTrue(list.contains("env"), "nested ${...} variable should still be 
collected");
+  }
+
+  @Test
+  void testGetUsedVariablesStillCollectsPlainVariables() {
+    List<String> list = new ArrayList<>();
+    StringUtil.getUsedVariables("${VARIABLE_1} and %%VARIABLE_2%%", list, 
false, false);
+    assertTrue(list.contains("VARIABLE_1"));
+    assertTrue(list.contains("VARIABLE_2"));
+  }
 }
diff --git 
a/engine/src/main/java/org/apache/hop/core/reflection/StringSearcher.java 
b/engine/src/main/java/org/apache/hop/core/reflection/StringSearcher.java
index f359229824..2d6c011841 100644
--- a/engine/src/main/java/org/apache/hop/core/reflection/StringSearcher.java
+++ b/engine/src/main/java/org/apache/hop/core/reflection/StringSearcher.java
@@ -176,6 +176,12 @@ public class StringSearcher {
       for (int j = 0; j < objectArray.length; j++) {
         findMetaData(((Object[]) obj)[j], level + 1, stringList, parentObject, 
grandParentObject);
       }
+    } else if (obj instanceof Iterable<?> iterable) {
+      for (Object element : iterable) {
+        if (element != null) {
+          stringSearchInObject(element, level, stringList, parentObject, 
grandParentObject, field);
+        }
+      }
     } else {
       findMetaData(obj, level + 1, stringList, parentObject, 
grandParentObject);
     }
diff --git a/engine/src/main/java/org/apache/hop/pipeline/PipelineMeta.java 
b/engine/src/main/java/org/apache/hop/pipeline/PipelineMeta.java
index b45e0aace9..b53a7c7db2 100644
--- a/engine/src/main/java/org/apache/hop/pipeline/PipelineMeta.java
+++ b/engine/src/main/java/org/apache/hop/pipeline/PipelineMeta.java
@@ -2915,8 +2915,10 @@ public class PipelineMeta extends AbstractMeta
     List<String> varList = new ArrayList<>();
 
     // Look around in the strings, see what we find...
+    // Exclude variable resolvers (#{...}): their content is a resolver name 
plus a secret path, not
+    // a user-settable variable, so it shouldn't end up in the run options 
variable list.
     for (StringSearchResult result : stringList) {
-      StringUtil.getUsedVariables(result.getString(), varList, false);
+      StringUtil.getUsedVariables(result.getString(), varList, false, false);
     }
 
     return varList;
diff --git a/engine/src/main/java/org/apache/hop/workflow/WorkflowMeta.java 
b/engine/src/main/java/org/apache/hop/workflow/WorkflowMeta.java
index be0d4c550f..05f2fc36a8 100644
--- a/engine/src/main/java/org/apache/hop/workflow/WorkflowMeta.java
+++ b/engine/src/main/java/org/apache/hop/workflow/WorkflowMeta.java
@@ -1366,8 +1366,10 @@ public class WorkflowMeta extends AbstractMeta
     List<String> varList = new ArrayList<>();
 
     // Look around in the strings, see what we find...
+    // Exclude variable resolvers (#{...}): their content is a resolver name 
plus a secret path, not
+    // a user-settable variable, so it shouldn't end up in the run options 
variable list.
     for (StringSearchResult result : stringList) {
-      StringUtil.getUsedVariables(result.getString(), varList, false);
+      StringUtil.getUsedVariables(result.getString(), varList, false, false);
     }
 
     return varList;
diff --git 
a/engine/src/test/java/org/apache/hop/core/reflection/StringSearcherTest.java 
b/engine/src/test/java/org/apache/hop/core/reflection/StringSearcherTest.java
index 387e904c62..af3889c3ee 100644
--- 
a/engine/src/test/java/org/apache/hop/core/reflection/StringSearcherTest.java
+++ 
b/engine/src/test/java/org/apache/hop/core/reflection/StringSearcherTest.java
@@ -16,68 +16,101 @@
  */
 package org.apache.hop.core.reflection;
 
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
 import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension;
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 
-// import org.apache.hop.pipeline.transforms.filterrows.FilterRowsMeta;
-
 @ExtendWith(RestoreHopEngineEnvironmentExtension.class)
 public class StringSearcherTest {
 
-  // TODO: Move Test
-  /* @Test
-  public void testSearchConditionCase() {
-    String dummyTransformName = "Output";
-    DummyMeta dummyMeta = new DummyMeta();
-    String dummyTransformPid = PluginRegistry.getInstance().getPluginId( 
TransformPluginType.class, dummyMeta );
-    TransformMeta dummyTransform = new TransformMeta( dummyTransformPid, 
dummyTransformname, dummyMeta );
-
-    List<StringSearchResult> stringList = new ArrayList<StringSearchResult>();
-    StringSearcher.findMetaData( dummyTransform, 0, stringList, dummyMeta, 0 );
-
-    int checkCount = 0;
-    String aResult = null;
-    // Check that it found a couple of fields and emits the values properly
-    for ( int i = 0; i < stringList.size(); i++ ) {
-      aResult = stringList.get( i ).toString();
-      if ( aResult.endsWith( "Dummy (transformId)" ) ) {
-        checkCount++;
-      } else if ( aResult.endsWith( "Output (name)" ) ) {
-        checkCount++;
-      }
-      if ( checkCount == 2 ) {
-        break;
-      }
+  /** A leaf metadata object holding a variable reference, e.g. a GetVariables 
field definition. */
+  public static class Field {
+    private String variableString;
+
+    public Field(String variableString) {
+      this.variableString = variableString;
+    }
+
+    public String getVariableString() {
+      return variableString;
+    }
+  }
+
+  /** Metadata shape used before the @HopMetadataProperty migration: an array 
of objects. */
+  public static class ArrayHolder {
+    private Field[] fields;
+
+    public Field[] getFields() {
+      return fields;
     }
-    assertEquals( 2, checkCount );
-
-    FilterRowsMeta filterRowsMeta = new FilterRowsMeta();
-    Condition condition = new Condition();
-    condition.setNegated( false );
-    condition.setLeftValuename( "wibble_t" );
-    condition.setRightValuename( "wobble_s" );
-    condition.setFunction( org.apache.hop.core.Condition.FUNC_EQUAL );
-    filterRowsMeta.setDefault();
-    filterRowsMeta.setCondition( condition );
-
-    String filterRowsPluginPid = PluginRegistry.getInstance().getPluginId( 
TransformPluginType.class, filterRowsMeta );
-    TransformMeta filterRowsTransform = new TransformMeta( 
filterRowsPluginPid, "Filter Rows", filterRowsMeta );
-
-    stringList.clear();
-    StringSearcher.findMetaData( filterRowsTransform, 0, stringList, 
filterRowsMeta, 0 );
-
-    checkCount = 0;
-    for ( int i = 0; i < stringList.size(); i++ ) {
-      aResult = stringList.get( i ).toString();
-      if ( aResult.endsWith( "FilterRows (transformId)" ) ) {
-        checkCount++;
-      } else if ( aResult.endsWith( "Filter Rows (name)" ) ) {
-        checkCount++;
-      }
-      if ( checkCount == 2 ) {
-        break;
-      }
+  }
+
+  /** Metadata shape used after the migration: a List of objects (issue 
#2481). */
+  public static class ListHolder {
+    private List<Field> fields;
+
+    public List<Field> getFields() {
+      return fields;
     }
-    assertEquals( 2, checkCount );
-  }*/
+  }
+
+  /** A List of plain strings must also have its elements discovered. */
+  public static class StringListHolder {
+    private List<String> values;
+
+    public List<String> getValues() {
+      return values;
+    }
+  }
+
+  private static boolean containsString(List<StringSearchResult> results, 
String expected) {
+    return results.stream().anyMatch(r -> expected.equals(r.getString()));
+  }
+
+  @Test
+  public void testFindMetaDataScansObjectArray() {
+    ArrayHolder holder = new ArrayHolder();
+    holder.fields = new Field[] {new Field("${VARIABLE_1}"), new 
Field("${VARIABLE_2}")};
+
+    List<StringSearchResult> results = new ArrayList<>();
+    StringSearcher.findMetaData(holder, 1, results, holder, holder);
+
+    assertTrue(containsString(results, "${VARIABLE_1}"), "array element 1 not 
found");
+    assertTrue(containsString(results, "${VARIABLE_2}"), "array element 2 not 
found");
+  }
+
+  /**
+   * Regression test for <a 
href="https://github.com/apache/hop/issues/2481";>#2481</a>: variables
+   * nested in List-based metadata (the @HopMetadataProperty model most 
transforms migrated to) were
+   * no longer discovered, so Run Options no longer pre-populated them.
+   */
+  @Test
+  public void testFindMetaDataScansObjectList() {
+    ListHolder holder = new ListHolder();
+    holder.fields = new ArrayList<>();
+    holder.fields.add(new Field("${VARIABLE_1}"));
+    holder.fields.add(new Field("${VARIABLE_2}"));
+
+    List<StringSearchResult> results = new ArrayList<>();
+    StringSearcher.findMetaData(holder, 1, results, holder, holder);
+
+    assertTrue(containsString(results, "${VARIABLE_1}"), "list element 1 not 
found");
+    assertTrue(containsString(results, "${VARIABLE_2}"), "list element 2 not 
found");
+  }
+
+  @Test
+  public void testFindMetaDataScansStringList() {
+    StringListHolder holder = new StringListHolder();
+    holder.values = new ArrayList<>();
+    holder.values.add("${VARIABLE_1}");
+
+    List<StringSearchResult> results = new ArrayList<>();
+    StringSearcher.findMetaData(holder, 1, results, holder, holder);
+
+    assertTrue(containsString(results, "${VARIABLE_1}"), "string list element 
not found");
+  }
 }

Reply via email to