From 7095059852e932d51322a3afb6ea72901296855b Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Thu, 24 Oct 2024 13:46:49 +0200 Subject: [PATCH] Add support for querying historic variables by case instance and process isntance ids --- .../HistoricVariableInstanceQuery.java | 4 ++ ...CmmnHistoricVariableInstanceQueryImpl.java | 8 ++++ .../cmmn/test/runtime/VariablesTest.java | 33 +++++++++++++++++ .../engine/impl/query/AbstractQuery.java | 7 ++++ .../history/HistoricVariableInstanceTest.java | 36 ++++++++++++++++++ .../HistoricVariableInstanceQuery.java | 10 +++++ .../HistoricVariableInstanceQueryImpl.java | 37 +++++++++++++++++++ .../entity/HistoricVariableInstance.xml | 30 +++++++++++++-- 8 files changed, 162 insertions(+), 3 deletions(-) diff --git a/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/history/HistoricVariableInstanceQuery.java b/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/history/HistoricVariableInstanceQuery.java index dd16d7532c2..a168bd64372 100644 --- a/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/history/HistoricVariableInstanceQuery.java +++ b/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/history/HistoricVariableInstanceQuery.java @@ -12,6 +12,7 @@ */ package org.flowable.cmmn.api.history; +import java.util.Collection; import java.util.Set; import org.flowable.common.engine.api.query.Query; @@ -27,6 +28,9 @@ public interface HistoricVariableInstanceQuery extends Query caseInstanceIds); /** Only select historic variables with the given plan item instance id. */ HistoricVariableInstanceQuery planItemInstanceId(String planItemInstanceId); diff --git a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/history/CmmnHistoricVariableInstanceQueryImpl.java b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/history/CmmnHistoricVariableInstanceQueryImpl.java index 9ada4cde185..867a7d6050d 100644 --- a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/history/CmmnHistoricVariableInstanceQueryImpl.java +++ b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/history/CmmnHistoricVariableInstanceQueryImpl.java @@ -12,6 +12,7 @@ */ package org.flowable.cmmn.engine.impl.history; +import java.util.Collection; import java.util.List; import java.util.Set; @@ -51,6 +52,13 @@ public HistoricVariableInstanceQuery caseInstanceId(String caseInstanceId) { return this; } + @Override + public HistoricVariableInstanceQuery caseInstanceIds(Collection caseInstanceIds) { + wrappedHistoricVariableInstanceQuery.scopeIds(caseInstanceIds); + wrappedHistoricVariableInstanceQuery.scopeType(ScopeTypes.CMMN); + return this; + } + @Override public HistoricVariableInstanceQuery planItemInstanceId(String planItemInstanceId) { wrappedHistoricVariableInstanceQuery.subScopeId(planItemInstanceId); diff --git a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/VariablesTest.java b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/VariablesTest.java index 84b690dc365..1aefc336d15 100644 --- a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/VariablesTest.java +++ b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/VariablesTest.java @@ -16,6 +16,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.entry; +import static org.assertj.core.api.Assertions.tuple; import java.io.Serializable; import java.util.ArrayList; @@ -358,6 +359,38 @@ public void testHistoricVariables() { } } + @Test + @CmmnDeployment(resources = "org/flowable/cmmn/test/task/CmmnTaskServiceTest.testOneHumanTaskCase.cmmn") + public void testVariableInstanceQueryByCaseInstanceIds() { + CaseInstance caseInstance1 = cmmnRuntimeService.createCaseInstanceBuilder() + .caseDefinitionKey("oneHumanTaskCase") + .variable("myVar", "test1") + .start(); + + cmmnRuntimeService.createCaseInstanceBuilder() + .caseDefinitionKey("oneHumanTaskCase") + .variable("myVar", "test2") + .start(); + + CaseInstance caseInstance3 = cmmnRuntimeService.createCaseInstanceBuilder() + .caseDefinitionKey("oneHumanTaskCase") + .variable("myVar", "test3") + .start(); + + if (CmmnHistoryTestHelper.isHistoryLevelAtLeast(HistoryLevel.AUDIT, cmmnEngineConfiguration)) { + Set ids = new HashSet<>(); + ids.add(caseInstance1.getId()); + ids.add(caseInstance3.getId()); + assertThat(cmmnHistoryService.createHistoricVariableInstanceQuery().caseInstanceIds(ids).count()).isEqualTo(2); + assertThat(cmmnHistoryService.createHistoricVariableInstanceQuery().caseInstanceIds(ids).list()) + .extracting(HistoricVariableInstance::getVariableName, HistoricVariableInstance::getValue) + .containsExactlyInAnyOrder( + tuple("myVar", "test1"), + tuple("myVar", "test3") + ); + } + } + @Test @CmmnDeployment public void testTransientVariables() { diff --git a/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/query/AbstractQuery.java b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/query/AbstractQuery.java index 1dffe8e7f13..66c7684e10c 100644 --- a/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/query/AbstractQuery.java +++ b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/query/AbstractQuery.java @@ -13,6 +13,7 @@ package org.flowable.common.engine.impl.query; import java.io.Serializable; +import java.util.Collection; import java.util.List; import org.flowable.common.engine.api.FlowableException; @@ -21,10 +22,12 @@ import org.flowable.common.engine.api.query.QueryProperty; import org.flowable.common.engine.impl.Direction; import org.flowable.common.engine.impl.context.Context; +import org.flowable.common.engine.impl.db.AbstractDataManager; import org.flowable.common.engine.impl.db.ListQueryParameterObject; import org.flowable.common.engine.impl.interceptor.Command; import org.flowable.common.engine.impl.interceptor.CommandContext; import org.flowable.common.engine.impl.interceptor.CommandExecutor; +import org.flowable.common.engine.impl.util.CollectionUtil; /** * Abstract superclass for all query types. @@ -97,6 +100,10 @@ protected void checkQueryOk() { } } + protected List> getSafeList(Collection collection) { + return CollectionUtil.partition(collection, AbstractDataManager.MAX_ENTRIES_IN_CLAUSE); + } + @Override @SuppressWarnings("unchecked") public U singleResult() { diff --git a/modules/flowable-engine/src/test/java/org/flowable/engine/test/history/HistoricVariableInstanceTest.java b/modules/flowable-engine/src/test/java/org/flowable/engine/test/history/HistoricVariableInstanceTest.java index 7ea5dcfa109..54e04601551 100644 --- a/modules/flowable-engine/src/test/java/org/flowable/engine/test/history/HistoricVariableInstanceTest.java +++ b/modules/flowable-engine/src/test/java/org/flowable/engine/test/history/HistoricVariableInstanceTest.java @@ -14,6 +14,7 @@ package org.flowable.engine.test.history; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; import java.util.Arrays; import java.util.HashMap; @@ -26,6 +27,7 @@ import org.flowable.common.engine.impl.util.CollectionUtil; import org.flowable.engine.history.HistoricActivityInstance; import org.flowable.engine.history.HistoricDetail; +import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.history.HistoricVariableUpdate; import org.flowable.engine.impl.test.HistoryTestHelper; import org.flowable.engine.impl.test.PluggableFlowableTestCase; @@ -345,6 +347,40 @@ public void testHistoricVariableQuery2() { } + @Test + public void testHistoricVariableQueryByProcessInstanceIds() { + deployTwoTasksTestProcess(); + + Set processInstanceIds = new HashSet<>(); + ProcessInstance instance1 = runtimeService.createProcessInstanceBuilder() + .processDefinitionKey("twoTasksProcess") + .variable("startVar", "hello") + .start(); + runtimeService.createProcessInstanceBuilder() + .processDefinitionKey("twoTasksProcess") + .variable("startVar2", "hello2") + .start(); + ProcessInstance instance3 = runtimeService.createProcessInstanceBuilder() + .processDefinitionKey("twoTasksProcess") + .variable("startVar3", "hello3") + .start(); + processInstanceIds.add(instance1.getId()); + processInstanceIds.add(instance3.getId()); + + waitForHistoryJobExecutorToProcessAllJobs(7000, 100); + + assertThat(historyService.createHistoricVariableInstanceQuery().processInstanceIds(processInstanceIds).count()).isEqualTo(2); + assertThat(historyService.createHistoricVariableInstanceQuery().processInstanceIds(processInstanceIds).list()).hasSize(2); + + assertThat(historyService.createHistoricVariableInstanceQuery().processInstanceIds(processInstanceIds).list()) + .extracting(HistoricVariableInstance::getVariableName, HistoricVariableInstance::getValue) + .containsExactlyInAnyOrder( + tuple("startVar", "hello"), + tuple("startVar3", "hello3") + ); + } + + @Test public void testHistoricVariableQueryByExecutionIds() { deployTwoTasksTestProcess(); diff --git a/modules/flowable-variable-service-api/src/main/java/org/flowable/variable/api/history/HistoricVariableInstanceQuery.java b/modules/flowable-variable-service-api/src/main/java/org/flowable/variable/api/history/HistoricVariableInstanceQuery.java index be277ae47bc..7edb0686b0e 100644 --- a/modules/flowable-variable-service-api/src/main/java/org/flowable/variable/api/history/HistoricVariableInstanceQuery.java +++ b/modules/flowable-variable-service-api/src/main/java/org/flowable/variable/api/history/HistoricVariableInstanceQuery.java @@ -13,6 +13,8 @@ package org.flowable.variable.api.history; +import java.util.Collection; +import java.util.Collections; import java.util.Set; import org.flowable.common.engine.api.query.Query; @@ -31,6 +33,9 @@ public interface HistoricVariableInstanceQuery extends Query processInstanceIds); + /** Only select historic process variables with the given id. **/ HistoricVariableInstanceQuery executionId(String executionId); @@ -77,6 +82,11 @@ public interface HistoricVariableInstanceQuery extends Query scopeIds); /** * Only select historic variables with the given sub scope id. diff --git a/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/impl/HistoricVariableInstanceQueryImpl.java b/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/impl/HistoricVariableInstanceQueryImpl.java index cfef712ca9f..715a82ea2b4 100644 --- a/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/impl/HistoricVariableInstanceQueryImpl.java +++ b/modules/flowable-variable-service/src/main/java/org/flowable/variable/service/impl/HistoricVariableInstanceQueryImpl.java @@ -13,6 +13,7 @@ package org.flowable.variable.service.impl; +import java.util.Collection; import java.util.List; import java.util.Set; @@ -44,12 +45,14 @@ public class HistoricVariableInstanceQueryImpl extends AbstractQuery executionIds; protected String processInstanceId; + protected Collection processInstanceIds; protected String activityInstanceId; protected String variableName; protected String variableNameLike; protected boolean excludeTaskRelated; protected boolean excludeVariableInitialization; protected String scopeId; + protected Collection scopeIds; protected String subScopeId; protected String scopeType; protected QueryVariableValue queryVariableValue; @@ -83,6 +86,15 @@ public HistoricVariableInstanceQueryImpl processInstanceId(String processInstanc return this; } + @Override + public HistoricVariableInstanceQuery processInstanceIds(Collection processInstanceIds) { + if (processInstanceIds == null || processInstanceIds.isEmpty()) { + throw new FlowableIllegalArgumentException("processInstanceIds is empty"); + } + this.processInstanceIds = processInstanceIds; + return this; + } + @Override public HistoricVariableInstanceQueryImpl executionId(String executionId) { if (executionId == null) { @@ -242,6 +254,15 @@ public HistoricVariableInstanceQuery scopeId(String scopeId) { return this; } + @Override + public HistoricVariableInstanceQuery scopeIds(Collection scopeIds) { + if (scopeIds == null || scopeIds.isEmpty()) { + throw new FlowableIllegalArgumentException("scopeIds is empty"); + } + this.scopeIds = scopeIds; + return this; + } + @Override public HistoricVariableInstanceQuery subScopeId(String subScopeId) { if (excludeLocalVariables) { @@ -334,6 +355,14 @@ public String getProcessInstanceId() { return processInstanceId; } + public Collection getProcessInstanceIds() { + return processInstanceIds; + } + + public List> getSafeProcessInstanceIds() { + return getSafeList(processInstanceIds); + } + public String getTaskId() { return taskId; } @@ -358,6 +387,14 @@ public String getScopeId() { return scopeId; } + public Collection getScopeIds() { + return scopeIds; + } + + public List> getSafeScopeIds() { + return getSafeList(scopeIds); + } + public String getSubScopeId() { return subScopeId; } diff --git a/modules/flowable-variable-service/src/main/resources/org/flowable/variable/service/db/mapping/entity/HistoricVariableInstance.xml b/modules/flowable-variable-service/src/main/resources/org/flowable/variable/service/db/mapping/entity/HistoricVariableInstance.xml index b03c9b67ef9..679922c2d5c 100644 --- a/modules/flowable-variable-service/src/main/resources/org/flowable/variable/service/db/mapping/entity/HistoricVariableInstance.xml +++ b/modules/flowable-variable-service/src/main/resources/org/flowable/variable/service/db/mapping/entity/HistoricVariableInstance.xml @@ -368,6 +368,16 @@ and RES.PROC_INST_ID_ = #{processInstanceId, jdbcType=NVARCHAR} + + and ( + + RES.PROC_INST_ID_ in + + #{item, jdbcType=NVARCHAR} + + + ) + and RES.EXECUTION_ID_ = #{executionId, jdbcType=NVARCHAR} @@ -381,10 +391,14 @@ and RES.TASK_ID_ = #{taskId, jdbcType=NVARCHAR} - and RES.TASK_ID_ in - - #{taskId, jdbcType=NVARCHAR} + and ( + + RES.TASK_ID_ in + + #{item, jdbcType=NVARCHAR} + + ) and RES.TASK_ID_ is NULL @@ -397,6 +411,16 @@ and RES.SCOPE_ID_ = #{scopeId, jdbcType=NVARCHAR} + + and ( + + RES.SCOPE_ID_ in + + #{item, jdbcType=NVARCHAR} + + + ) + and RES.SUB_SCOPE_ID_ = #{subScopeId, jdbcType=NVARCHAR}