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 2ab94d5975 The "execution information" filter by execution ID has 
stopped working., fixes #7157 (#7159)
2ab94d5975 is described below

commit 2ab94d59759275910b927108fd851a1535325d7a
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Fri May 22 15:31:46 2026 +0200

    The "execution information" filter by execution ID has stopped working., 
fixes #7157 (#7159)
---
 .../hop/execution/DefaultExecutionSelector.java    |  10 ++
 .../execution/DefaultExecutionSelectorTest.java    | 128 +++++++++++++++++++++
 .../neo4j/execution/NeoExecutionInfoLocation.java  |  71 ++++++------
 3 files changed, 175 insertions(+), 34 deletions(-)

diff --git 
a/engine/src/main/java/org/apache/hop/execution/DefaultExecutionSelector.java 
b/engine/src/main/java/org/apache/hop/execution/DefaultExecutionSelector.java
index e72288e322..2d6077da2e 100644
--- 
a/engine/src/main/java/org/apache/hop/execution/DefaultExecutionSelector.java
+++ 
b/engine/src/main/java/org/apache/hop/execution/DefaultExecutionSelector.java
@@ -64,6 +64,16 @@ public record DefaultExecutionSelector(
   }
 
   public boolean isSelected(ExecutionState executionState) {
+    // An exact execution-ID match (a UUID) overrides every state-based filter.
+    // The selection was already decided in isSelected(Execution).
+    //
+    if (isSelectingByUuid()) {
+      return true;
+    }
+    if (executionState == null) {
+      // Without a state we can't evaluate the state-based filters below.
+      return false;
+    }
     if (isSelectingParents && 
StringUtils.isNotEmpty(executionState.getParentId())) {
       return false;
     }
diff --git 
a/engine/src/test/java/org/apache/hop/execution/DefaultExecutionSelectorTest.java
 
b/engine/src/test/java/org/apache/hop/execution/DefaultExecutionSelectorTest.java
new file mode 100644
index 0000000000..843f9850bd
--- /dev/null
+++ 
b/engine/src/test/java/org/apache/hop/execution/DefaultExecutionSelectorTest.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.execution;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.time.Duration;
+import java.util.Date;
+import java.util.UUID;
+import org.junit.jupiter.api.Test;
+
+class DefaultExecutionSelectorTest {
+
+  /** Mimics the Execution Information perspective defaults: only parents, 
last hour. */
+  private static DefaultExecutionSelector selectorWithFilter(String 
filterText) {
+    return new DefaultExecutionSelector(
+        true, false, false, false, false, false, filterText, 
LastPeriod.ONE_HOUR);
+  }
+
+  private static Execution execution(String id, String parentId, Date 
startDate) {
+    Execution execution = new Execution();
+    execution.setId(id);
+    execution.setParentId(parentId);
+    execution.setName("test-execution");
+    execution.setExecutionType(ExecutionType.Pipeline);
+    execution.setExecutionStartDate(startDate);
+    return execution;
+  }
+
+  private static ExecutionState state(String id, String parentId) {
+    ExecutionState state = new ExecutionState();
+    state.setId(id);
+    state.setParentId(parentId);
+    state.setExecutionType(ExecutionType.Pipeline);
+    state.setStatusDescription("Running");
+    return state;
+  }
+
+  private static Date daysAgo(int days) {
+    return new Date(System.currentTimeMillis() - 
Duration.ofDays(days).toMillis());
+  }
+
+  @Test
+  void isSelectingByUuidRecognizesUuid() {
+    
assertTrue(selectorWithFilter(UUID.randomUUID().toString()).isSelectingByUuid());
+  }
+
+  @Test
+  void isSelectingByUuidRejectsNonUuidAndBlank() {
+    assertFalse(selectorWithFilter("my-pipeline").isSelectingByUuid());
+    assertFalse(selectorWithFilter("").isSelectingByUuid());
+    assertFalse(selectorWithFilter(null).isSelectingByUuid());
+  }
+
+  @Test
+  void filterByUuidMatchesExecutionOutsideTimeWindow() {
+    // An exact execution-ID match must ignore the time-window filter so an old
+    // execution can still be found by its ID.
+    String id = UUID.randomUUID().toString();
+    assertTrue(selectorWithFilter(id).isSelected(execution(id, null, 
daysAgo(1))));
+  }
+
+  @Test
+  void filterByUuidMatchIsCaseInsensitive() {
+    String id = UUID.randomUUID().toString();
+    DefaultExecutionSelector selector = selectorWithFilter(id.toUpperCase());
+    assertTrue(selector.isSelected(execution(id, null, new Date())));
+  }
+
+  @Test
+  void filterByUuidDoesNotMatchDifferentId() {
+    DefaultExecutionSelector selector = 
selectorWithFilter(UUID.randomUUID().toString());
+    assertFalse(selector.isSelected(execution(UUID.randomUUID().toString(), 
null, new Date())));
+  }
+
+  @Test
+  void filterByUuidMatchesChildExecutionState() {
+    // A child execution has a non-empty parentId; the default "only parents"
+    // toggle must not veto an exact UUID match.
+    String id = UUID.randomUUID().toString();
+    DefaultExecutionSelector selector = selectorWithFilter(id);
+    assertTrue(selector.isSelected(state(id, UUID.randomUUID().toString())));
+  }
+
+  @Test
+  void filterByUuidSelectsNullExecutionState() {
+    // The selection is decided by isSelected(Execution); a not-yet-written 
state
+    // must not veto it (and must not throw).
+    DefaultExecutionSelector selector = 
selectorWithFilter(UUID.randomUUID().toString());
+    assertTrue(selector.isSelected((ExecutionState) null));
+  }
+
+  @Test
+  void nullExecutionStateIsSkippedWithoutUuidFilter() {
+    // Without a UUID filter a null state must be skipped instead of throwing.
+    assertFalse(selectorWithFilter("my-pipeline").isSelected((ExecutionState) 
null));
+  }
+
+  @Test
+  void parentToggleStillFiltersChildrenWithoutUuidFilter() {
+    DefaultExecutionSelector selector = selectorWithFilter("");
+    assertFalse(selector.isSelected(state("x", "some-parent-id")));
+    assertTrue(selector.isSelected(state("x", null)));
+  }
+
+  @Test
+  void timeWindowStillAppliesWithoutUuidFilter() {
+    DefaultExecutionSelector selector = selectorWithFilter("");
+    assertTrue(selector.isSelected(execution(UUID.randomUUID().toString(), 
null, new Date())));
+    assertFalse(selector.isSelected(execution(UUID.randomUUID().toString(), 
null, daysAgo(1))));
+  }
+}
diff --git 
a/plugins/tech/neo4j/src/main/java/org/apache/hop/neo4j/execution/NeoExecutionInfoLocation.java
 
b/plugins/tech/neo4j/src/main/java/org/apache/hop/neo4j/execution/NeoExecutionInfoLocation.java
index 964d777cb7..1ea5ea7728 100644
--- 
a/plugins/tech/neo4j/src/main/java/org/apache/hop/neo4j/execution/NeoExecutionInfoLocation.java
+++ 
b/plugins/tech/neo4j/src/main/java/org/apache/hop/neo4j/execution/NeoExecutionInfoLocation.java
@@ -673,44 +673,47 @@ public class NeoExecutionInfoLocation implements 
IExecutionInfoLocation {
 
     // Can we push down some selector parameters?
     //
-    boolean firstCondition = true;
-    if (selector.isSelectingParents()) {
-      builder.withWhereIsNull(firstCondition, "n", EP_PARENT_ID);
-      firstCondition = false;
-    }
-    // We filter by execution ID on the nodes because the filter text is a UUID
-    //
     if (selector.isSelectingByUuid()) {
-      builder.withWhereEquals(firstCondition, "n", EP_ID, "pId", 
selector.filterText());
-      firstCondition = false;
-    }
-    if (selector.isSelectingFailed()) {
-      builder.withWhereEquals(firstCondition, "n", EP_FAILED, "pFailed", true);
-      firstCondition = false;
-    }
-    if (selector.isSelectingRunning()) {
-      builder.withWhereEquals(firstCondition, "n", EP_STATUS_DESCRIPTION, 
"pStatus", "Running");
-      firstCondition = false;
-    }
-    if (selector.isSelectingFinished()) {
-      builder.withWhereContains(firstCondition, "n", EP_STATUS_DESCRIPTION, 
"pStatus", "Finished");
-      firstCondition = false;
-    }
-    if (selector.isSelectingWorkflows()) {
-      builder.withWhereEquals(firstCondition, "n", EP_EXECUTION_TYPE, "pType", 
"Workflow");
-    } else if (selector.isSelectingPipelines()) {
-      builder.withWhereEquals(firstCondition, "n", EP_EXECUTION_TYPE, "pType", 
"Pipeline");
+      // We filter by execution ID on the nodes because the filter text is a 
UUID.
+      // An exact execution-ID match overrides every other filter (time window,
+      // parent/child, type and status) so the execution is found regardless 
of age.
+      //
+      builder.withWhereEquals(true, "n", EP_ID, "pId", selector.filterText());
     } else {
-      if (firstCondition) {
-        builder.withExtraClause(" WHERE ");
+      boolean firstCondition = true;
+      if (selector.isSelectingParents()) {
+        builder.withWhereIsNull(firstCondition, "n", EP_PARENT_ID);
+        firstCondition = false;
+      }
+      if (selector.isSelectingFailed()) {
+        builder.withWhereEquals(firstCondition, "n", EP_FAILED, "pFailed", 
true);
+        firstCondition = false;
+      }
+      if (selector.isSelectingRunning()) {
+        builder.withWhereEquals(firstCondition, "n", EP_STATUS_DESCRIPTION, 
"pStatus", "Running");
+        firstCondition = false;
+      }
+      if (selector.isSelectingFinished()) {
+        builder.withWhereContains(
+            firstCondition, "n", EP_STATUS_DESCRIPTION, "pStatus", "Finished");
+        firstCondition = false;
+      }
+      if (selector.isSelectingWorkflows()) {
+        builder.withWhereEquals(firstCondition, "n", EP_EXECUTION_TYPE, 
"pType", "Workflow");
+      } else if (selector.isSelectingPipelines()) {
+        builder.withWhereEquals(firstCondition, "n", EP_EXECUTION_TYPE, 
"pType", "Pipeline");
       } else {
-        builder.withExtraClause(" AND ");
+        if (firstCondition) {
+          builder.withExtraClause(" WHERE ");
+        } else {
+          builder.withExtraClause(" AND ");
+        }
+        builder.withExtraClause("n." + EP_EXECUTION_TYPE + " IN [ 'Workflow', 
'Pipeline' ]");
+      }
+      if (selector.startDateFilter() != LastPeriod.NONE) {
+        builder.withExtraClause(" AND n." + EP_EXECUTION_START_DATE + " >= 
$fromStartDate ");
+        builder.parameters().put("fromStartDate", 
selector.startDateFilter().calculateStartDate());
       }
-      builder.withExtraClause("n." + EP_EXECUTION_TYPE + " IN [ 'Workflow', 
'Pipeline' ]");
-    }
-    if (selector.startDateFilter() != LastPeriod.NONE) {
-      builder.withExtraClause(" AND n." + EP_EXECUTION_START_DATE + " >= 
$fromStartDate ");
-      builder.parameters().put("fromStartDate", 
selector.startDateFilter().calculateStartDate());
     }
 
     // The properties to return

Reply via email to