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