diff --git a/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/runtime/PlanItemInstanceQuery.java b/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/runtime/PlanItemInstanceQuery.java index 3b63bb95745..6a2435cbc62 100644 --- a/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/runtime/PlanItemInstanceQuery.java +++ b/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/runtime/PlanItemInstanceQuery.java @@ -96,6 +96,16 @@ public interface PlanItemInstanceQuery extends Query planItemDefinitionType); + /** + * Begin an OR statement. Make sure you invoke the endOr method at the end of your OR statement. + */ + PlanItemInstanceQuery or(); + + /** + * End an OR statement. + */ + PlanItemInstanceQuery endOr(); + /** * @return The query will only return ended (completed/terminated/occurred/exited) plan item instances. * No runtime instances will be returned. diff --git a/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/CmmnXmlConverter.java b/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/CmmnXmlConverter.java index 3fb4e517d3b..25045eb3549 100644 --- a/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/CmmnXmlConverter.java +++ b/modules/flowable-cmmn-converter/src/main/java/org/flowable/cmmn/converter/CmmnXmlConverter.java @@ -41,7 +41,6 @@ import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; -import org.apache.commons.lang3.StringUtils; import org.flowable.cmmn.converter.exception.XMLException; import org.flowable.cmmn.converter.export.AssociationExport; import org.flowable.cmmn.converter.export.CaseExport; @@ -71,7 +70,6 @@ import org.flowable.cmmn.model.Sentry; import org.flowable.cmmn.model.SentryOnPart; import org.flowable.cmmn.model.Stage; -import org.flowable.cmmn.model.Task; import org.flowable.cmmn.model.TextAnnotation; import org.flowable.cmmn.model.TimerEventListener; import org.flowable.common.engine.api.FlowableException; @@ -528,20 +526,7 @@ protected void procesPlanItem(CmmnModel cmmnModel, PlanItem planItem, PlanItemDe } if (!planItem.getExitCriteria().isEmpty()) { - boolean exitCriteriaAllowed = true; - if (planItemDefinition instanceof Task) { - Task task = (Task) planItemDefinition; - if (!task.isBlocking() && StringUtils.isEmpty(task.getBlockingExpression())) { - exitCriteriaAllowed = false; - } - } - - if (exitCriteriaAllowed) { - resolveExitCriteriaSentry(planItem); - } else { - LOGGER.warn("Ignoring exit criteria on plan item {}", planItem.getId()); - planItem.getExitCriteria().clear(); - } + resolveExitCriteriaSentry(planItem); } if (planItemDefinition instanceof Stage) { diff --git a/modules/flowable-cmmn-converter/src/test/java/org/flowable/test/cmmn/converter/ExitCriteriaNonBlockingCmmnXmlConverterTest.java b/modules/flowable-cmmn-converter/src/test/java/org/flowable/test/cmmn/converter/ExitCriteriaNonBlockingCmmnXmlConverterTest.java deleted file mode 100644 index cc4917853f6..00000000000 --- a/modules/flowable-cmmn-converter/src/test/java/org/flowable/test/cmmn/converter/ExitCriteriaNonBlockingCmmnXmlConverterTest.java +++ /dev/null @@ -1,70 +0,0 @@ -/* Licensed 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.flowable.test.cmmn.converter; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.tuple; - -import org.flowable.cmmn.model.Case; -import org.flowable.cmmn.model.CmmnModel; -import org.flowable.cmmn.model.Criterion; -import org.flowable.cmmn.model.PlanItem; -import org.flowable.cmmn.model.Sentry; -import org.flowable.cmmn.model.SentryOnPart; -import org.flowable.cmmn.model.Stage; -import org.flowable.test.cmmn.converter.util.CmmnXmlConverterTest; - -/** - * @author Filip Hrisafov - */ -public class ExitCriteriaNonBlockingCmmnXmlConverterTest { - - @CmmnXmlConverterTest("org/flowable/test/cmmn/converter/exit-criteria-non-blocking.cmmn") - public void validateModel(CmmnModel cmmnModel) { - assertThat(cmmnModel).isNotNull(); - assertThat(cmmnModel.getCases()).hasSize(1); - - // Case - Case caze = cmmnModel.getCases().get(0); - assertThat(caze.getId()).isEqualTo("exitCriteriaNonBlocking"); - - // Plan model - Stage planModel = caze.getPlanModel(); - assertThat(planModel).isNotNull(); - assertThat(planModel.getId()).isEqualTo("exitCriteriaNonBlockingPlanModel"); - assertThat(planModel.getName()).isEqualTo("Exit Criteria Non Blocking CasePlanModel"); - - PlanItem planItemTaskA = cmmnModel.findPlanItem("taskA"); - assertThat(planItemTaskA).isNotNull(); - assertThat(planItemTaskA.getDefinitionRef()).isEqualTo("taskDefinition"); - - PlanItem planItemTaskB = cmmnModel.findPlanItem("taskB"); - assertThat(planItemTaskB).isNotNull(); - assertThat(planItemTaskB.getDefinitionRef()).isEqualTo("taskDefinition"); - assertThat(planItemTaskB.getExitCriteria()) - .extracting(Criterion::getId, Criterion::getSentryRef) - .isEmpty(); - - assertThat(planModel.getSentries()) - .extracting(Sentry::getId) - .containsOnly("sentry"); - assertThat(planModel.getSentries().get(0).getOnParts()) - .extracting(SentryOnPart::getId, SentryOnPart::getSourceRef, SentryOnPart::getStandardEvent) - .containsOnly( - tuple("onPart_1", "taskA", "complete") - ); - - assertThat(cmmnModel.getAssociations()).isEmpty(); - } - -} diff --git a/modules/flowable-cmmn-converter/src/test/java/org/flowable/test/cmmn/converter/HumanTaskWithEntryAndExitCriterionCmmnXmlConverterTest.java b/modules/flowable-cmmn-converter/src/test/java/org/flowable/test/cmmn/converter/HumanTaskWithEntryAndExitCriterionCmmnXmlConverterTest.java new file mode 100644 index 00000000000..58f1fd80675 --- /dev/null +++ b/modules/flowable-cmmn-converter/src/test/java/org/flowable/test/cmmn/converter/HumanTaskWithEntryAndExitCriterionCmmnXmlConverterTest.java @@ -0,0 +1,37 @@ +/* Licensed 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.flowable.test.cmmn.converter; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.flowable.cmmn.model.CmmnModel; +import org.flowable.cmmn.model.HumanTask; +import org.flowable.cmmn.model.PlanItem; +import org.flowable.cmmn.model.PlanItemDefinition; +import org.flowable.test.cmmn.converter.util.CmmnXmlConverterTest; + +public class HumanTaskWithEntryAndExitCriterionCmmnXmlConverterTest { + + @CmmnXmlConverterTest("org/flowable/test/cmmn/converter/humanTaskWithEntryAndExitCriterion.cmmn") + public void validateModel(CmmnModel cmmnModel) { + assertThat(cmmnModel).isNotNull(); + + PlanItemDefinition itemDefinition = cmmnModel.findPlanItemDefinition("task1"); + + assertThat(itemDefinition).isInstanceOf(HumanTask.class); + PlanItem planItem = cmmnModel.findPlanItem("planItem1"); + assertThat(planItem.getEntryCriteria()).hasSize(1); + assertThat(planItem.getExitCriteria()).hasSize(1); + } + +} \ No newline at end of file diff --git a/modules/flowable-cmmn-converter/src/test/resources/org/flowable/test/cmmn/converter/exit-criteria-non-blocking.cmmn b/modules/flowable-cmmn-converter/src/test/resources/org/flowable/test/cmmn/converter/exit-criteria-non-blocking.cmmn deleted file mode 100644 index 952996de956..00000000000 --- a/modules/flowable-cmmn-converter/src/test/resources/org/flowable/test/cmmn/converter/exit-criteria-non-blocking.cmmn +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - complete - - - - - - - - diff --git a/modules/flowable-cmmn-converter/src/test/resources/org/flowable/test/cmmn/converter/humanTaskWithEntryAndExitCriterion.cmmn b/modules/flowable-cmmn-converter/src/test/resources/org/flowable/test/cmmn/converter/humanTaskWithEntryAndExitCriterion.cmmn new file mode 100644 index 00000000000..c5b4e2211a4 --- /dev/null +++ b/modules/flowable-cmmn-converter/src/test/resources/org/flowable/test/cmmn/converter/humanTaskWithEntryAndExitCriterion.cmmn @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/StageActivityBehavior.java b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/StageActivityBehavior.java index d9cea11145e..84a28945604 100644 --- a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/StageActivityBehavior.java +++ b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/StageActivityBehavior.java @@ -16,6 +16,7 @@ import org.apache.commons.lang3.StringUtils; import org.flowable.cmmn.api.delegate.DelegatePlanItemInstance; +import org.flowable.cmmn.api.runtime.PlanItemInstanceState; import org.flowable.cmmn.engine.impl.behavior.CoreCmmnTriggerableActivityBehavior; import org.flowable.cmmn.engine.impl.behavior.PlanItemActivityBehavior; import org.flowable.cmmn.engine.impl.persistence.entity.PlanItemInstanceEntity; @@ -92,6 +93,10 @@ protected void handleChildPlanItemInstances(CommandContext commandContext, Deleg } else if (PlanItemTransition.EXIT.equals(transition)) { CommandContextUtil.getAgenda(commandContext).planExitPlanItemInstanceOperation(childPlanItemInstance, null, null, null); } + + } else if (PlanItemTransition.TERMINATE.equals(transition) || PlanItemTransition.EXIT.equals(transition)) { + CommandContextUtil.getCmmnEngineConfiguration(commandContext).getListenerNotificationHelper() + .executeLifecycleListeners(commandContext, childPlanItemInstance, childPlanItemInstance.getState(), PlanItemInstanceState.TERMINATED); } } } diff --git a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/persistence/entity/data/impl/MybatisPlanItemInstanceDataManagerImpl.java b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/persistence/entity/data/impl/MybatisPlanItemInstanceDataManagerImpl.java index 07f0d90a309..7e5e7670e32 100644 --- a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/persistence/entity/data/impl/MybatisPlanItemInstanceDataManagerImpl.java +++ b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/persistence/entity/data/impl/MybatisPlanItemInstanceDataManagerImpl.java @@ -189,5 +189,11 @@ protected void setSafeInValueLists(PlanItemInstanceQueryImpl planItemInstanceQue if (planItemInstanceQuery.getInvolvedGroups() != null) { planItemInstanceQuery.setSafeInvolvedGroups(createSafeInValuesList(planItemInstanceQuery.getInvolvedGroups())); } + + if (planItemInstanceQuery.getOrQueryObjects() != null && !planItemInstanceQuery.getOrQueryObjects().isEmpty()) { + for (PlanItemInstanceQueryImpl oInstanceQuery : planItemInstanceQuery.getOrQueryObjects()) { + setSafeInValueLists(oInstanceQuery); + } + } } } diff --git a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/runtime/AbstractCmmnDynamicStateManager.java b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/runtime/AbstractCmmnDynamicStateManager.java index 33d439ae2b8..e9837e3d3c4 100644 --- a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/runtime/AbstractCmmnDynamicStateManager.java +++ b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/runtime/AbstractCmmnDynamicStateManager.java @@ -139,7 +139,7 @@ protected void doMovePlanItemState(CaseInstanceChangeState caseInstanceChangeSta executeChangePlanItemDefinitionWithNewTargetIds(caseInstanceChangeState, originalCaseDefinitionId, commandContext); - navigatePlanItemInstances(currentPlanItemInstances, caseInstanceChangeState.getCaseDefinitionToMigrateTo()); + navigatePlanItemInstances(currentPlanItemInstances, caseInstanceChangeState.getCaseDefinitionToMigrateTo(), originalCaseDefinitionId); // Set the case variables first so they are available during the change state logic caseInstance.setVariables(caseInstanceChangeState.getCaseVariables()); @@ -856,9 +856,11 @@ protected void setCaseDefinitionIdForPlanItemInstances(Map> stagesByPlanItemDefinitionId, CaseDefinition caseDefinition) { + protected void navigatePlanItemInstances(Map> stagesByPlanItemDefinitionId, CaseDefinition caseDefinition, String originalCaseDefinitionId) { if (caseDefinition != null) { TaskService taskService = cmmnEngineConfiguration.getTaskServiceConfiguration().getTaskService(); + CmmnModel originalCmmnModel = CaseDefinitionUtil.getCmmnModel(originalCaseDefinitionId); + CmmnModel targetCmmnModel = CaseDefinitionUtil.getCmmnModel(caseDefinition.getId()); for (List planItemInstances : stagesByPlanItemDefinitionId.values()) { for (PlanItemInstanceEntity planItemInstance : planItemInstances) { @@ -869,6 +871,31 @@ protected void navigatePlanItemInstances(Map boolean evaluateCondition(Variab return true; } + protected boolean taskPropertyValueIsDifferent(String originalValue, String targetValue) { + return (StringUtils.isNotEmpty(originalValue) && !originalValue.equals(targetValue)) || + (StringUtils.isEmpty(originalValue) && StringUtils.isNotEmpty(targetValue)); + } } diff --git a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/runtime/PlanItemInstanceQueryImpl.java b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/runtime/PlanItemInstanceQueryImpl.java index 264eff18936..453935c2f32 100644 --- a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/runtime/PlanItemInstanceQueryImpl.java +++ b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/runtime/PlanItemInstanceQueryImpl.java @@ -12,6 +12,7 @@ */ package org.flowable.cmmn.engine.impl.runtime; +import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.List; @@ -89,6 +90,10 @@ public class PlanItemInstanceQueryImpl extends AbstractVariableQueryImpl orQueryObjects = new ArrayList<>(); + protected PlanItemInstanceQueryImpl currentOrQueryObject; + protected boolean inOrStatement; + public PlanItemInstanceQueryImpl() { } @@ -108,7 +113,11 @@ public PlanItemInstanceQuery caseDefinitionId(String caseDefinitionId) { if (caseDefinitionId == null) { throw new FlowableIllegalArgumentException("Case definition id is null"); } - this.caseDefinitionId = caseDefinitionId; + if (inOrStatement) { + this.currentOrQueryObject.caseDefinitionId = caseDefinitionId; + } else { + this.caseDefinitionId = caseDefinitionId; + } return this; } @@ -117,7 +126,11 @@ public PlanItemInstanceQuery derivedCaseDefinitionId(String derivedCaseDefinitio if (derivedCaseDefinitionId == null) { throw new FlowableIllegalArgumentException("Derived case definition id is null"); } - this.derivedCaseDefinitionId = derivedCaseDefinitionId; + if (inOrStatement) { + this.currentOrQueryObject.derivedCaseDefinitionId = derivedCaseDefinitionId; + } else { + this.derivedCaseDefinitionId = derivedCaseDefinitionId; + } return this; } @@ -126,7 +139,11 @@ public PlanItemInstanceQuery caseInstanceId(String caseInstanceId) { if (caseInstanceId == null) { throw new FlowableIllegalArgumentException("Case instance id is null"); } - this.caseInstanceId = caseInstanceId; + if (inOrStatement) { + this.currentOrQueryObject.caseInstanceId = caseInstanceId; + } else { + this.caseInstanceId = caseInstanceId; + } return this; } @@ -135,7 +152,11 @@ public PlanItemInstanceQuery stageInstanceId(String stageInstanceId) { if (stageInstanceId == null) { throw new FlowableIllegalArgumentException("Stage instance id is null"); } - this.stageInstanceId = stageInstanceId; + if (inOrStatement) { + this.currentOrQueryObject.stageInstanceId = stageInstanceId; + } else { + this.stageInstanceId = stageInstanceId; + } return this; } @@ -144,7 +165,11 @@ public PlanItemInstanceQuery planItemInstanceId(String planItemInstanceId) { if (planItemInstanceId == null) { throw new FlowableIllegalArgumentException("Plan Item instance id is null"); } - this.planItemInstanceId = planItemInstanceId; + if (inOrStatement) { + this.currentOrQueryObject.planItemInstanceId = planItemInstanceId; + } else { + this.planItemInstanceId = planItemInstanceId; + } return this; } @@ -153,7 +178,11 @@ public PlanItemInstanceQuery planItemInstanceElementId(String elementId) { if (elementId == null) { throw new FlowableIllegalArgumentException("Element id is null"); } - this.elementId = elementId; + if (inOrStatement) { + this.currentOrQueryObject.elementId = elementId; + } else { + this.elementId = elementId; + } return this; } @@ -162,7 +191,11 @@ public PlanItemInstanceQuery planItemDefinitionId(String planItemDefinitionId) { if (planItemDefinitionId == null) { throw new FlowableIllegalArgumentException("Plan item definition id is null"); } - this.planItemDefinitionId = planItemDefinitionId; + if (inOrStatement) { + this.currentOrQueryObject.planItemDefinitionId = planItemDefinitionId; + } else { + this.planItemDefinitionId = planItemDefinitionId; + } return this; } @@ -171,7 +204,11 @@ public PlanItemInstanceQuery planItemDefinitionType(String planItemDefinitionTyp if (planItemDefinitionType == null) { throw new FlowableIllegalArgumentException("Plan item definition type is null"); } - this.planItemDefinitionType = planItemDefinitionType; + if (inOrStatement) { + this.currentOrQueryObject.planItemDefinitionType = planItemDefinitionType; + } else { + this.planItemDefinitionType = planItemDefinitionType; + } return this; } @@ -183,7 +220,11 @@ public PlanItemInstanceQuery planItemDefinitionTypes(List planItemDefini if (planItemDefinitionTypes.isEmpty()) { throw new FlowableIllegalArgumentException("Plan item definition types is empty"); } - this.planItemDefinitionTypes = planItemDefinitionTypes; + if (inOrStatement) { + this.currentOrQueryObject.planItemDefinitionTypes = planItemDefinitionTypes; + } else { + this.planItemDefinitionTypes = planItemDefinitionTypes; + } return this; } @@ -192,7 +233,11 @@ public PlanItemInstanceQuery planItemInstanceName(String name) { if (name == null) { throw new FlowableIllegalArgumentException("Name is null"); } - this.name = name; + if (inOrStatement) { + this.currentOrQueryObject.name = name; + } else { + this.name = name; + } return this; } @@ -201,7 +246,11 @@ public PlanItemInstanceQuery planItemInstanceState(String state) { if (state == null) { throw new FlowableIllegalArgumentException("State is null"); } - this.state = state; + if (inOrStatement) { + this.currentOrQueryObject.state = state; + } else { + this.state = state; + } return this; } @@ -260,7 +309,11 @@ public PlanItemInstanceQuery planItemInstanceCreatedBefore(Date createdBefore) { if (createdBefore == null) { throw new FlowableIllegalArgumentException("createdBefore is null"); } - this.createdBefore = createdBefore; + if (inOrStatement) { + this.currentOrQueryObject.createdBefore = createdBefore; + } else { + this.createdBefore = createdBefore; + } return this; } @@ -269,7 +322,11 @@ public PlanItemInstanceQuery planItemInstanceCreatedAfter(Date createdAfter) { if (createdAfter == null) { throw new FlowableIllegalArgumentException("createdAfter is null"); } - this.createdAfter = createdAfter; + if (inOrStatement) { + this.currentOrQueryObject.createdAfter = createdAfter; + } else { + this.createdAfter = createdAfter; + } return this; } @@ -278,7 +335,11 @@ public PlanItemInstanceQuery planItemInstanceLastAvailableBefore(Date availableB if (availableBefore == null) { throw new FlowableIllegalArgumentException("availableBefore is null"); } - this.lastAvailableBefore = availableBefore; + if (inOrStatement) { + this.currentOrQueryObject.lastAvailableBefore = availableBefore; + } else { + this.lastAvailableBefore = availableBefore; + } return this; } @@ -287,7 +348,11 @@ public PlanItemInstanceQuery planItemInstanceLastAvailableAfter(Date availableAf if (availableAfter == null) { throw new FlowableIllegalArgumentException("availableAfter is null"); } - this.lastAvailableAfter = availableAfter; + if (inOrStatement) { + this.currentOrQueryObject.lastAvailableAfter = availableAfter; + } else { + this.lastAvailableAfter = availableAfter; + } return this; } @@ -296,7 +361,11 @@ public PlanItemInstanceQuery planItemInstanceLastUnavailableBefore(Date unavaila if (unavailableBefore == null) { throw new FlowableIllegalArgumentException("unavailableBefore is null"); } - this.lastUnavailableBefore = unavailableBefore; + if (inOrStatement) { + this.currentOrQueryObject.lastUnavailableBefore = unavailableBefore; + } else { + this.lastUnavailableBefore = unavailableBefore; + } return this; } @@ -305,7 +374,11 @@ public PlanItemInstanceQuery planItemInstanceLastUnavailableAfter(Date unavailab if (unavailableAfter == null) { throw new FlowableIllegalArgumentException("unavailableAfter is null"); } - this.lastUnavailableAfter = unavailableAfter; + if (inOrStatement) { + this.currentOrQueryObject.lastUnavailableAfter = unavailableAfter; + } else { + this.lastUnavailableAfter = unavailableAfter; + } return this; } @@ -314,7 +387,11 @@ public PlanItemInstanceQuery planItemInstanceLastEnabledBefore(Date enabledBefor if (enabledBefore == null) { throw new FlowableIllegalArgumentException("enabledBefore is null"); } - this.lastEnabledBefore = enabledBefore; + if (inOrStatement) { + this.currentOrQueryObject.lastEnabledBefore = enabledBefore; + } else { + this.lastEnabledBefore = enabledBefore; + } return this; } @@ -323,7 +400,11 @@ public PlanItemInstanceQuery planItemInstanceLastEnabledAfter(Date enabledAfter) if (enabledAfter == null) { throw new FlowableIllegalArgumentException("enabledAfter is null"); } - this.lastEnabledAfter = enabledAfter; + if (inOrStatement) { + this.currentOrQueryObject.lastEnabledAfter = enabledAfter; + } else { + this.lastEnabledAfter = enabledAfter; + } return this; } @@ -332,7 +413,11 @@ public PlanItemInstanceQuery planItemInstanceLastDisabledBefore(Date disabledBef if (disabledBefore == null) { throw new FlowableIllegalArgumentException("disabledBefore is null"); } - this.lastDisabledBefore = disabledBefore; + if (inOrStatement) { + this.currentOrQueryObject.lastDisabledBefore = disabledBefore; + } else { + this.lastDisabledBefore = disabledBefore; + } return this; } @@ -341,7 +426,11 @@ public PlanItemInstanceQuery planItemInstanceLastDisabledAfter(Date disabledAfte if (disabledAfter == null) { throw new FlowableIllegalArgumentException("disabledAfter is null"); } - this.lastDisabledAfter = disabledAfter; + if (inOrStatement) { + this.currentOrQueryObject.lastDisabledAfter = disabledAfter; + } else { + this.lastDisabledAfter = disabledAfter; + } return this; } @@ -350,7 +439,11 @@ public PlanItemInstanceQuery planItemInstanceLastStartedBefore(Date startedBefor if (startedBefore == null) { throw new FlowableIllegalArgumentException("activatedBefore is null"); } - this.lastStartedBefore = startedBefore; + if (inOrStatement) { + this.currentOrQueryObject.lastStartedBefore = startedBefore; + } else { + this.lastStartedBefore = startedBefore; + } return this; } @@ -359,7 +452,11 @@ public PlanItemInstanceQuery planItemInstanceLastStartedAfter(Date startedAfter) if (startedAfter == null) { throw new FlowableIllegalArgumentException("startedAfter is null"); } - this.lastStartedAfter = startedAfter; + if (inOrStatement) { + this.currentOrQueryObject.lastStartedAfter = startedAfter; + } else { + this.lastStartedAfter = startedAfter; + } return this; } @@ -368,7 +465,11 @@ public PlanItemInstanceQuery planItemInstanceLastSuspendedBefore(Date suspendedB if (suspendedBefore == null) { throw new FlowableIllegalArgumentException("suspendedBefore is null"); } - this.lastSuspendedBefore = suspendedBefore; + if (inOrStatement) { + this.currentOrQueryObject.lastSuspendedBefore = suspendedBefore; + } else { + this.lastSuspendedBefore = suspendedBefore; + } return this; } @@ -377,7 +478,11 @@ public PlanItemInstanceQuery planItemInstanceLastSuspendedAfter(Date suspendedAf if (suspendedAfter == null) { throw new FlowableIllegalArgumentException("suspendedAfter is null"); } - this.lastSuspendedAfter = suspendedAfter; + if (inOrStatement) { + this.currentOrQueryObject.lastSuspendedAfter = suspendedAfter; + } else { + this.lastSuspendedAfter = suspendedAfter; + } return this; } @@ -386,7 +491,11 @@ public PlanItemInstanceQuery planItemInstanceCompletedBefore(Date completedBefor if (completedBefore == null) { throw new FlowableIllegalArgumentException("completedBefore is null"); } - this.completedBefore = completedBefore; + if (inOrStatement) { + this.currentOrQueryObject.completedBefore = completedBefore; + } else { + this.completedBefore = completedBefore; + } return this; } @@ -395,7 +504,11 @@ public PlanItemInstanceQuery planItemInstanceCompletedAfter(Date completedAfter) if (completedAfter == null) { throw new FlowableIllegalArgumentException("completedAfter is null"); } - this.completedAfter = completedAfter; + if (inOrStatement) { + this.currentOrQueryObject.completedAfter = completedAfter; + } else { + this.completedAfter = completedAfter; + } return this; } @@ -404,7 +517,11 @@ public PlanItemInstanceQuery planItemInstanceOccurredBefore(Date occurredBefore) if (occurredBefore == null) { throw new FlowableIllegalArgumentException("occurredBefore is null"); } - this.occurredBefore = occurredBefore; + if (inOrStatement) { + this.currentOrQueryObject.occurredBefore = occurredBefore; + } else { + this.occurredBefore = occurredBefore; + } return this; } @@ -413,7 +530,11 @@ public PlanItemInstanceQuery planItemInstanceOccurredAfter(Date occurredAfter) { if (occurredAfter == null) { throw new FlowableIllegalArgumentException("occurredAfter is null"); } - this.occurredAfter = occurredAfter; + if (inOrStatement) { + this.currentOrQueryObject.occurredAfter = occurredAfter; + } else { + this.occurredAfter = occurredAfter; + } return this; } @@ -422,7 +543,11 @@ public PlanItemInstanceQuery planItemInstanceTerminatedBefore(Date terminatedBef if (terminatedBefore == null) { throw new FlowableIllegalArgumentException("terminatedBefore is null"); } - this.terminatedBefore = terminatedBefore; + if (inOrStatement) { + this.currentOrQueryObject.terminatedBefore = terminatedBefore; + } else { + this.terminatedBefore = terminatedBefore; + } return this; } @@ -431,7 +556,11 @@ public PlanItemInstanceQuery planItemInstanceTerminatedAfter(Date terminatedAfte if (terminatedAfter == null) { throw new FlowableIllegalArgumentException("terminatedAfter is null"); } - this.terminatedAfter = terminatedAfter; + if (inOrStatement) { + this.currentOrQueryObject.terminatedAfter = terminatedAfter; + } else { + this.terminatedAfter = terminatedAfter; + } return this; } @@ -440,7 +569,11 @@ public PlanItemInstanceQuery planItemInstanceExitBefore(Date exitBefore) { if (exitBefore == null) { throw new FlowableIllegalArgumentException("exitBefore is null"); } - this.exitBefore = exitBefore; + if (inOrStatement) { + this.currentOrQueryObject.exitBefore = exitBefore; + } else { + this.exitBefore = exitBefore; + } return this; } @@ -449,7 +582,11 @@ public PlanItemInstanceQuery planItemInstanceExitAfter(Date exitAfter) { if (exitAfter == null) { throw new FlowableIllegalArgumentException("exitAfter is null"); } - this.exitAfter = exitAfter; + if (inOrStatement) { + this.currentOrQueryObject.exitAfter = exitAfter; + } else { + this.exitAfter = exitAfter; + } return this; } @@ -458,27 +595,45 @@ public PlanItemInstanceQuery planItemInstanceEndedBefore(Date endedBefore) { if (endedBefore == null) { throw new FlowableIllegalArgumentException("endedBefore is null"); } - this.endedBefore = endedBefore; + if (inOrStatement) { + this.currentOrQueryObject.endedBefore = endedBefore; + } else { + this.endedBefore = endedBefore; + } return this; } + @Override public PlanItemInstanceQuery planItemInstanceEndedAfter(Date endedAfter) { if (endedAfter == null) { throw new FlowableIllegalArgumentException("endedAfter is null"); } - this.endedAfter = endedAfter; + if (inOrStatement) { + this.currentOrQueryObject.endedAfter = endedAfter; + } else { + this.endedAfter = endedAfter; + } return this; } @Override public PlanItemInstanceQuery ended() { - this.ended = true; + if (inOrStatement) { + this.currentOrQueryObject.ended = true; + includeEnded = true; + } else { + this.ended = true; + } return this; } @Override public PlanItemInstanceQuery includeEnded() { - this.includeEnded = true; + if (inOrStatement) { + throw new FlowableIllegalArgumentException("includeEnded is not allowed within an or query"); + } else { + this.includeEnded = true; + } return this; } @@ -487,31 +642,51 @@ public PlanItemInstanceQuery planItemInstanceStartUserId(String startUserId) { if (startUserId == null) { throw new FlowableIllegalArgumentException("Start user id is null"); } - this.startUserId = startUserId; + if (inOrStatement) { + this.currentOrQueryObject.startUserId = startUserId; + } else { + this.startUserId = startUserId; + } return this; } @Override public PlanItemInstanceQuery planItemInstanceReferenceId(String referenceId) { - this.referenceId = referenceId; + if (inOrStatement) { + this.currentOrQueryObject.referenceId = referenceId; + } else { + this.referenceId = referenceId; + } return this; } @Override public PlanItemInstanceQuery planItemInstanceReferenceType(String referenceType) { - this.referenceType = referenceType; + if (inOrStatement) { + this.currentOrQueryObject.referenceType = referenceType; + } else { + this.referenceType = referenceType; + } return this; } @Override public PlanItemInstanceQuery planItemInstanceCompletable() { - this.completable = true; + if (inOrStatement) { + this.currentOrQueryObject.completable = true; + } else { + this.completable = true; + } return this; } @Override public PlanItemInstanceQuery onlyStages() { - this.onlyStages = true; + if (inOrStatement) { + this.currentOrQueryObject.onlyStages = true; + } else { + this.onlyStages = true; + } return this; } @@ -520,7 +695,11 @@ public PlanItemInstanceQuery planItemInstanceEntryCriterionId(String entryCriter if (entryCriterionId == null) { throw new FlowableIllegalArgumentException("EntryCriterionId is null"); } - this.entryCriterionId = entryCriterionId; + if (inOrStatement) { + this.currentOrQueryObject.entryCriterionId = entryCriterionId; + } else { + this.entryCriterionId = entryCriterionId; + } return this; } @@ -529,7 +708,11 @@ public PlanItemInstanceQuery planItemInstanceExitCriterionId(String exitCriterio if (exitCriterionId == null) { throw new FlowableIllegalArgumentException("ExitCriterionId is null"); } - this.exitCriterionId = exitCriterionId; + if (inOrStatement) { + this.currentOrQueryObject.exitCriterionId = exitCriterionId; + } else { + this.exitCriterionId = exitCriterionId; + } return this; } @@ -538,7 +721,11 @@ public PlanItemInstanceQuery planItemInstanceFormKey(String formKey) { if (formKey == null) { throw new FlowableIllegalArgumentException("formKey is null"); } - this.formKey = formKey; + if (inOrStatement) { + this.currentOrQueryObject.formKey = formKey; + } else { + this.formKey = formKey; + } return this; } @@ -547,7 +734,11 @@ public PlanItemInstanceQuery planItemInstanceExtraValue(String extraValue) { if (extraValue == null) { throw new FlowableIllegalArgumentException("extraValue is null"); } - this.extraValue = extraValue; + if (inOrStatement) { + this.currentOrQueryObject.extraValue = extraValue; + } else { + this.extraValue = extraValue; + } return this; } @@ -556,7 +747,11 @@ public PlanItemInstanceQuery involvedUser(String involvedUser) { if (involvedUser == null) { throw new FlowableIllegalArgumentException("involvedUser is null"); } - this.involvedUser = involvedUser; + if (inOrStatement) { + this.currentOrQueryObject.involvedUser = involvedUser; + } else { + this.involvedUser = involvedUser; + } return this; } @@ -565,7 +760,11 @@ public PlanItemInstanceQuery involvedGroups(Collection involvedGroups) { if (involvedGroups == null) { throw new FlowableIllegalArgumentException("involvedGroups is null"); } - this.involvedGroups = involvedGroups; + if (inOrStatement) { + this.currentOrQueryObject.involvedGroups = involvedGroups; + } else { + this.involvedGroups = involvedGroups; + } return this; } @@ -574,79 +773,309 @@ public PlanItemInstanceQuery planItemInstanceTenantId(String tenantId) { if (tenantId == null) { throw new FlowableIllegalArgumentException("Tenant id is null"); } - this.tenantId = tenantId; + if (inOrStatement) { + this.currentOrQueryObject.tenantId = tenantId; + } else { + this.tenantId = tenantId; + } return this; } @Override public PlanItemInstanceQuery planItemInstanceWithoutTenantId() { - this.withoutTenantId = true; + if (inOrStatement) { + this.currentOrQueryObject.withoutTenantId = true; + } else { + this.withoutTenantId = true; + } return this; } - + + @Override + public PlanItemInstanceQuery variableValueEquals(String name, Object value) { + if (inOrStatement) { + this.currentOrQueryObject.variableValueEquals(name, value); + } else { + super.variableValueEquals(name, value); + } + return this; + } + + @Override + public PlanItemInstanceQuery variableValueEquals(Object value) { + if (inOrStatement) { + this.currentOrQueryObject.variableValueEquals(value); + } else { + super.variableValueEquals(value); + } + return this; + } + + @Override + public PlanItemInstanceQuery variableValueEqualsIgnoreCase(String name, String value) { + if (inOrStatement) { + this.currentOrQueryObject.variableValueEqualsIgnoreCase(name, value); + } else { + super.variableValueEqualsIgnoreCase(name, value); + } + return this; + } + + @Override + public PlanItemInstanceQuery variableValueNotEqualsIgnoreCase(String name, String value) { + if (inOrStatement) { + this.currentOrQueryObject.variableValueNotEqualsIgnoreCase(name, value); + } else { + super.variableValueNotEqualsIgnoreCase(name, value); + } + return this; + } + + @Override + public PlanItemInstanceQuery variableValueNotEquals(String name, Object value) { + if (inOrStatement) { + this.currentOrQueryObject.variableValueNotEquals(name, value); + } else { + super.variableValueNotEquals(name, value); + } + return this; + } + + @Override + public PlanItemInstanceQuery variableValueLikeIgnoreCase(String name, String value) { + if (inOrStatement) { + this.currentOrQueryObject.variableValueLikeIgnoreCase(name, value); + } else { + super.variableValueLikeIgnoreCase(name, value); + } + return this; + } + + @Override + public PlanItemInstanceQuery variableValueLike(String name, String value) { + if (inOrStatement) { + this.currentOrQueryObject.variableValueLike(name, value); + } else { + super.variableValueLike(name, value); + } + return this; + } + + @Override + public PlanItemInstanceQuery variableValueLessThanOrEqual(String name, Object value) { + if (inOrStatement) { + this.currentOrQueryObject.variableValueLessThanOrEqual(name, value); + } else { + super.variableValueLessThanOrEqual(name, value); + } + return this; + } + + @Override + public PlanItemInstanceQuery variableValueLessThan(String name, Object value) { + if (inOrStatement) { + this.currentOrQueryObject.variableValueLessThan(name, value); + } else { + super.variableValueLessThan(name, value); + } + return this; + } + + @Override + public PlanItemInstanceQuery variableValueGreaterThanOrEqual(String name, Object value) { + if (inOrStatement) { + this.currentOrQueryObject.variableValueGreaterThanOrEqual(name, value); + } else { + + super.variableValueGreaterThanOrEqual(name, value); + } + return this; + } + + @Override + public PlanItemInstanceQuery variableValueGreaterThan(String name, Object value) { + if (inOrStatement) { + this.currentOrQueryObject.variableValueGreaterThan(name, value); + } else { + super.variableValueGreaterThan(name, value); + } + return this; + } + @Override + public PlanItemInstanceQuery variableExists(String name) { + if (inOrStatement) { + this.currentOrQueryObject.variableExists(name); + } else { + super.variableExists(name); + } + return this; + } + + @Override + public PlanItemInstanceQuery variableNotExists(String name) { + if (inOrStatement) { + this.currentOrQueryObject.variableNotExists(name); + } else { + super.variableNotExists(name); + } + return this; + } + @Override public PlanItemInstanceQuery caseVariableValueEquals(String name, Object value) { - return variableValueEquals(name, value, false); + if (inOrStatement) { + this.currentOrQueryObject.variableValueEquals(name, value, false); + } else { + variableValueEquals(name, value, false); + } + return this; } @Override public PlanItemInstanceQuery caseVariableValueEquals(Object value) { - return variableValueEquals(value, false); + if (inOrStatement) { + this.currentOrQueryObject.variableValueEquals(value, false); + } else { + variableValueEquals(value, false); + } + return this; } @Override public PlanItemInstanceQuery caseVariableValueEqualsIgnoreCase(String name, String value) { - return variableValueEqualsIgnoreCase(name, value, false); + if (inOrStatement) { + this.currentOrQueryObject.variableValueEqualsIgnoreCase(name, value, false); + } else { + variableValueEqualsIgnoreCase(name, value, false); + } + return this; } @Override public PlanItemInstanceQuery caseVariableValueNotEquals(String name, Object value) { - return variableValueNotEquals(name, value, false); + if (inOrStatement) { + this.currentOrQueryObject.variableValueNotEquals(name, value, false); + } else { + variableValueNotEquals(name, value, false); + } + return this; } @Override public PlanItemInstanceQuery caseVariableValueNotEqualsIgnoreCase(String name, String value) { - return variableValueNotEqualsIgnoreCase(name, value, false); + if (inOrStatement) { + this.currentOrQueryObject.variableValueNotEqualsIgnoreCase(name, value, false); + } else { + variableValueNotEqualsIgnoreCase(name, value, false); + } + return this; } @Override public PlanItemInstanceQuery caseVariableValueGreaterThan(String name, Object value) { - return variableValueGreaterThan(name, value, false); + if (inOrStatement) { + this.currentOrQueryObject.variableValueGreaterThan(name, value, false); + } else { + variableValueGreaterThan(name, value, false); + } + return this; } @Override public PlanItemInstanceQuery caseVariableValueGreaterThanOrEqual(String name, Object value) { - return variableValueGreaterThanOrEqual(name, value, false); + if (inOrStatement) { + this.currentOrQueryObject.variableValueGreaterThanOrEqual(name, value, false); + } else { + variableValueGreaterThanOrEqual(name, value, false); + } + return this; } @Override public PlanItemInstanceQuery caseVariableValueLessThan(String name, Object value) { - return variableValueLessThan(name, value, false); + if (inOrStatement) { + this.currentOrQueryObject.variableValueLessThan(name, value, false); + } else { + variableValueLessThan(name, value, false); + } + return this; } @Override public PlanItemInstanceQuery caseVariableValueLessThanOrEqual(String name, Object value) { - return variableValueLessThanOrEqual(name, value, false); + if (inOrStatement) { + this.currentOrQueryObject.variableValueLessThanOrEqual(name, value, false); + } else { + variableValueLessThanOrEqual(name, value, false); + } + return this; } @Override public PlanItemInstanceQuery caseVariableValueLike(String name, String value) { - return variableValueLike(name, value, false); + if (inOrStatement) { + this.currentOrQueryObject.variableValueLike(name, value, false); + } else { + variableValueLike(name, value, false); + } + return this; } @Override public PlanItemInstanceQuery caseVariableValueLikeIgnoreCase(String name, String value) { - return variableValueLikeIgnoreCase(name, value, false); + if (inOrStatement) { + this.currentOrQueryObject.variableValueLikeIgnoreCase(name, value, false); + } else { + variableValueLikeIgnoreCase(name, value, false); + } + return this; } @Override public PlanItemInstanceQuery caseVariableExists(String name) { - return variableExists(name, false); + if (inOrStatement) { + this.currentOrQueryObject.variableExists(name, false); + } else { + variableExists(name, false); + } + return this; } @Override public PlanItemInstanceQuery caseVariableNotExists(String name) { - return variableNotExists(name, false); + if (inOrStatement) { + this.currentOrQueryObject.variableNotExists(name, false); + } else { + variableNotExists(name, false); + } + return this; + } + + @Override + public PlanItemInstanceQuery or() { + if (inOrStatement) { + throw new FlowableIllegalArgumentException("The query is already in an or statement"); + } + + inOrStatement = true; + if (commandContext != null) { + currentOrQueryObject = new PlanItemInstanceQueryImpl(commandContext, cmmnEngineConfiguration); + } else { + currentOrQueryObject = new PlanItemInstanceQueryImpl(commandExecutor, cmmnEngineConfiguration); + } + orQueryObjects.add(currentOrQueryObject); + return this; + } + + @Override + public PlanItemInstanceQuery endOr() { + if (!inOrStatement) { + throw new FlowableIllegalArgumentException("endOr() can only be called after calling or()"); + } + + inOrStatement = false; + currentOrQueryObject = null; + return this; } @Override @@ -861,4 +1290,16 @@ public List> getSafeInvolvedGroups() { public void setSafeInvolvedGroups(List> safeInvolvedGroups) { this.safeInvolvedGroups = safeInvolvedGroups; } + + @Override + protected void ensureVariablesInitialized() { + super.ensureVariablesInitialized(); + for (PlanItemInstanceQueryImpl orQueryObject : orQueryObjects) { + orQueryObject.ensureVariablesInitialized(); + } + } + + public List getOrQueryObjects() { + return orQueryObjects; + } } diff --git a/modules/flowable-cmmn-engine/src/main/resources/org/flowable/cmmn/db/mapping/entity/PlanItemInstance.xml b/modules/flowable-cmmn-engine/src/main/resources/org/flowable/cmmn/db/mapping/entity/PlanItemInstance.xml index 7f6fe30d612..276c6264fad 100644 --- a/modules/flowable-cmmn-engine/src/main/resources/org/flowable/cmmn/db/mapping/entity/PlanItemInstance.xml +++ b/modules/flowable-cmmn-engine/src/main/resources/org/flowable/cmmn/db/mapping/entity/PlanItemInstance.xml @@ -634,6 +634,297 @@ + + and + + + or RES.CASE_DEF_ID_ = #{orQueryObject.caseDefinitionId, jdbcType=VARCHAR} + + + or RES.DERIVED_CASE_DEF_ID_ = #{orQueryObject.derivedCaseDefinitionId, jdbcType=VARCHAR} + + + or RES.CASE_INST_ID_ = #{orQueryObject.caseInstanceId, jdbcType=VARCHAR} + + + or RES.STAGE_INST_ID_ = #{orQueryObject.stageInstanceId, jdbcType=VARCHAR} + + + or RES.ID_ = #{orQueryObject.planItemInstanceId, jdbcType=VARCHAR} + + + or RES.ELEMENT_ID_ = #{orQueryObject.elementId, jdbcType=VARCHAR} + + + or RES.ITEM_DEFINITION_ID_ = #{orQueryObject.planItemDefinitionId, jdbcType=VARCHAR} + + + or RES.ITEM_DEFINITION_TYPE_ = #{orQueryObject.planItemDefinitionType, jdbcType=VARCHAR} + + + or RES.ITEM_DEFINITION_TYPE_ in + + #{planItemDefinitionType, jdbcType=VARCHAR} + + + + or RES.NAME_ = #{orQueryObject.name, jdbcType=NVARCHAR} + + + or RES.STATE_ = #{orQueryObject.state, jdbcType=VARCHAR} + + + or RES.CREATE_TIME_ <= #{orQueryObject.createdBefore, jdbcType=TIMESTAMP} + + + or RES.CREATE_TIME_ >= #{orQueryObject.createdAfter, jdbcType=TIMESTAMP} + + + or RES.LAST_AVAILABLE_TIME_ <= #{orQueryObject.lastAvailableBefore, jdbcType=TIMESTAMP} + + + or RES.LAST_AVAILABLE_TIME_ >= #{orQueryObject.lastAvailableAfter, jdbcType=TIMESTAMP} + + + or RES.LAST_UNAVAILABLE_TIME_ <= #{orQueryObject.lastUnavailableBefore, jdbcType=TIMESTAMP} + + + or RES.LAST_UNAVAILABLE_TIME_ >= #{orQueryObject.lastUnavailableAfter, jdbcType=TIMESTAMP} + + + or RES.LAST_ENABLED_TIME_ <= #{orQueryObject.lastEnabledBefore, jdbcType=TIMESTAMP} + + + or RES.LAST_ENABLED_TIME_ >= #{orQueryObject.lastEnabledAfter, jdbcType=TIMESTAMP} + + + or RES.LAST_DISABLED_TIME_ <= #{orQueryObject.lastDisabledBefore, jdbcType=TIMESTAMP} + + + or RES.LAST_DISABLED_TIME_ >= #{orQueryObject.lastDisabledAfter, jdbcType=TIMESTAMP} + + + or RES.LAST_STARTED_TIME_ <= #{orQueryObject.lastStartedBefore, jdbcType=TIMESTAMP} + + + or RES.LAST_STARTED_TIME_ >= #{orQueryObject.lastStartedAfter, jdbcType=TIMESTAMP} + + + or RES.LAST_SUSPENDED_TIME_ <= #{orQueryObject.lastSuspendedBefore, jdbcType=TIMESTAMP} + + + or RES.LAST_SUSPENDED_TIME_ >= #{orQueryObject.lastSuspendedAfter, jdbcType=TIMESTAMP} + + + or RES.COMPLETED_TIME_ <= #{orQueryObject.completedBefore, jdbcType=TIMESTAMP} + + + or RES.COMPLETED_TIME_ >= #{orQueryObject.completedAfter, jdbcType=TIMESTAMP} + + + or RES.OCCURRED_TIME_ <= #{orQueryObject.occurredBefore, jdbcType=TIMESTAMP} + + + or RES.OCCURRED_TIME_ >= #{orQueryObject.occurredAfter, jdbcType=TIMESTAMP} + + + or RES.TERMINATED_TIME_ <= #{orQueryObject.terminatedBefore, jdbcType=TIMESTAMP} + + + or RES.TERMINATED_TIME_ >= #{orQueryObject.terminatedAfter, jdbcType=TIMESTAMP} + + + or RES.EXIT_TIME_ <= #{orQueryObject.exitBefore, jdbcType=TIMESTAMP} + + + or RES.EXIT_TIME_ >= #{orQueryObject.exitAfter, jdbcType=TIMESTAMP} + + + or RES.ENDED_TIME_ <= #{orQueryObject.endedBefore, jdbcType=TIMESTAMP} + + + or RES.ENDED_TIME_ >= #{orQueryObject.endedAfter, jdbcType=TIMESTAMP} + + + or RES.ENDED_TIME_ is not null + + + or RES.START_USER_ID_ = #{orQueryObject.startUserId, jdbcType=VARCHAR} + + + or RES.REFERENCE_ID_ = #{orQueryObject.referenceId, jdbcType=VARCHAR} + + + or RES.REFERENCE_TYPE_ = #{orQueryObject.referenceType, jdbcType=VARCHAR} + + + or RES.IS_COMPLETEABLE_ = #{orQueryObject.completable, jdbcType=BOOLEAN} + + + or RES.ENTRY_CRITERION_ID_ = #{orQueryObject.entryCriterionId, jdbcType=VARCHAR} + + + or RES.EXIT_CRITERION_ID_ = #{orQueryObject.exitCriterionId, jdbcType=VARCHAR} + + + or RES.EXTRA_VALUE_ = #{orQueryObject.formKey, jdbcType=VARCHAR} + + + or RES.EXTRA_VALUE_ = #{orQueryObject.extraValue, jdbcType=VARCHAR} + + + or RES.TENANT_ID_ = #{orQueryObject.tenantId, jdbcType=VARCHAR} + + + or (RES.TENANT_ID_ is null or RES.TENANT_ID_ = '') + + + or RES.IS_STAGE_ = #{orQueryObject.onlyStages, jdbcType=BOOLEAN} + + + or exists(select I.ID_ from ${prefix}ACT_RU_IDENTITYLINK I where I.SUB_SCOPE_ID_ = RES.ID_ and I.SCOPE_TYPE_ = 'planItem' + and + ( + + I.USER_ID_ = #{orQueryObject.involvedUser, jdbcType=NVARCHAR} + + + or + + + ( + + + or + + I.GROUP_ID_ IN + + #{involvedGroup, jdbcType=NVARCHAR} + + + ) + + ) + ) + + + or + + + + and EXISTS ( + select ID_ from ${prefix}ACT_RU_VARIABLE where NAME_ = #{queryVariableValue.name, jdbcType=NVARCHAR} + + and RES.CASE_INST_ID_ = SCOPE_ID_ and SUB_SCOPE_ID_ is null and SCOPE_TYPE_ = 'cmmn' + + + and RES.ID_ = SUB_SCOPE_ID_ and SCOPE_TYPE_ = 'cmmn' + + ) + + + and NOT EXISTS ( + select ID_ from ${prefix}ACT_RU_VARIABLE where NAME_ = #{queryVariableValue.name, jdbcType=NVARCHAR} + + and RES.CASE_INST_ID_ = SCOPE_ID_ and SUB_SCOPE_ID_ is null and SCOPE_TYPE_ = 'cmmn' + + + and RES.ID_ = SUB_SCOPE_ID_ and SCOPE_TYPE_ = 'cmmn' + + ) + + + and exists ( + select 1 + from ${prefix}ACT_RU_VARIABLE V + + + and RES.ID_ = V.SUB_SCOPE_ID_ + and V.SCOPE_TYPE_ = 'cmmn' + + + and RES.CASE_INST_ID_ = V.SCOPE_ID_ + and V.SUB_SCOPE_ID_ is null + and V.SCOPE_TYPE_ = 'cmmn' + + + + and V.NAME_= #{queryVariableValue.name, jdbcType=NVARCHAR} + + + and V.TYPE_ = #{queryVariableValue.type, jdbcType=NVARCHAR} + + + + + and (lower(V.TEXT_) + + + and (V.TEXT_ + + + + LIKE + + + #{queryVariableValue.textValue, jdbcType=NVARCHAR} + + ${wildcardEscapeClause} + + + or V.TEXT_ is null + + ) + + + and V.TEXT2_ + + LIKE + + + #{queryVariableValue.textValue2, jdbcType=NVARCHAR} + + ${wildcardEscapeClause} + + + + and (V.LONG_ + + #{queryVariableValue.longValue, jdbcType=BIGINT} + + or V.LONG_ is null + + ) + + + and (V.DOUBLE_ + + #{queryVariableValue.doubleValue, jdbcType=DOUBLE} + + or V.DOUBLE_ is null + + ) + + + + + + and (V.TEXT_ is not null or V.TEXT2_ is not null or V.LONG_ is not null or V.DOUBLE_ is not null or V.BYTEARRAY_ID_ is not null) + + + and V.TEXT_ is null and V.TEXT2_ is null and V.LONG_ is null and V.DOUBLE_ is null and V.BYTEARRAY_ID_ is null + + + + + ) + + + + + + diff --git a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/history/PlanItemInstanceHistoryServiceTest.java b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/history/PlanItemInstanceHistoryServiceTest.java index a9fccdd414c..a99e7adf00f 100644 --- a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/history/PlanItemInstanceHistoryServiceTest.java +++ b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/history/PlanItemInstanceHistoryServiceTest.java @@ -568,6 +568,11 @@ public void testQueryByUnavailableState() { List planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceLastUnavailableAfter(startTime).list(); assertThat(planItemInstances).extracting(PlanItemInstance::getName).containsExactly("myEventListener"); + planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceLastUnavailableAfter(startTime).endOr() + .list(); + assertThat(planItemInstances).extracting(PlanItemInstance::getName).containsExactly("myEventListener"); + if (CmmnHistoryTestHelper.isHistoryLevelAtLeast(HistoryLevel.ACTIVITY, cmmnEngineConfiguration)) { List historicPlanItemInstances = cmmnHistoryService.createHistoricPlanItemInstanceQuery().lastUnavailableAfter(startTime).list(); assertThat(historicPlanItemInstances).extracting(HistoricPlanItemInstance::getName).containsExactly("myEventListener"); @@ -576,6 +581,11 @@ public void testQueryByUnavailableState() { planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceLastUnavailableAfter(afterStartTime).list(); assertThat(planItemInstances).extracting(PlanItemInstance::getName).isEmpty(); + planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceLastUnavailableAfter(afterStartTime).endOr() + .list(); + assertThat(planItemInstances).extracting(PlanItemInstance::getName).isEmpty(); + if (CmmnHistoryTestHelper.isHistoryLevelAtLeast(HistoryLevel.ACTIVITY, cmmnEngineConfiguration)) { List historicPlanItemInstances = cmmnHistoryService.createHistoricPlanItemInstanceQuery().lastUnavailableAfter(afterStartTime).list(); assertThat(historicPlanItemInstances).extracting(HistoricPlanItemInstance::getName).isEmpty(); @@ -585,6 +595,11 @@ public void testQueryByUnavailableState() { planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceLastUnavailableBefore(afterStartTime).list(); assertThat(planItemInstances).extracting(PlanItemInstance::getName).containsExactly("myEventListener"); + planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceLastUnavailableBefore(afterStartTime).endOr() + .list(); + assertThat(planItemInstances).extracting(PlanItemInstance::getName).containsExactly("myEventListener"); + if (CmmnHistoryTestHelper.isHistoryLevelAtLeast(HistoryLevel.ACTIVITY, cmmnEngineConfiguration)) { List historicPlanItemInstances = cmmnHistoryService.createHistoricPlanItemInstanceQuery().lastUnavailableBefore(afterStartTime).list(); assertThat(historicPlanItemInstances).extracting(HistoricPlanItemInstance::getName).containsExactly("myEventListener"); @@ -594,6 +609,11 @@ public void testQueryByUnavailableState() { planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceLastUnavailableBefore(beforeStartTime).list(); assertThat(planItemInstances).extracting(PlanItemInstance::getName).isEmpty(); + planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceLastUnavailableBefore(beforeStartTime).endOr() + .list(); + assertThat(planItemInstances).extracting(PlanItemInstance::getName).isEmpty(); + if (CmmnHistoryTestHelper.isHistoryLevelAtLeast(HistoryLevel.ACTIVITY, cmmnEngineConfiguration)) { List historicPlanItemInstances = cmmnHistoryService.createHistoricPlanItemInstanceQuery().lastUnavailableBefore(beforeStartTime).list(); assertThat(historicPlanItemInstances).extracting(HistoricPlanItemInstance::getName).isEmpty(); diff --git a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/listener/PlanItemInstanceLifecycleListenerTest.java b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/listener/PlanItemInstanceLifecycleListenerTest.java index ccd21db250f..d815e592f39 100644 --- a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/listener/PlanItemInstanceLifecycleListenerTest.java +++ b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/listener/PlanItemInstanceLifecycleListenerTest.java @@ -405,6 +405,43 @@ public void testUserEventListenerRepetitionWithCondition() { assertThat(cmmnRuntimeService.createUserEventListenerInstanceQuery().caseInstanceId(caseInstance.getId()).count()).isZero(); } + + @Test + @CmmnDeployment + public void testTerminateCaseInstance() { + setTestLifeCycleListener(null, new TestTerminateLifecycleListener()); + + // Start case instance + CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder() + .caseDefinitionKey("testManualActivation") + .start(); + + assertThat(testLifeCycleListener.getEvents()).isEmpty(); + + List planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery().caseInstanceId(caseInstance.getId()).list(); + assertThat(planItemInstances).hasSize(3); + + PlanItemInstance planItemInstance = cmmnRuntimeService.createPlanItemInstanceQuery().caseInstanceId(caseInstance.getId()).planItemInstanceElementId("planItemTaskA").singleResult(); + assertThat(planItemInstance.getState()).isEqualTo(PlanItemInstanceState.ENABLED); + + cmmnRuntimeService.terminateCaseInstance(caseInstance.getId()); + + List lifeCycleEvents = testLifeCycleListener.getEvents(); + assertThat(lifeCycleEvents).hasSize(2); + + for (TestLifeCycleEvent lifeCycleEvent : lifeCycleEvents) { + if ("planItemTaskA".equals(lifeCycleEvent.getPlanItemInstance().getElementId())) { + assertThat(lifeCycleEvent.getPlanItemInstance().getElementId()).isEqualTo("planItemTaskA"); + assertThat(lifeCycleEvent.getOldState()).isEqualTo(PlanItemInstanceState.ENABLED); + assertThat(lifeCycleEvent.getNewState()).isEqualTo(PlanItemInstanceState.TERMINATED); + + } else { + assertThat(lifeCycleEvent.getPlanItemInstance().getElementId()).isEqualTo("planItemTaskB"); + assertThat(lifeCycleEvent.getOldState()).isEqualTo(PlanItemInstanceState.ENABLED); + assertThat(lifeCycleEvent.getNewState()).isEqualTo(PlanItemInstanceState.TERMINATED); + } + } + } private void setTestLifeCycleListener(String planItemDefinitionType, AbstractTestLifecycleListener testLifeCycleListener) { cmmnEngineConfiguration.addPlanItemInstanceLifeCycleListener(planItemDefinitionType, testLifeCycleListener); diff --git a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/listener/TestTerminateLifecycleListener.java b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/listener/TestTerminateLifecycleListener.java new file mode 100644 index 00000000000..5c3eb1bb8e9 --- /dev/null +++ b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/listener/TestTerminateLifecycleListener.java @@ -0,0 +1,28 @@ +/* Licensed 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.flowable.cmmn.test.listener; + +import org.flowable.cmmn.api.runtime.PlanItemInstanceState; + +public class TestTerminateLifecycleListener extends AbstractTestLifecycleListener { + + @Override + public String getSourceState() { + return PlanItemInstanceState.ENABLED; + } + + @Override + public String getTargetState() { + return null; + } +} diff --git a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/migration/CaseInstanceMigrationTest.java b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/migration/CaseInstanceMigrationTest.java index 877a7e1a771..37f14ea8f65 100644 --- a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/migration/CaseInstanceMigrationTest.java +++ b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/migration/CaseInstanceMigrationTest.java @@ -378,6 +378,67 @@ void withSimpleOneTaskCaseIntroducingNewTaskWithSentryLinkedToFirstTask() { } } } + + @Test + void withSimpleOneTaskCaseChangingOnlyTaskProperties() { + // Arrange + deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/one-task.cmmn.xml"); + CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("testCase").start(); + CaseDefinition destinationDefinition = deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/one-task-new-properties.cmmn.xml"); + + // Act + cmmnMigrationService.createCaseInstanceMigrationBuilder() + .migrateToCaseDefinition(destinationDefinition.getId()) + .migrate(caseInstance.getId()); + + // Assert + CaseInstance caseInstanceAfterMigration = cmmnRuntimeService.createCaseInstanceQuery() + .caseInstanceId(caseInstance.getId()) + .singleResult(); + assertThat(caseInstanceAfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + List planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance.getId()) + .list(); + assertThat(planItemInstances).hasSize(1); + Map> planItemsByElementId = planItemInstances.stream() + .collect(Collectors.groupingBy(PlanItemInstance::getElementId)); + PlanItemInstance planItem1 = planItemsByElementId.get("planItem1").get(0); + assertThat(planItem1.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + assertThat(planItem1.getName()).isEqualTo("Task 1"); + assertThat(planItem1.getState()).isEqualTo(PlanItemInstanceState.ACTIVE); + + Task task = cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).singleResult(); + assertThat(task.getTaskDefinitionKey()).isEqualTo("humanTask1"); + assertThat(task.getName()).isEqualTo("Task 2"); + assertThat(task.getFormKey()).isEqualTo("myForm"); + assertThat(task.getCategory()).isEqualTo("myCategory"); + assertThat(task.getDescription()).isEqualTo("Example description"); + assertThat(task.getScopeDefinitionId()).isEqualTo(destinationDefinition.getId()); + cmmnTaskService.complete(task.getId()); + + assertThat(cmmnRuntimeService.createCaseInstanceQuery().caseInstanceId(caseInstance.getId()).count()).isZero(); + + if (CmmnHistoryTestHelper.isHistoryLevelAtLeast(HistoryLevel.ACTIVITY, cmmnEngineConfiguration)) { + assertThat(cmmnHistoryService.createHistoricCaseInstanceQuery().caseInstanceId(caseInstance.getId()).count()).isEqualTo(1); + assertThat(cmmnHistoryService.createHistoricCaseInstanceQuery().caseInstanceId(caseInstance.getId()).singleResult().getCaseDefinitionId()) + .isEqualTo(destinationDefinition.getId()); + + List historicPlanItemInstances = cmmnHistoryService.createHistoricPlanItemInstanceQuery() + .planItemInstanceCaseInstanceId(caseInstance.getId()).list(); + assertThat(historicPlanItemInstances).hasSize(1); + assertThat(historicPlanItemInstances.get(0).getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + + List historicTasks = cmmnHistoryService.createHistoricTaskInstanceQuery().caseInstanceId(caseInstance.getId()).list(); + assertThat(historicTasks).hasSize(1); + for (HistoricTaskInstance historicTask : historicTasks) { + assertThat(historicTask.getScopeDefinitionId()).isEqualTo(destinationDefinition.getId()); + assertThat(historicTask.getName()).isEqualTo("Task 2"); + assertThat(historicTask.getFormKey()).isEqualTo("myForm"); + assertThat(historicTask.getCategory()).isEqualTo("myCategory"); + assertThat(historicTask.getDescription()).isEqualTo("Example description"); + } + } + } @Test void withTwoTasksIntroducingANewStageAroundSecondTask() { @@ -488,6 +549,92 @@ void withTwoTasksIntroducingANewStageAroundSecondTaskAndSecondTaskActive() { assertThat(planItem3.getPlanItemDefinitionId()).isEqualTo("expandedStage1"); assertThat(planItem3.getState()).isEqualTo(PlanItemInstanceState.ACTIVE); } + + @Test + void withTwoTasksCaseChangingTaskProperties() { + // Arrange + deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/one-task.cmmn.xml"); + CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("testCase").start(); + CaseDefinition destinationDefinition = deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/two-task-new-properties.cmmn.xml"); + + // Act + cmmnMigrationService.createCaseInstanceMigrationBuilder() + .migrateToCaseDefinition(destinationDefinition.getId()) + .addActivatePlanItemDefinitionMapping(PlanItemDefinitionMappingBuilder.createActivatePlanItemDefinitionMappingFor("humanTask2")) + .migrate(caseInstance.getId()); + + // Assert + CaseInstance caseInstanceAfterMigration = cmmnRuntimeService.createCaseInstanceQuery() + .caseInstanceId(caseInstance.getId()) + .singleResult(); + assertThat(caseInstanceAfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + List planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance.getId()) + .list(); + assertThat(planItemInstances).hasSize(2); + Map> planItemsByElementId = planItemInstances.stream() + .collect(Collectors.groupingBy(PlanItemInstance::getElementId)); + PlanItemInstance planItem1 = planItemsByElementId.get("planItem1").get(0); + assertThat(planItem1.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + assertThat(planItem1.getName()).isEqualTo("Task 1"); + assertThat(planItem1.getState()).isEqualTo(PlanItemInstanceState.ACTIVE); + + Task task = cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).taskDefinitionKey("humanTask1").singleResult(); + assertThat(task.getTaskDefinitionKey()).isEqualTo("humanTask1"); + assertThat(task.getName()).isEqualTo("Task 2"); + assertThat(task.getFormKey()).isEqualTo("myForm"); + assertThat(task.getCategory()).isEqualTo("myCategory"); + assertThat(task.getDescription()).isEqualTo("Example description"); + assertThat(task.getScopeDefinitionId()).isEqualTo(destinationDefinition.getId()); + cmmnTaskService.complete(task.getId()); + + PlanItemInstance planItem2 = planItemsByElementId.get("planItem2").get(0); + assertThat(planItem2.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + assertThat(planItem2.getName()).isEqualTo("Task 3"); + assertThat(planItem2.getState()).isEqualTo(PlanItemInstanceState.ACTIVE); + + task = cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).singleResult(); + assertThat(task.getTaskDefinitionKey()).isEqualTo("humanTask2"); + assertThat(task.getName()).isEqualTo("Task 3"); + assertThat(task.getFormKey()).isEqualTo("myForm2"); + assertThat(task.getCategory()).isEqualTo("myCategory2"); + assertThat(task.getDescription()).isEqualTo("Example description2"); + assertThat(task.getScopeDefinitionId()).isEqualTo(destinationDefinition.getId()); + cmmnTaskService.complete(task.getId()); + + assertThat(cmmnRuntimeService.createCaseInstanceQuery().caseInstanceId(caseInstance.getId()).count()).isZero(); + + if (CmmnHistoryTestHelper.isHistoryLevelAtLeast(HistoryLevel.ACTIVITY, cmmnEngineConfiguration)) { + assertThat(cmmnHistoryService.createHistoricCaseInstanceQuery().caseInstanceId(caseInstance.getId()).count()).isEqualTo(1); + assertThat(cmmnHistoryService.createHistoricCaseInstanceQuery().caseInstanceId(caseInstance.getId()).singleResult().getCaseDefinitionId()) + .isEqualTo(destinationDefinition.getId()); + + List historicPlanItemInstances = cmmnHistoryService.createHistoricPlanItemInstanceQuery() + .planItemInstanceCaseInstanceId(caseInstance.getId()).list(); + assertThat(historicPlanItemInstances).hasSize(2); + assertThat(historicPlanItemInstances.get(0).getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + assertThat(historicPlanItemInstances.get(1).getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + + List historicTasks = cmmnHistoryService.createHistoricTaskInstanceQuery().caseInstanceId(caseInstance.getId()).list(); + assertThat(historicTasks).hasSize(2); + for (HistoricTaskInstance historicTask : historicTasks) { + assertThat(historicTask.getScopeDefinitionId()).isEqualTo(destinationDefinition.getId()); + + if ("humanTask1".equals(historicTask.getTaskDefinitionKey())) { + assertThat(historicTask.getName()).isEqualTo("Task 2"); + assertThat(historicTask.getFormKey()).isEqualTo("myForm"); + assertThat(historicTask.getCategory()).isEqualTo("myCategory"); + assertThat(historicTask.getDescription()).isEqualTo("Example description"); + + } else { + assertThat(historicTask.getName()).isEqualTo("Task 3"); + assertThat(historicTask.getFormKey()).isEqualTo("myForm2"); + assertThat(historicTask.getCategory()).isEqualTo("myCategory2"); + assertThat(historicTask.getDescription()).isEqualTo("Example description2"); + } + } + } + } @Test void withSimpleOneTaskCaseIntroducingNewTaskWithConditionalSentryNotActivated() { diff --git a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/CaseInstanceQueryImplTest.java b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/CaseInstanceQueryImplTest.java index 8cddf467440..89db895889a 100644 --- a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/CaseInstanceQueryImplTest.java +++ b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/CaseInstanceQueryImplTest.java @@ -28,6 +28,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -1726,6 +1727,73 @@ public void testQueryLocalDateTimeVariable() throws Exception { Assertions.assertThat(caseInstance).isNull(); } + @Test + public void testQueryUUIDVariable() throws Exception { + Map vars = new HashMap<>(); + UUID someUUID = UUID.randomUUID(); + vars.put("uuidVar", someUUID); + + CaseInstance caseInstance1 = cmmnRuntimeService.createCaseInstanceBuilder() + .caseDefinitionKey("oneTaskCase") + .variables(vars) + .start(); + + UUID someUUID2 = UUID.randomUUID(); + vars = new HashMap<>(); + vars.put("uuidVar", someUUID); + vars.put("uuidVar2", someUUID2); + CaseInstance caseInstance2 = cmmnRuntimeService.createCaseInstanceBuilder() + .caseDefinitionKey("oneTaskCase") + .variables(vars) + .start(); + + UUID someUUID3 = UUID.randomUUID(); + vars = new HashMap<>(); + vars.put("uuidVar", someUUID3); + CaseInstance caseInstance3 = cmmnRuntimeService.createCaseInstanceBuilder() + .caseDefinitionKey("oneTaskCase") + .variables(vars) + .start(); + + // Query on single uuid variable, should result in 2 matches + CaseInstanceQuery query = cmmnRuntimeService.createCaseInstanceQuery().variableValueEquals("uuidVar", someUUID); + List caseInstances = query.list(); + Assertions.assertThat(caseInstances).hasSize(2); + + // Query on two uuid variables, should result in single value + query = cmmnRuntimeService.createCaseInstanceQuery().variableValueEquals("uuidVar", someUUID) + .variableValueEquals("uuidVar2", someUUID2); + CaseInstance caseInstance = query.singleResult(); + Assertions.assertThat(caseInstance).isNotNull(); + Assertions.assertThat(caseInstance.getId()).isEqualTo(caseInstance2.getId()); + + UUID unexistingUUID = UUID.randomUUID(); + // Query with unexisting variable value + caseInstance = cmmnRuntimeService.createCaseInstanceQuery().variableValueEquals("uuidVar", unexistingUUID).singleResult(); + Assertions.assertThat(caseInstance).isNull(); + + // Test NOT_EQUALS + caseInstance = cmmnRuntimeService.createCaseInstanceQuery().variableValueNotEquals("uuidVar", someUUID).singleResult(); + Assertions.assertThat(caseInstance).isNotNull(); + Assertions.assertThat(caseInstance.getId()).isEqualTo(caseInstance3.getId()); + + // Test value-only matching + caseInstance = cmmnRuntimeService.createCaseInstanceQuery().variableValueEquals(someUUID3).singleResult(); + Assertions.assertThat(caseInstance).isNotNull(); + Assertions.assertThat(caseInstance.getId()).isEqualTo(caseInstance3.getId()); + + caseInstances = cmmnRuntimeService.createCaseInstanceQuery().variableValueEquals(someUUID).list(); + Assertions.assertThat(caseInstances) + .extracting(CaseInstance::getId) + .containsExactlyInAnyOrder( + caseInstance1.getId(), + caseInstance2.getId() + ); + + caseInstance = cmmnRuntimeService.createCaseInstanceQuery().variableValueEquals(unexistingUUID).singleResult(); + Assertions.assertThat(caseInstance).isNull(); + } + @Test public void testLocalization() { CaseInstance createdCase = cmmnRuntimeService.createCaseInstanceBuilder() diff --git a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/CasePageTaskTest.java b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/CasePageTaskTest.java index 46f03e0e1a2..21a123b7ae8 100644 --- a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/CasePageTaskTest.java +++ b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/CasePageTaskTest.java @@ -81,6 +81,14 @@ public void testInStage() { .singleResult(); assertThat(pagePlanItemInstance).isNotNull(); + pagePlanItemInstance = cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").caseInstanceId(caseInstance.getId()).endOr() + .or().caseInstanceId("undefinedId").planItemDefinitionId("casePageTask1").endOr() + .or().caseInstanceId("undefinedId").planItemInstanceFormKey("myFormKeyValue").endOr() + .includeEnded() + .singleResult(); + assertThat(pagePlanItemInstance).isNotNull(); + // page tasks go into terminated or completed state, depending on the parent ending type like complete or exit assertThat(pagePlanItemInstance.getState()).isEqualTo(PlanItemInstanceState.COMPLETED); assertThat(pagePlanItemInstance.getFormKey()).isEqualTo("myFormKeyValue"); @@ -178,6 +186,10 @@ public void testIdentityLinks() { planItemInstance = cmmnRuntimeService.createPlanItemInstanceQuery().involvedUser("janedoe").singleResult(); assertThat(planItemInstance.getName()).isEqualTo("Case Page Task One"); + planItemInstance = cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").involvedUser("janedoe").endOr().singleResult(); + assertThat(planItemInstance.getName()).isEqualTo("Case Page Task One"); + planItemInstance = cmmnRuntimeService.createPlanItemInstanceQuery().involvedUser("johndoe2").singleResult(); assertThat(planItemInstance).isNull(); @@ -185,6 +197,10 @@ public void testIdentityLinks() { planItemInstance = cmmnRuntimeService.createPlanItemInstanceQuery().involvedGroups(groups).singleResult(); assertThat(planItemInstance.getName()).isEqualTo("Case Page Task One"); + planItemInstance = cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").involvedGroups(groups).endOr().singleResult(); + assertThat(planItemInstance.getName()).isEqualTo("Case Page Task One"); + planItemInstance = cmmnRuntimeService.createPlanItemInstanceQuery().involvedUser("johndoe").involvedGroups(groups).singleResult(); assertThat(planItemInstance.getName()).isEqualTo("Case Page Task One"); diff --git a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/HumanTaskTest.java b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/HumanTaskTest.java index 9d93e1370d7..6def8de0a20 100644 --- a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/HumanTaskTest.java +++ b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/HumanTaskTest.java @@ -596,4 +596,28 @@ public Void execute(CommandContext commandContext) { cmmnEngineConfiguration.getJobServiceConfiguration().setJobHandlers(existingJobHandlers); } } + + @Test + @CmmnDeployment + public void testTaskNonBlockingWithEntryAndExitCriterion() { + CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder() + .caseDefinitionKey("myCase") + .start(); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().caseInstanceId(caseInstance.getId()).planItemDefinitionId("task1").list()).hasSize(1); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().caseInstanceId(caseInstance.getId()) + .planItemDefinitionId("task1") + .planItemInstanceStateEnabled().list()).hasSize(1); + + cmmnRuntimeService.startPlanItemInstance(cmmnRuntimeService.createPlanItemInstanceQuery().caseInstanceId(caseInstance.getId()) + .planItemDefinitionId("task1") + .planItemInstanceStateEnabled().singleResult().getId()); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().caseInstanceId(caseInstance.getId()) + .planItemDefinitionId("task1") + .planItemInstanceStateActive().list()).hasSize(0); + + assertThat(cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).list()).hasSize(0); + assertThat(cmmnRuntimeService.createCaseInstanceQuery().count()).isZero(); + } } diff --git a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/PlanItemInstanceQueryTest.java b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/PlanItemInstanceQueryTest.java index b1ac4ec847e..c3a524da287 100644 --- a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/PlanItemInstanceQueryTest.java +++ b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/PlanItemInstanceQueryTest.java @@ -13,7 +13,10 @@ package org.flowable.cmmn.test.runtime; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.tuple; +import static org.flowable.cmmn.api.runtime.PlanItemInstanceState.ACTIVE; +import static org.flowable.cmmn.api.runtime.PlanItemInstanceState.AVAILABLE; import java.util.ArrayList; import java.util.Arrays; @@ -29,6 +32,7 @@ import org.flowable.cmmn.engine.PlanItemLocalizationManager; import org.flowable.cmmn.engine.test.CmmnDeployment; import org.flowable.cmmn.engine.test.FlowableCmmnTestCase; +import org.flowable.common.engine.api.FlowableIllegalArgumentException; import org.flowable.task.api.Task; import org.junit.Before; import org.junit.Test; @@ -47,14 +51,22 @@ public void deployCaseDefinition() { .deploy()); caseDefinitionId = cmmnRepositoryService.createCaseDefinitionQuery() .deploymentId(deploymentId) + .caseDefinitionKey("testPlanItemInstanceQuery") .singleResult() .getId(); } @Test + @CmmnDeployment(resources = "org/flowable/cmmn/test/runtime/oneHumanTaskCase.cmmn") public void testByCaseDefinitionId() { startInstances(5); - assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().list()).hasSize(20); + cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("oneHumanTaskCase").start(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().caseDefinitionId(caseDefinitionId).list()).hasSize(20); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").caseDefinitionId(caseDefinitionId).endOr() + .list()).hasSize(20); + } @Test @@ -62,6 +74,9 @@ public void testByCaseInstanceId() { List caseInstanceIds = startInstances(3); for (String caseInstanceId : caseInstanceIds) { assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().caseInstanceId(caseInstanceId).list()).hasSize(4); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseDefinitionId("undefinedId").caseInstanceId(caseInstanceId).endOr() + .list()).hasSize(4); } } @@ -73,6 +88,10 @@ public void testByStageInstanceId() { .planItemInstanceName("Stage one") .singleResult(); assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().stageInstanceId(planItemInstance.getId()).count()).isEqualTo(2); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").stageInstanceId(planItemInstance.getId()).endOr() + .count()).isEqualTo(2); + } @Test @@ -81,6 +100,9 @@ public void testByPlanItemInstanceId() { List planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery().list(); for (PlanItemInstance planItemInstance : planItemInstances) { assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceId(planItemInstance.getId()).count()).isEqualTo(1); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceId(planItemInstance.getId()).endOr() + .count()).isEqualTo(1); } } @@ -88,12 +110,20 @@ public void testByPlanItemInstanceId() { public void testByElementId() { startInstances(4); assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceElementId("planItem3").list()).hasSize(4); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceElementId("planItem3").endOr() + .list()).hasSize(4); + } @Test public void testByName() { startInstances(9); assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceName("B").list()).hasSize(9); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceName("B").endOr() + .list()).hasSize(9); + } @Test @@ -107,6 +137,27 @@ public void testByState() { assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceState(PlanItemInstanceState.ENABLED).list()).hasSize(1); assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceStateEnabled().list()).hasSize(1); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceState(PlanItemInstanceState.ACTIVE).endOr() + .list()).hasSize(2); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceStateActive().endOr() + .list()).hasSize(2); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceState(PlanItemInstanceState.AVAILABLE).endOr() + .list()).hasSize(1); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceStateAvailable().endOr() + .list()).hasSize(1); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceState(PlanItemInstanceState.ENABLED).endOr() + .list()).hasSize(1); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceStateEnabled().endOr() + .list()).hasSize(1); } @Test @@ -114,6 +165,13 @@ public void testByPlanItemDefinitionType() { startInstances(3); assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemDefinitionType(PlanItemDefinitionType.HUMAN_TASK).list()).hasSize(6); assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemDefinitionType(PlanItemDefinitionType.STAGE).list()).hasSize(6); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemDefinitionType(PlanItemDefinitionType.HUMAN_TASK).endOr() + .list()).hasSize(6); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemDefinitionType(PlanItemDefinitionType.STAGE).endOr() + .list()).hasSize(6); } @Test @@ -121,6 +179,11 @@ public void testByPlanItemDefinitionTypes() { startInstances(2); assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() .planItemDefinitionTypes(Arrays.asList(PlanItemDefinitionType.STAGE, PlanItemDefinitionType.HUMAN_TASK)).list()).hasSize(8); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemDefinitionTypes(Arrays.asList(PlanItemDefinitionType.STAGE, PlanItemDefinitionType.HUMAN_TASK)) + .endOr() + .list()).hasSize(8); } @Test @@ -133,6 +196,16 @@ public void testByStateEnabled() { .hasSize(4) .extracting(PlanItemInstance::getName) .containsOnly("B"); + + planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceStateEnabled().endOr() + .list(); + + assertThat(planItemInstances) + .hasSize(4) + .extracting(PlanItemInstance::getName) + .containsOnly("B"); + } @Test @@ -148,6 +221,11 @@ public void testByStateDisabled() { assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceStateDisabled().list()) .hasSize(2) .extracting(PlanItemInstance::getName).containsOnly("B"); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceStateDisabled().endOr() + .list()).hasSize(2) + .extracting(PlanItemInstance::getName).containsOnly("B"); } @Test @@ -163,6 +241,16 @@ public void testByStateAvailable() { .hasSize(3) .extracting(PlanItemInstance::getName) .containsOnly("Stage two"); + + planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceStateAvailable().endOr() + .orderByName().asc() + .list(); + + assertThat(planItemInstances) + .hasSize(3) + .extracting(PlanItemInstance::getName) + .containsOnly("Stage two"); } @Test @@ -178,6 +266,16 @@ public void testByStateActive() { .hasSize(4) .extracting(PlanItemInstance::getName) .containsExactly("A", "A", "Stage one", "Stage one"); + + planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceStateActive().endOr() + .orderByName().asc() + .list(); + + assertThat(planItemInstances) + .hasSize(4) + .extracting(PlanItemInstance::getName) + .containsExactly("A", "A", "Stage one", "Stage one"); } @Test @@ -192,6 +290,16 @@ public void testByStateAndType() { .planItemInstanceState(PlanItemInstanceState.ENABLED) .planItemDefinitionType(PlanItemDefinitionType.HUMAN_TASK) .list()).hasSize(3); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceState(PlanItemInstanceState.ACTIVE).endOr() + .planItemDefinitionType(PlanItemDefinitionType.HUMAN_TASK) + .list()).hasSize(3); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .planItemInstanceState(PlanItemInstanceState.ENABLED) + .or().planItemDefinitionType(PlanItemDefinitionType.HUMAN_TASK).caseInstanceId("undefinedId").endOr() + .list()).hasSize(3); } @Test @@ -217,6 +325,19 @@ public void testByStateCompleted() { // Without ended, should only return runtime plan item instances assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceStateCompleted().list()).isEmpty(); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceStateCompleted().endOr().includeEnded() + .list()).hasSize(3); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .planItemInstanceStateCompleted() + .includeEnded() + .list()).hasSize(3); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceStateCompleted().endOr() + .list()).isEmpty(); + } @Test @@ -231,6 +352,18 @@ public void testByStateTerminated() { .list(); assertThat(planItemInstances).isEmpty(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").ended().endOr() + .planItemInstanceStateTerminated() + .list() + ).isEmpty(); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .ended() + .or().caseInstanceId("undefinedId").planItemInstanceStateTerminated().endOr() + .list() + ).isEmpty(); + // Completing the user event will terminate A/C/Stage for c UserEventListenerInstance userEventListenerInstance = cmmnRuntimeService.createUserEventListenerInstanceQuery() .caseInstanceId(caseInstance.getId()) @@ -245,6 +378,14 @@ public void testByStateTerminated() { assertThat(planItemInstances) .extracting(PlanItemInstance::getName) .containsExactly("A", "C", "The Stage"); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").ended().endOr() + .or().caseInstanceId("undefinedId").planItemInstanceStateTerminated().endOr() + .orderByName().asc() + .list()) + .extracting(PlanItemInstance::getName) + .containsExactly("A", "C", "The Stage"); } @Test @@ -265,6 +406,24 @@ public void testIncludeEnded() { planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceName("A").ended().list(); assertThat(planItemInstances).hasSize(4); + + planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceName("A").endOr() + .list(); + + assertThat(planItemInstances).hasSize(7); // 11 - 4 (runtime only) + + planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceName("A").endOr() + .includeEnded() + .list(); + assertThat(planItemInstances).hasSize(11); + + planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceName("A").endOr() + .or().caseInstanceId("undefinedId").ended().endOr() + .list(); + assertThat(planItemInstances).hasSize(4); } @Test @@ -288,6 +447,21 @@ public void testCreatedBefore() { .planItemInstanceCreatedBefore(new Date(now.getTime() + 30000)) .list(); assertThat(planItemInstances).hasSize(7); + + planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery() + .planItemInstanceName("A") + .planItemDefinitionType(PlanItemDefinitionType.HUMAN_TASK) + .or().caseInstanceId("undefinedId").planItemInstanceCreatedBefore(new Date(now.getTime() + 10000)).endOr() + .list(); + assertThat(planItemInstances).hasSize(3); + + planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery() + .planItemInstanceName("A") + .planItemDefinitionType(PlanItemDefinitionType.HUMAN_TASK) + .or().caseInstanceId("undefinedId").planItemInstanceCreatedBefore(new Date(now.getTime() + 30000)).endOr() + .list(); + + assertThat(planItemInstances).hasSize(7); } @Test @@ -311,6 +485,21 @@ public void testCreatedAfter() { .planItemInstanceCreatedAfter(new Date(now.getTime() + 10000)) .list(); assertThat(planItemInstances).hasSize(8); + + planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery() + .or().planItemInstanceName("A").caseInstanceId("undefinedId").endOr() + .or().caseInstanceId("undefinedId").planItemDefinitionType(PlanItemDefinitionType.HUMAN_TASK).endOr() + .or().caseInstanceId("undefinedId").planItemInstanceCreatedAfter(new Date(now.getTime() - 10000)).endOr() + .list(); + assertThat(planItemInstances).hasSize(10); + + planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery() + .or().planItemInstanceName("A").caseInstanceId("undefinedId").endOr() + .or().caseInstanceId("undefinedId").planItemDefinitionType(PlanItemDefinitionType.HUMAN_TASK).endOr() + .or().caseInstanceId("undefinedId").planItemInstanceCreatedAfter(new Date(now.getTime() + 10000)).endOr() + .list(); + + assertThat(planItemInstances).hasSize(8); } @Test @@ -348,6 +537,34 @@ public void testLastAvailableBeforeAndAfter() { .planItemInstanceLastAvailableBefore(new Date(now.getTime() + 10000)) .list(); assertThat(planItemInstances).hasSize(3); + + planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceName("A").endOr() + .or().planItemDefinitionType(PlanItemDefinitionType.HUMAN_TASK).endOr() + .or().caseInstanceId("undefinedId").planItemInstanceLastAvailableAfter(new Date(now.getTime() - 10000)).endOr() + .list(); + assertThat(planItemInstances).hasSize(8); + + planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceName("A").endOr() + .or().caseInstanceId("undefinedId").planItemDefinitionType(PlanItemDefinitionType.HUMAN_TASK).endOr() + .or().caseInstanceId("undefinedId").planItemInstanceLastAvailableBefore(new Date(now.getTime() - 10000)).endOr() + .or().caseInstanceId("undefinedId").list(); + assertThat(planItemInstances).isEmpty(); + + planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceName("A").endOr() + .or().caseInstanceId("undefinedId").planItemDefinitionType(PlanItemDefinitionType.HUMAN_TASK).endOr() + .or().caseInstanceId("undefinedId").planItemInstanceLastAvailableAfter(new Date(now.getTime() + 10000)).endOr() + .list(); + assertThat(planItemInstances).hasSize(5); + + planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceName("A").endOr() + .or().caseInstanceId("undefinedId").planItemDefinitionType(PlanItemDefinitionType.HUMAN_TASK).endOr() + .or().caseInstanceId("undefinedId").planItemInstanceLastAvailableBefore(new Date(now.getTime() + 10000)).endOr() + .list(); + assertThat(planItemInstances).hasSize(3); } @Test @@ -379,6 +596,28 @@ public void testLastEnabledBeforeAndAfter() { planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery() .planItemInstanceLastEnabledAfter(new Date(now.getTime() + 250000)).list(); assertThat(planItemInstances).isEmpty(); + + planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceLastEnabledBefore(new Date(now.getTime() + 30000)).endOr().list(); + assertThat(planItemInstances).hasSize(5); + + planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceLastEnabledBefore(new Date(now.getTime() + 5000)).endOr().list(); + assertThat(planItemInstances).hasSize(2); + + planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceLastEnabledBefore(new Date(now.getTime() - 1000)).endOr().list(); + assertThat(planItemInstances).isEmpty(); + + // After + planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceLastEnabledAfter(new Date(now.getTime() - 5000)).endOr().list(); + assertThat(planItemInstances).hasSize(5); + + planItemInstances = cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceLastEnabledAfter(new Date(now.getTime() + 250000)).endOr() + .list(); + assertThat(planItemInstances).isEmpty(); } @Test @@ -400,11 +639,31 @@ public void testLastDisabledBeforeAndAfter() { assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceLastDisabledBefore(new Date(now.getTime() + 5000)).list()).hasSize(1); assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceLastDisabledBefore(new Date(now.getTime() - 5000)).list()).isEmpty(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceLastDisabledBefore(new Date(now.getTime() - 5000)).endOr() + .list()).isEmpty(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceLastDisabledBefore(new Date(now.getTime() + 5000)).endOr() + .list()).hasSize(1); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceLastDisabledBefore(new Date(now.getTime() - 5000)).endOr() + .list()).isEmpty(); + // After assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceLastDisabledAfter(new Date(now.getTime())).list()).hasSize(2); assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceLastDisabledAfter(new Date(now.getTime() + 5000)).list()).hasSize(1); assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceLastDisabledAfter(new Date(now.getTime() + 11000)).list()).isEmpty(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceLastDisabledAfter(new Date(now.getTime())).endOr() + .list()).hasSize(2); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceLastDisabledAfter(new Date(now.getTime() + 5000)).endOr() + .list()).hasSize(1); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceLastDisabledAfter(new Date(now.getTime() + 11000)).endOr() + .list()).isEmpty(); + // Re-enable and disable PlanItemInstance planItemInstance = cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceId(planItemInstanceId).singleResult(); Date lastEnabledTime = planItemInstance.getLastEnabledTime(); @@ -423,6 +682,22 @@ public void testLastDisabledBeforeAndAfter() { assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceLastDisabledAfter(new Date(now.getTime())).list()).hasSize(2); assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceLastDisabledAfter(new Date(now.getTime() + 15000)).list()).hasSize(1); assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceLastDisabledAfter(new Date(now.getTime() + 35000)).list()).isEmpty(); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceLastDisabledBefore(new Date(now.getTime() + 20000)).endOr() + .list()).hasSize(1); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceLastDisabledBefore(new Date(now.getTime() + 5000)).endOr() + .list()).isEmpty(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceLastDisabledAfter(new Date(now.getTime())).endOr() + .list()).hasSize(2); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceLastDisabledAfter(new Date(now.getTime() + 15000)).endOr() + .list()).hasSize(1); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceLastDisabledAfter(new Date(now.getTime() + 35000)).endOr() + .list()).isEmpty(); } @Test @@ -437,6 +712,20 @@ public void testLastStartedBeforeAndAfter() { assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceLastStartedBefore(new Date(now.getTime() + 1000)).list()).hasSize(8); assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceLastStartedBefore(new Date(now.getTime() - 1000)).list()).isEmpty(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceLastStartedAfter(new Date(now.getTime() - 1000)).endOr() + .list()).hasSize(8); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceLastStartedAfter(new Date(now.getTime() + 1000)).endOr() + .list()).isEmpty(); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceLastStartedBefore(new Date(now.getTime() + 1000)).endOr() + .list()).hasSize(8); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceLastStartedBefore(new Date(now.getTime() - 1000)).endOr() + .list()).isEmpty(); + // Starting an enabled planitem setClockTo(now.getTime() + 10000); cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceName("B").listPage(0, 2) @@ -450,6 +739,26 @@ public void testLastStartedBeforeAndAfter() { assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceLastStartedBefore(new Date(now.getTime() + 15000)).list()).hasSize(10); assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceLastStartedBefore(new Date(now.getTime() - 1000)).list()).isEmpty(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceLastStartedAfter(new Date(now.getTime() - 1000)).endOr() + .list()).hasSize(10); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceLastStartedAfter(new Date(now.getTime() + 5000)).endOr() + .list()).hasSize(2); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceLastStartedAfter(new Date(now.getTime() + 15000)).endOr() + .list()).isEmpty(); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceLastStartedBefore(new Date(now.getTime() + 1000)).endOr() + .list()).hasSize(8); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceLastStartedBefore(new Date(now.getTime() + 15000)).endOr() + .list()).hasSize(10); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceLastStartedBefore(new Date(now.getTime() - 1000)).endOr() + .list()).isEmpty(); + } @Test @@ -489,6 +798,70 @@ public void testCompletedBeforeAndAfter() { assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceEndedAfter(new Date(now.getTime())).ended().list()).hasSize(2); assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceEndedAfter(new Date(now.getTime() + 15000)).includeEnded().list()) .hasSize(1); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceCompletedBefore(new Date(now.getTime() + 30000)).endOr() + .list()).isEmpty(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceCompletedBefore(new Date(now.getTime() + 30000)).endOr() + .includeEnded() + .list()) + .hasSize(2); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceCompletedBefore(new Date(now.getTime() + 15000)).endOr() + .includeEnded() + .list()) + .hasSize(1); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceCompletedAfter(new Date(now.getTime())).endOr() + .list()).isEmpty(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceCompletedAfter(new Date(now.getTime())).endOr() + .includeEnded() + .list()).hasSize(2); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceCompletedAfter(new Date(now.getTime())).endOr() + .or().caseInstanceId("undefinedId").ended().endOr() + .list()).hasSize(2); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceCompletedAfter(new Date(now.getTime() + 15000)).endOr() + .includeEnded() + .list()) + .hasSize(1); + + // Same queries, but with endedBefore/After + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceEndedBefore(new Date(now.getTime() + 30000)).endOr() + .list()).isEmpty(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceEndedBefore(new Date(now.getTime() + 30000)).endOr() + .includeEnded() + .list()) + .hasSize(2); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceEndedBefore(new Date(now.getTime() + 15000)).endOr() + .includeEnded() + .list()) + .hasSize(1); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceEndedAfter(new Date(now.getTime())).endOr() + .list()).isEmpty(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceEndedAfter(new Date(now.getTime())).endOr() + .includeEnded() + .list()).hasSize(2); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceEndedAfter(new Date(now.getTime())).endOr() + .or().caseInstanceId("undefinedId").ended().endOr() + .list()).hasSize(2); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceEndedAfter(new Date(now.getTime() + 15000)).endOr() + .includeEnded() + .list()) + .hasSize(1); + } @Test @@ -519,6 +892,41 @@ public void testLastOccurredBeforeAndAfter() { .hasSize(1); assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceOccurredBefore(new Date(now.getTime() - 1000)).includeEnded().list()) .isEmpty(); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceOccurredAfter(new Date(now.getTime() - 1000)).endOr() + .list()).isEmpty(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceOccurredAfter(new Date(now.getTime() - 1000)).endOr() + .includeEnded() + .list()) + .hasSize(2); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceOccurredAfter(new Date(now.getTime() + 1000)).endOr() + .includeEnded() + .list()) + .hasSize(1); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceOccurredAfter(new Date(now.getTime() + 15000)).endOr() + .includeEnded() + .list()) + .isEmpty(); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceOccurredBefore(new Date(now.getTime() + 20000)).endOr() + .includeEnded() + .list()) + .hasSize(2); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceOccurredBefore(new Date(now.getTime() + 5000)).endOr() + .includeEnded() + .list()) + .hasSize(1); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceOccurredBefore(new Date(now.getTime() - 1000)).endOr() + .includeEnded() + .list()) + .isEmpty(); } @Test @@ -568,6 +976,75 @@ public void testExitBeforeAndAfter() { .planItemDefinitionType(PlanItemDefinitionType.USER_EVENT_LISTENER) .planItemInstanceEndedAfter(new Date(now.getTime() - 1000)).list()).hasSize(2); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .includeEnded() + .or().caseInstanceId("undefinedId").planItemInstanceExitAfter(new Date(now.getTime() - 1000)).endOr() + .list()).hasSize(6); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .includeEnded() + .or().caseInstanceId("undefinedId").planItemInstanceExitAfter(new Date(now.getTime() + 1000)).endOr() + .list()).hasSize(3); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .includeEnded() + .or().caseInstanceId("undefinedId").planItemInstanceExitAfter(new Date(now.getTime() + 20000)).endOr() + .list()) + .isEmpty(); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .includeEnded() + .or().caseInstanceId("undefinedId").planItemInstanceExitBefore(new Date(now.getTime() - 1000)).endOr() + .list()) + .isEmpty(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .includeEnded() + .or().caseInstanceId("undefinedId").planItemInstanceExitBefore(new Date(now.getTime() + 1000)).endOr() + .list()) + .hasSize(3); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .includeEnded() + .or().caseInstanceId("undefinedId").planItemInstanceExitBefore(new Date(now.getTime() + 20000)).endOr() + .list()) + .hasSize(6); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .includeEnded() + .or().caseInstanceId("undefinedId").planItemInstanceEndedAfter(new Date(now.getTime() - 1000)).endOr() + .list()) + .hasSize(8); // + 2 for user event listener + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .includeEnded() + .or().caseInstanceId("undefinedId").planItemInstanceEndedAfter(new Date(now.getTime() + 1000)).endOr() + .list()) + .hasSize(4); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .includeEnded() + .or().caseInstanceId("undefinedId").planItemInstanceEndedAfter(new Date(now.getTime() + 20000)).endOr() + .list()) + .isEmpty(); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .includeEnded() + .or().caseInstanceId("undefinedId").planItemInstanceEndedBefore(new Date(now.getTime() - 1000)).endOr() + .list()) + .isEmpty(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .includeEnded() + .or().caseInstanceId("undefinedId").planItemInstanceEndedBefore(new Date(now.getTime() + 1000)).endOr() + .list()) + .hasSize(4); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .includeEnded() + .or().caseInstanceId("undefinedId").planItemInstanceEndedBefore(new Date(now.getTime() + 20000)).endOr() + .list()) + .hasSize(8); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .includeEnded() + .or().caseInstanceId("undefinedId").planItemDefinitionType(PlanItemDefinitionType.USER_EVENT_LISTENER).endOr() + .or().caseInstanceId("undefinedId").planItemInstanceEndedAfter(new Date(now.getTime() - 1000)).endOr() + .list()).hasSize(2); + } @Test @@ -606,6 +1083,52 @@ public void testTerminateBeforeAndAfter() { .isEmpty(); assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().includeEnded().planItemInstanceEndedAfter(new Date(now.getTime() - 1000)).list()) .hasSize(2); + + // Terminated + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .includeEnded() + .or().caseInstanceId("undefinedId").planItemInstanceTerminatedBefore(new Date(now.getTime() + 1000)).endOr() + .list()) + .hasSize(2); // 2 -> stage and C + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .includeEnded() + .or().caseInstanceId("undefinedId").planItemInstanceTerminatedBefore(new Date(now.getTime() - 1000)).endOr() + .list()) + .isEmpty(); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .includeEnded() + .or().caseInstanceId("undefinedId").planItemInstanceTerminatedAfter(new Date(now.getTime() + 1000)).endOr() + .list()) + .isEmpty(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .includeEnded() + .or().caseInstanceId("undefinedId").planItemInstanceTerminatedAfter(new Date(now.getTime() - 1000)).endOr() + .list()) + .hasSize(2); + + // Ended + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .includeEnded() + .or().caseInstanceId("undefinedId").planItemInstanceEndedBefore(new Date(now.getTime() + 1000)).endOr() + .list()) + .hasSize(2); // 2 -> stage and C + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .includeEnded() + .or().caseInstanceId("undefinedId").planItemInstanceEndedBefore(new Date(now.getTime() - 1000)).endOr() + .list()) + .isEmpty(); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .includeEnded() + .or().caseInstanceId("undefinedId").planItemInstanceEndedAfter(new Date(now.getTime() + 1000)).endOr() + .list()) + .isEmpty(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .includeEnded() + .or().caseInstanceId("undefinedId").planItemInstanceEndedAfter(new Date(now.getTime() - 1000)).endOr() + .list()) + .hasSize(2); } @Test @@ -681,6 +1204,7 @@ public void localize(HistoricPlanItemInstance historicPlanItemInstance, String l ); } + @Test public void testQueryVariableValueEqualsAndNotEquals() { CaseInstance caseWithStringValue = cmmnRuntimeService.createCaseInstanceBuilder() .caseDefinitionKey("testPlanItemInstanceQuery") @@ -712,6 +1236,13 @@ public void testQueryVariableValueEqualsAndNotEquals() { assertThat(planItemWithStringValue).isNotNull(); + planItemWithStringValue = cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseDefinitionId("undefinedId").caseInstanceId(caseWithStringValue.getId()).endOr() + .or().caseDefinitionId("undefinedId").planItemInstanceName("Stage one").endOr() + .singleResult(); + + assertThat(planItemWithStringValue).isNotNull(); + PlanItemInstance planItemWithNullValue = cmmnRuntimeService.createPlanItemInstanceQuery() .caseInstanceId(caseWithNullValue.getId()) .planItemInstanceName("Stage one") @@ -719,6 +1250,13 @@ public void testQueryVariableValueEqualsAndNotEquals() { assertThat(planItemWithNullValue).isNotNull(); + planItemWithNullValue = cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseDefinitionId("undefinedId").caseInstanceId(caseWithNullValue.getId()).endOr() + .or().caseDefinitionId("undefinedId").planItemInstanceName("Stage one").endOr() + .singleResult(); + + assertThat(planItemWithNullValue).isNotNull(); + PlanItemInstance planItemWithLongValue = cmmnRuntimeService.createPlanItemInstanceQuery() .caseInstanceId(caseWithLongValue.getId()) .planItemInstanceName("Stage one") @@ -726,6 +1264,13 @@ public void testQueryVariableValueEqualsAndNotEquals() { assertThat(planItemWithLongValue).isNotNull(); + planItemWithLongValue = cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseDefinitionId("undefinedId").caseInstanceId(caseWithLongValue.getId()).endOr() + .or().caseDefinitionId("undefinedId").planItemInstanceName("Stage one").endOr() + .singleResult(); + + assertThat(planItemWithLongValue).isNotNull(); + PlanItemInstance planItemWithDoubleValue = cmmnRuntimeService.createPlanItemInstanceQuery() .caseInstanceId(caseWithDoubleValue.getId()) .planItemInstanceName("Stage one") @@ -733,6 +1278,13 @@ public void testQueryVariableValueEqualsAndNotEquals() { assertThat(planItemWithDoubleValue).isNotNull(); + planItemWithDoubleValue = cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseDefinitionId("undefinedId").caseInstanceId(caseWithDoubleValue.getId()).endOr() + .or().caseDefinitionId("undefinedId").planItemInstanceName("Stage one").endOr() + .singleResult(); + + assertThat(planItemWithDoubleValue).isNotNull(); + assertThat(cmmnRuntimeService.hasLocalVariable(planItemWithStringValue.getId(), "var")).isFalse(); cmmnRuntimeService.setLocalVariable(planItemWithStringValue.getId(), "var", "TEST"); assertThat(cmmnRuntimeService.hasLocalVariable(planItemWithStringValue.getId(), "var")).isTrue(); @@ -754,6 +1306,12 @@ public void testQueryVariableValueEqualsAndNotEquals() { tuple("Stage one", caseWithStringValue.getId()) ); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().variableValueEquals("TEST").list()) + .extracting(PlanItemInstance::getName, PlanItemInstance::getCaseInstanceId) + .containsExactlyInAnyOrder( + tuple("Stage one", caseWithStringValue.getId()) + ); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().variableValueNotEquals("var", 100L).list()) .extracting(PlanItemInstance::getName, PlanItemInstance::getCaseInstanceId) .containsExactlyInAnyOrder( @@ -808,6 +1366,183 @@ public void testQueryVariableValueEqualsAndNotEquals() { .containsExactlyInAnyOrder( tuple("Stage one", caseWithStringValue.getId()) ); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueNotEquals("var", "TEST").endOr() + .list()) + .extracting(PlanItemInstance::getName, PlanItemInstance::getCaseInstanceId) + .containsExactlyInAnyOrder( + tuple("Stage one", caseWithNullValue.getId()), + tuple("Stage one", caseWithLongValue.getId()), + tuple("Stage one", caseWithDoubleValue.getId()) + ); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueEquals("var", "TEST").endOr() + .list()) + .extracting(PlanItemInstance::getName, PlanItemInstance::getCaseInstanceId) + .containsExactlyInAnyOrder( + tuple("Stage one", caseWithStringValue.getId()) + ); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueEquals("TEST").endOr() + .list()) + .extracting(PlanItemInstance::getName, PlanItemInstance::getCaseInstanceId) + .containsExactlyInAnyOrder( + tuple("Stage one", caseWithStringValue.getId()) + ); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueNotEquals("var", 100L).endOr() + .list()) + .extracting(PlanItemInstance::getName, PlanItemInstance::getCaseInstanceId) + .containsExactlyInAnyOrder( + tuple("Stage one", caseWithStringValue.getId()), + tuple("Stage one", caseWithNullValue.getId()), + tuple("Stage one", caseWithDoubleValue.getId()) + ); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueEquals("var", 100L).endOr() + .list()) + .extracting(PlanItemInstance::getName, PlanItemInstance::getCaseInstanceId) + .containsExactlyInAnyOrder( + tuple("Stage one", caseWithLongValue.getId()) + ); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueNotEquals("var", 45.55).endOr() + .list()) + .extracting(PlanItemInstance::getName, PlanItemInstance::getCaseInstanceId) + .containsExactlyInAnyOrder( + tuple("Stage one", caseWithStringValue.getId()), + tuple("Stage one", caseWithNullValue.getId()), + tuple("Stage one", caseWithLongValue.getId()) + ); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueEquals("var", 45.55).endOr() + .list()) + .extracting(PlanItemInstance::getName, PlanItemInstance::getCaseInstanceId) + .containsExactlyInAnyOrder( + tuple("Stage one", caseWithDoubleValue.getId()) + ); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueNotEquals("var", "test").endOr() + .list()) + .extracting(PlanItemInstance::getName, PlanItemInstance::getCaseInstanceId) + .containsExactlyInAnyOrder( + tuple("Stage one", caseWithStringValue.getId()), + tuple("Stage one", caseWithNullValue.getId()), + tuple("Stage one", caseWithLongValue.getId()), + tuple("Stage one", caseWithDoubleValue.getId()) + ); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueNotEqualsIgnoreCase("var", "test").endOr() + .list()) + .extracting(PlanItemInstance::getName, PlanItemInstance::getCaseInstanceId) + .containsExactlyInAnyOrder( + tuple("Stage one", caseWithNullValue.getId()), + tuple("Stage one", caseWithLongValue.getId()), + tuple("Stage one", caseWithDoubleValue.getId()) + ); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueEquals("var", "test").endOr() + .list()) + .extracting(PlanItemInstance::getName, PlanItemInstance::getCaseInstanceId) + .isEmpty(); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueEqualsIgnoreCase("var", "test").endOr() + .list()) + .extracting(PlanItemInstance::getName, PlanItemInstance::getCaseInstanceId) + .containsExactlyInAnyOrder( + tuple("Stage one", caseWithStringValue.getId()) + ); + + } + + @Test + @CmmnDeployment + public void testOrQueryByCompletable() { + CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder() + .caseDefinitionKey("testNonAutoCompleteStageManualCompleteable") + .variable("required", true) + .start(); + + final PlanItemInstance stagePlanItemInstance1 = cmmnRuntimeService.createPlanItemInstanceQuery().planItemDefinitionType(PlanItemDefinitionType.STAGE) + .singleResult(); + // Completing the one task should mark the stage as completeable + Task task = cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).singleResult(); + assertThat(task.getName()).isEqualTo("Required task"); + cmmnTaskService.complete(task.getId()); + + PlanItemInstance stagePlanItemInstance2 = cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceId(stagePlanItemInstance1.getId()) + .singleResult(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceCompletable().singleResult()).isNotNull(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceCompletable().endOr().singleResult()).isNotNull(); + + cmmnRuntimeService.completeStagePlanItemInstance(stagePlanItemInstance2.getId()); + + } + + @Test + @CmmnDeployment + public void testQueryByDerivedCaseDefinitionId() { + CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("dynamicPlanItemInjection").start(); + + List planItemInstances = getPlanItemInstances(caseInstance.getId()); + // inject new plan item into Stage A + PlanItemInstance injectedTask = dynamicCmmnService + .createInjectedPlanItemInstanceBuilder() + .name("Injected Task A") + .caseDefinitionId(caseInstance.getCaseDefinitionId()) + .elementId(getPlanItemInstanceByName(planItemInstances, "Task A", ACTIVE).getElementId()) + .createInStage(getPlanItemInstanceIdByNameAndState(planItemInstances, "Stage A", ACTIVE)); + + // test the query for the derived case definition (in this unit test, it will be the same as the running one) + List derivedPlanItems = cmmnRuntimeService.createPlanItemInstanceQuery() + .derivedCaseDefinitionId(caseInstance.getCaseDefinitionId()) + .list(); + + assertThat(derivedPlanItems).isNotNull(); + assertThat(derivedPlanItems) + .extracting(PlanItemInstance::getName) + .containsExactly("Injected Task A"); + + derivedPlanItems = cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").derivedCaseDefinitionId(caseInstance.getCaseDefinitionId()).endOr() + .list(); + + assertThat(derivedPlanItems).isNotNull(); + assertThat(derivedPlanItems) + .extracting(PlanItemInstance::getName) + .containsExactly("Injected Task A"); + + } + + @Test + @CmmnDeployment + public void testOrQueryByStages() { + CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("myCase").start(); + + List activeStages = cmmnRuntimeService.createPlanItemInstanceQuery().caseInstanceId(caseInstance.getId()).onlyStages() + .planItemInstanceStateActive().list(); + assertThat(activeStages) + .extracting(PlanItemInstance::getPlanItemDefinitionId) + .containsExactly("expandedStage2"); + + activeStages = cmmnRuntimeService.createPlanItemInstanceQuery().caseInstanceId(caseInstance.getId()) + .or().caseInstanceId("undefinedId").onlyStages().endOr() + .planItemInstanceStateActive().list(); + assertThat(activeStages) + .extracting(PlanItemInstance::getPlanItemDefinitionId) + .containsExactly("expandedStage2"); } private List startInstances(int numberOfInstances) { diff --git a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/RuntimeServiceTest.java b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/RuntimeServiceTest.java index cefbf20ee5c..729abdac760 100644 --- a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/RuntimeServiceTest.java +++ b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/runtime/RuntimeServiceTest.java @@ -435,6 +435,94 @@ public void testPlanItemVariableQueryWithBlockingTask() { assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().caseVariableValueLessThan("numberVar", 5).count()).isZero(); assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().caseVariableValueLessThanOrEqual("numberVar", 10).count()).isEqualTo(4); assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().caseVariableValueLessThanOrEqual("numberVar", 9).count()).isZero(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().caseVariableValueEquals(9).count()).isZero(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().caseVariableValueEquals(10).count()).isEqualTo(4); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().caseVariableExists("numberVar").count()).isEqualTo(4); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().caseVariableExists("notExisting").count()).isZero(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().caseVariableNotExists("numberVar").count()).isZero(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().caseVariableNotExists("notExisting").count()).isEqualTo(4); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefined").caseVariableValueEquals("var", "test").endOr() + .count()).isEqualTo(4); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefined").caseVariableValueEquals("var", "test2").endOr() + .count()).isZero(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefined").caseVariableValueEqualsIgnoreCase("var", "TEST").endOr() + .count()).isEqualTo(4); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefined").caseVariableValueEqualsIgnoreCase("var", "TEST2").endOr() + .count()).isZero(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefined").caseVariableValueNotEquals("var", "test2").endOr() + .count()).isEqualTo(4); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefined").caseVariableValueNotEquals("var", "test").endOr() + .count()).isZero(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefined").caseVariableValueNotEqualsIgnoreCase("var", "TEST2").endOr() + .count()).isEqualTo(4); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefined").caseVariableValueNotEqualsIgnoreCase("var", "TEST").endOr() + .count()).isZero(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefined").caseVariableValueLike("var", "te%").endOr() + .count()).isEqualTo(4); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefined").caseVariableValueLike("var", "te2%").endOr() + .count()).isZero(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefined").caseVariableValueLikeIgnoreCase("var", "TE%").endOr() + .count()).isEqualTo(4); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefined").caseVariableValueLikeIgnoreCase("var", "TE2%").endOr() + .count()).isZero(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefined").caseVariableValueGreaterThan("numberVar", 5).endOr() + .count()).isEqualTo(4); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefined").caseVariableValueGreaterThan("numberVar", 11).endOr() + .count()).isZero(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefined").caseVariableValueGreaterThanOrEqual("numberVar", 10).endOr() + .count()).isEqualTo(4); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefined").caseVariableValueGreaterThanOrEqual("numberVar", 11).endOr() + .count()).isZero(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefined").caseVariableValueLessThan("numberVar", 20).endOr() + .count()).isEqualTo(4); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefined").caseVariableValueLessThan("numberVar", 5).endOr() + .count()).isZero(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefined").caseVariableValueLessThanOrEqual("numberVar", 10).endOr() + .count()).isEqualTo(4); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefined").caseVariableValueLessThanOrEqual("numberVar", 9).endOr() + .count()).isZero(); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").caseVariableValueEquals(9).endOr() + .count()).isZero(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").caseVariableValueEquals(10).endOr() + .count()).isEqualTo(4); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").caseVariableExists("numberVar").endOr() + .count()).isEqualTo(4); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").caseVariableExists("notExisting").endOr() + .count()).isZero(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").caseVariableNotExists("numberVar").endOr() + .count()).isZero(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").caseVariableNotExists("notExisting").endOr() + .count()).isEqualTo(4); PlanItemInstance planItemInstance = cmmnRuntimeService.createPlanItemInstanceQuery() .caseInstanceId(caseInstance.getId()) @@ -459,6 +547,8 @@ public void testPlanItemVariableQueryWithBlockingTask() { assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().caseVariableValueEquals("localVar", "test").count()).isZero(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().variableExists("localNumberVar").count()).isEqualTo(1); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().variableNotExists("localNumberVar").count()).isEqualTo(3); assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().variableValueEquals("localVar", "test").count()).isEqualTo(1); assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().variableValueEquals("localVar", "test2").count()).isZero(); assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().variableValueEqualsIgnoreCase("localVar", "TEST").count()).isEqualTo(1); @@ -473,6 +563,7 @@ public void testPlanItemVariableQueryWithBlockingTask() { assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().variableValueLikeIgnoreCase("localVar", "TE2%").count()).isZero(); assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().variableValueGreaterThan("localNumberVar", 5).count()).isEqualTo(1); assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().variableValueGreaterThan("localNumberVar", 17).count()).isZero(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().variableValueGreaterThan("localNumberVar", 17).count()).isZero(); assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().variableValueGreaterThanOrEqual("localNumberVar", 15).count()).isEqualTo(1); assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().variableValueGreaterThanOrEqual("localNumberVar", 16).count()).isZero(); assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().variableValueLessThan("localNumberVar", 20).count()).isEqualTo(1); @@ -480,6 +571,91 @@ public void testPlanItemVariableQueryWithBlockingTask() { assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().variableValueLessThanOrEqual("localNumberVar", 15).count()).isEqualTo(1); assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().variableValueLessThanOrEqual("localNumberVar", 9).count()).isZero(); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableExists("localNumberVar").endOr() + .count()).isEqualTo(1); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableNotExists("localNumberVar").endOr() + .count()).isEqualTo(3); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").caseVariableValueGreaterThan("numberVar", 10).endOr() + .count()).isEqualTo(4); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").caseVariableValueGreaterThan("numberVar", 11).endOr() + .count()).isZero(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").caseVariableValueGreaterThanOrEqual("numberVar", 11).endOr() + .count()).isEqualTo(4); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").caseVariableValueGreaterThanOrEqual("numberVar", 12).endOr() + .count()).isZero(); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").caseVariableValueEquals("localVar", "test").endOr() + .count()).isZero(); + + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueEquals("localVar", "test").endOr() + .count()).isEqualTo(1); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueEquals("localVar", "test2").endOr() + .count()).isZero(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueEqualsIgnoreCase("localVar", "TEST").endOr() + .count()).isEqualTo(1); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueEqualsIgnoreCase("localVar", "TEST2").endOr() + .count()).isZero(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueNotEquals("localVar", "test2").endOr() + .count()).isEqualTo(1); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueNotEquals("localVar", "test").endOr() + .count()).isZero(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueNotEqualsIgnoreCase("localVar", "TEST2").endOr() + .count()).isEqualTo(1); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueNotEqualsIgnoreCase("localVar", "TEST").endOr() + .count()).isZero(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueLike("localVar", "te%").endOr() + .count()).isEqualTo(1); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueLike("localVar", "te2%").endOr() + .count()).isZero(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueLikeIgnoreCase("localVar", "TE%").endOr() + .count()).isEqualTo(1); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueLikeIgnoreCase("localVar", "TE2%").endOr() + .count()).isZero(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueGreaterThan("localNumberVar", 5).endOr() + .count()).isEqualTo(1); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueGreaterThan("localNumberVar", 17).endOr() + .count()).isZero(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueGreaterThanOrEqual("localNumberVar", 15).endOr() + .count()).isEqualTo(1); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueGreaterThanOrEqual("localNumberVar", 16).endOr() + .count()).isZero(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueLessThan("localNumberVar", 20).endOr() + .count()).isEqualTo(1); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueLessThan("localNumberVar", 5).endOr() + .count()).isZero(); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueLessThanOrEqual("localNumberVar", 15).endOr() + .count()).isEqualTo(1); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").variableValueLessThanOrEqual("localNumberVar", 9).endOr() + .count()).isZero(); cmmnRuntimeService.triggerPlanItemInstance(planItemInstance.getId()); planItemInstance = cmmnRuntimeService.createPlanItemInstanceQuery().caseInstanceId(caseInstance.getId()) .planItemInstanceState(PlanItemInstanceState.ACTIVE).singleResult(); @@ -1176,6 +1352,14 @@ public void planItemQueryWithoutTenant() { assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceName("Task A").planItemInstanceWithoutTenantId().count()).isEqualTo(1); assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceName("Task A").planItemInstanceWithoutTenantId().list()).hasSize(1); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceName("Task A").endOr() + .or().caseInstanceId("undefinedId").planItemInstanceWithoutTenantId().endOr() + .count()).isEqualTo(1); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceName("Task A").endOr() + .or().caseInstanceId("undefinedId").planItemInstanceWithoutTenantId().endOr() + .list()).hasSize(1); } @Test diff --git a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/tenant/MultiTenantTest.java b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/tenant/MultiTenantTest.java index 7e51a9b80df..f446a5e8eb1 100644 --- a/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/tenant/MultiTenantTest.java +++ b/modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/tenant/MultiTenantTest.java @@ -63,6 +63,10 @@ public void testCaseInstances() { assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceWithoutTenantId().count()).isEqualTo(3); assertThat(cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceTenantId("test-tenant").count()).isEqualTo(5); + assertThat(cmmnRuntimeService.createPlanItemInstanceQuery() + .or().caseInstanceId("undefinedId").planItemInstanceTenantId("test-tenant").endOr() + .count()).isEqualTo(5); + } } diff --git a/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/listener/PlanItemInstanceLifecycleListenerTest.testTerminateCaseInstance.cmmn b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/listener/PlanItemInstanceLifecycleListenerTest.testTerminateCaseInstance.cmmn new file mode 100644 index 00000000000..83af848d023 --- /dev/null +++ b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/listener/PlanItemInstanceLifecycleListenerTest.testTerminateCaseInstance.cmmn @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/one-task-new-properties.cmmn.xml b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/one-task-new-properties.cmmn.xml new file mode 100644 index 00000000000..b7d673c8465 --- /dev/null +++ b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/one-task-new-properties.cmmn.xml @@ -0,0 +1,13 @@ + + + + + + + Example description + + + + diff --git a/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/two-task-new-properties.cmmn.xml b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/two-task-new-properties.cmmn.xml new file mode 100644 index 00000000000..a038a924fd3 --- /dev/null +++ b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/two-task-new-properties.cmmn.xml @@ -0,0 +1,18 @@ + + + + + + + + Example description + + + Example description2 + + + + diff --git a/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/runtime/HumanTaskTest.testTaskNonBlockingWithEntryAndExitCriterion.cmmn b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/runtime/HumanTaskTest.testTaskNonBlockingWithEntryAndExitCriterion.cmmn new file mode 100644 index 00000000000..06de96a250c --- /dev/null +++ b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/runtime/HumanTaskTest.testTaskNonBlockingWithEntryAndExitCriterion.cmmn @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + ${false} + + + + + + + \ No newline at end of file diff --git a/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/runtime/PlanItemInstanceQueryTest.testOrQueryByCompletable.cmmn b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/runtime/PlanItemInstanceQueryTest.testOrQueryByCompletable.cmmn new file mode 100644 index 00000000000..3920f95258f --- /dev/null +++ b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/runtime/PlanItemInstanceQueryTest.testOrQueryByCompletable.cmmn @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/runtime/PlanItemInstanceQueryTest.testOrQueryByStages.cmmn b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/runtime/PlanItemInstanceQueryTest.testOrQueryByStages.cmmn new file mode 100644 index 00000000000..d94ec557b29 --- /dev/null +++ b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/runtime/PlanItemInstanceQueryTest.testOrQueryByStages.cmmn @@ -0,0 +1,332 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + complete + + + + + + + + complete + + + + + + + + complete + + + + + + + + complete + + + + + + + + complete + + + + + + + + complete + + + + + + + + complete + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + complete + + + + + + + + complete + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/runtime/PlanItemInstanceQueryTest.testQueryByDerivedCaseDefinitionId.cmmn b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/runtime/PlanItemInstanceQueryTest.testQueryByDerivedCaseDefinitionId.cmmn new file mode 100644 index 00000000000..c095a8aa4dc --- /dev/null +++ b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/runtime/PlanItemInstanceQueryTest.testQueryByDerivedCaseDefinitionId.cmmn @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + complete + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/flowable-cmmn-rest/src/main/java/org/flowable/cmmn/rest/service/api/CmmnRestResponseFactory.java b/modules/flowable-cmmn-rest/src/main/java/org/flowable/cmmn/rest/service/api/CmmnRestResponseFactory.java index 6693757a594..64b91fa3b17 100644 --- a/modules/flowable-cmmn-rest/src/main/java/org/flowable/cmmn/rest/service/api/CmmnRestResponseFactory.java +++ b/modules/flowable-cmmn-rest/src/main/java/org/flowable/cmmn/rest/service/api/CmmnRestResponseFactory.java @@ -68,6 +68,7 @@ import org.flowable.common.rest.variable.RestVariableConverter; import org.flowable.common.rest.variable.ShortRestVariableConverter; import org.flowable.common.rest.variable.StringRestVariableConverter; +import org.flowable.common.rest.variable.UUIDRestVariableConverter; import org.flowable.dmn.api.DmnDecision; import org.flowable.eventsubscription.api.EventSubscription; import org.flowable.form.api.FormDefinition; @@ -1093,6 +1094,7 @@ protected void initializeVariableConverters() { variableConverters.add(new InstantRestVariableConverter()); variableConverters.add(new LocalDateRestVariableConverter()); variableConverters.add(new LocalDateTimeRestVariableConverter()); + variableConverters.add(new UUIDRestVariableConverter()); variableConverters.add(new JsonObjectRestVariableConverter(objectMapper)); } diff --git a/modules/flowable-cmmn-rest/src/main/java/org/flowable/cmmn/rest/service/api/runtime/caze/CaseInstanceVariableCollectionResource.java b/modules/flowable-cmmn-rest/src/main/java/org/flowable/cmmn/rest/service/api/runtime/caze/CaseInstanceVariableCollectionResource.java index d1ec8456eef..ef1be9213e6 100644 --- a/modules/flowable-cmmn-rest/src/main/java/org/flowable/cmmn/rest/service/api/runtime/caze/CaseInstanceVariableCollectionResource.java +++ b/modules/flowable-cmmn-rest/src/main/java/org/flowable/cmmn/rest/service/api/runtime/caze/CaseInstanceVariableCollectionResource.java @@ -46,7 +46,7 @@ public class CaseInstanceVariableCollectionResource extends BaseVariableResource { @ApiOperation(value = "List variables for a case instance", nickname="listCaseInstanceVariables", tags = {"Case Instance Variables" }, - notes = "In case the variable is a binary variable or serializable, the valueUrl points to an URL to fetch the raw value. If it’s a plain variable, the value is present in the response. Note that only local scoped variables are returned, as there is no global scope for process-instance variables.") + notes = "In case the variable is a binary variable or serializable, the valueUrl points to an URL to fetch the raw value. If it’s a plain variable, the value is present in the response. Note that only local scoped variables are returned, as there is no global scope for case-instance variables.") @ApiResponses(value = { @ApiResponse(code = 200, message = "Indicates the case instance was found and variables are returned."), @ApiResponse(code = 400, message = "Indicates the requested case instance was not found.") @@ -60,12 +60,12 @@ public List getVariables(@ApiParam(name = "caseInstanceId") @PathV @ApiOperation(value = "Update a multiple/single (non)binary variable on a case instance", tags = { "Case Instance Variables" }, nickname = "createOrUpdateCaseVariable", notes = "This endpoint can be used in 2 ways: By passing a JSON Body (RestVariable or an array of RestVariable) or by passing a multipart/form-data Object.\n" - + "Nonexistent variables are created on the process-instance and existing ones are overridden without any error.\n" + + "Nonexistent variables are created on the case-instance and existing ones are overridden without any error.\n" + "Any number of variables can be passed into the request body array.\n" - + "Note that scope is ignored, only local variables can be set in a case instance.\n" + + "Note that scope is ignored, only global variables can be set in a case instance.\n" + "NB: Swagger V2 specification doesn't support this use case that is why this endpoint might be buggy/incomplete if used with other tools.") @ApiImplicitParams({ - @ApiImplicitParam(name = "body", type = "org.flowable.rest.cmmn.service.api.engine.variable.RestVariable", value = "Create a variable on a process instance", paramType = "body", example = "{\n" + + @ApiImplicitParam(name = "body", type = "org.flowable.rest.cmmn.service.api.engine.variable.RestVariable", value = "Create a variable on a case instance", paramType = "body", example = "{\n" + " \"name\":\"intProcVar\"\n" + " \"type\":\"integer\"\n" + " \"value\":123,\n" + @@ -90,9 +90,9 @@ public Object createOrUpdateExecutionVariable(@ApiParam(name = "caseInstanceId") @ApiOperation(value = "Create variables or new binary variable on a case instance", tags = { "Case Instance Variables" }, nickname = "createCaseInstanceVariable", notes = "This endpoint can be used in 2 ways: By passing a JSON Body (RestVariable or an array of RestVariable) or by passing a multipart/form-data Object.\n" - + "Nonexistent variables are created on the process-instance and existing ones are overridden without any error.\n" + + "Nonexistent variables are created on the case-instance and existing ones are overridden without any error.\n" + "Any number of variables can be passed into the request body array.\n" - + "Note that scope is ignored, only local variables can be set in a case instance.\n" + + "Note that scope is ignored, only global variables can be set in a case instance.\n" + "NB: Swagger V2 specification doesn't support this use case that is why this endpoint might be buggy/incomplete if used with other tools.") @ApiImplicitParams({ @ApiImplicitParam(name = "body", type = "org.flowable.rest.cmmn.service.api.engine.variable.RestVariable", value = "Create a variable on a case instance", paramType = "body", example = "{\n" + diff --git a/modules/flowable-cmmn-rest/src/main/java/org/flowable/cmmn/rest/service/api/runtime/caze/CaseInstanceVariableResource.java b/modules/flowable-cmmn-rest/src/main/java/org/flowable/cmmn/rest/service/api/runtime/caze/CaseInstanceVariableResource.java index 025753c98f1..7afc41f2f50 100644 --- a/modules/flowable-cmmn-rest/src/main/java/org/flowable/cmmn/rest/service/api/runtime/caze/CaseInstanceVariableResource.java +++ b/modules/flowable-cmmn-rest/src/main/java/org/flowable/cmmn/rest/service/api/runtime/caze/CaseInstanceVariableResource.java @@ -71,7 +71,7 @@ public RestVariable getVariable(@ApiParam(name = "caseInstanceId") @PathVariable @ApiOperation(value = "Update a single variable on a case instance", tags = { "Case Instance Variables" }, nickname = "updateCaseInstanceVariable", notes = "This endpoint can be used in 2 ways: By passing a JSON Body (RestVariable) or by passing a multipart/form-data Object.\n" + "Nonexistent variables are created on the case instance and existing ones are overridden without any error.\n" - + "Note that scope is ignored, only local variables can be set in a process instance.\n" + + "Note that scope is ignored, only global variables can be set in a case instance.\n" + "NB: Swagger V2 specification doesn't support this use case that is why this endpoint might be buggy/incomplete if used with other tools.") @ApiImplicitParams({ @ApiImplicitParam(name = "body", type = "org.flowable.rest.cmmn.service.api.engine.variable.RestVariable", value = "Create a variable on a case instance", paramType = "body", example = "{\n" + @@ -85,7 +85,7 @@ public RestVariable getVariable(@ApiParam(name = "caseInstanceId") @PathVariable }) @ApiResponses(value = { @ApiResponse(code = 201, message = "Indicates both the case instance and variable were found and variable is updated."), - @ApiResponse(code = 404, message = "Indicates the requested case instance was not found or the process instance does not have a variable with the given name. Status description contains additional information about the error.") + @ApiResponse(code = 404, message = "Indicates the requested case instance was not found or the case instance does not have a variable with the given name. Status description contains additional information about the error.") }) @PutMapping(value = "/cmmn-runtime/case-instances/{caseInstanceId}/variables/{variableName}", produces = "application/json", consumes = {"application/json", "multipart/form-data"}) public RestVariable updateVariable(@ApiParam(name = "caseInstanceId") @PathVariable("caseInstanceId") String caseInstanceId, @ApiParam(name = "variableName") @PathVariable("variableName") String variableName, diff --git a/modules/flowable-cmmn-rest/src/test/java/org/flowable/cmmn/rest/service/api/runtime/CaseInstanceQueryResourceTest.java b/modules/flowable-cmmn-rest/src/test/java/org/flowable/cmmn/rest/service/api/runtime/CaseInstanceQueryResourceTest.java index f043cadd194..f7ecd848640 100644 --- a/modules/flowable-cmmn-rest/src/test/java/org/flowable/cmmn/rest/service/api/runtime/CaseInstanceQueryResourceTest.java +++ b/modules/flowable-cmmn-rest/src/test/java/org/flowable/cmmn/rest/service/api/runtime/CaseInstanceQueryResourceTest.java @@ -207,6 +207,8 @@ public void testQueryCaseInstancesPagingAndSorting() throws Exception { Authentication.setAuthenticatedUserId("queryCaseUser"); CaseInstance caseInstance1 = runtimeService.createCaseInstanceBuilder().caseDefinitionKey("oneHumanTaskCase").start(); CaseInstance caseInstance2 = runtimeService.createCaseInstanceBuilder().caseDefinitionKey("testRepeatingStage").start(); + caseInstance1 = runtimeService.createCaseInstanceQuery().caseInstanceId(caseInstance1.getId()).singleResult(); + caseInstance2 = runtimeService.createCaseInstanceQuery().caseInstanceId(caseInstance2.getId()).singleResult(); // Create request node ObjectNode requestNode = objectMapper.createObjectNode(); diff --git a/modules/flowable-cmmn-rest/src/test/java/org/flowable/cmmn/rest/service/api/runtime/CaseInstanceVariableResourceTest.java b/modules/flowable-cmmn-rest/src/test/java/org/flowable/cmmn/rest/service/api/runtime/CaseInstanceVariableResourceTest.java index 5c2c2f806cb..0eac3d757e7 100644 --- a/modules/flowable-cmmn-rest/src/test/java/org/flowable/cmmn/rest/service/api/runtime/CaseInstanceVariableResourceTest.java +++ b/modules/flowable-cmmn-rest/src/test/java/org/flowable/cmmn/rest/service/api/runtime/CaseInstanceVariableResourceTest.java @@ -27,6 +27,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.UUID; import org.apache.commons.io.IOUtils; import org.apache.http.HttpStatus; @@ -162,6 +163,30 @@ public void testGetCaseInstanceLocalDateTimeVariable() throws Exception { + "}"); } + @CmmnDeployment(resources = { "org/flowable/cmmn/rest/service/api/repository/oneHumanTaskCase.cmmn" }) + public void testGetCaseInstanceUUIDVariable() throws Exception { + + CaseInstance caseInstance = runtimeService.createCaseInstanceBuilder().caseDefinitionKey("oneHumanTaskCase").start(); + UUID someUUID = UUID.fromString("26da81fb-18a6-4c19-b7b4-6877a568bfe1"); + runtimeService.setVariable(caseInstance.getId(), "variable", someUUID); + + CloseableHttpResponse response = executeRequest( + new HttpGet(SERVER_URL_PREFIX + CmmnRestUrls.createRelativeResourceUrl(CmmnRestUrls.URL_CASE_INSTANCE_VARIABLE, + caseInstance.getId(), "variable")), HttpStatus.SC_OK); + + JsonNode responseNode = objectMapper.readTree(response.getEntity().getContent()); + + closeResponse(response); + assertThat(responseNode).isNotNull(); + assertThatJson(responseNode) + .when(Option.IGNORING_EXTRA_FIELDS) + .isEqualTo("{" + + " name: 'variable'," + + " type: 'uuid'," + + " value: '" + someUUID + "'" + + "}"); + } + /** * Test getting a case instance variable data. GET cmmn-runtime/case-instances/{caseInstanceId}/variables/{variableName} */ @@ -427,4 +452,37 @@ public void testUpdateBinaryCaseVariable() throws Exception { assertThat(variableValue).isInstanceOf(byte[].class); assertThat(new String((byte[]) variableValue)).isEqualTo("This is binary content"); } + + @CmmnDeployment(resources = { "org/flowable/cmmn/rest/service/api/repository/oneHumanTaskCase.cmmn" }) + public void testUpdateUUIDCaseVariable() throws Exception { + + UUID someUUID = UUID.fromString("87b859b2-d0c7-4845-93c2-e96ef69115b5"); + UUID someUUID2 = UUID.fromString("b2233abf-f84f-426f-b978-0d249b90cc45"); + CaseInstance caseInstance = runtimeService.createCaseInstanceBuilder().caseDefinitionKey("oneHumanTaskCase") + .variables(Collections.singletonMap("overlappingVariable", (Object) "caseValue")).start(); + runtimeService.setVariable(caseInstance.getId(), "uuidVariable", someUUID); + + // Update variable + ObjectNode requestNode = objectMapper.createObjectNode(); + requestNode.put("name", "uuidVariable"); + requestNode.put("value", someUUID2.toString()); + requestNode.put("type", "uuid"); + + HttpPut httpPut = new HttpPut( + SERVER_URL_PREFIX + CmmnRestUrls.createRelativeResourceUrl(CmmnRestUrls.URL_CASE_INSTANCE_VARIABLE, caseInstance.getId(), "uuidVariable")); + httpPut.setEntity(new StringEntity(requestNode.toString())); + CloseableHttpResponse response = executeRequest(httpPut, HttpStatus.SC_OK); + + assertThat(runtimeService.getVariable(caseInstance.getId(), "uuidVariable")).isEqualTo(someUUID2); + + JsonNode responseNode = objectMapper.readTree(response.getEntity().getContent()); + closeResponse(response); + assertThat(responseNode).isNotNull(); + assertThatJson(responseNode) + .when(Option.IGNORING_EXTRA_FIELDS) + .isEqualTo("{" + + " scope: 'global'," + + " value: 'b2233abf-f84f-426f-b978-0d249b90cc45'" + + "}"); + } } diff --git a/modules/flowable-cmmn-rest/src/test/java/org/flowable/cmmn/rest/service/api/runtime/TaskVariablesCollectionResourceTest.java b/modules/flowable-cmmn-rest/src/test/java/org/flowable/cmmn/rest/service/api/runtime/TaskVariablesCollectionResourceTest.java index e418d62d1f0..15df0728109 100644 --- a/modules/flowable-cmmn-rest/src/test/java/org/flowable/cmmn/rest/service/api/runtime/TaskVariablesCollectionResourceTest.java +++ b/modules/flowable-cmmn-rest/src/test/java/org/flowable/cmmn/rest/service/api/runtime/TaskVariablesCollectionResourceTest.java @@ -25,6 +25,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.UUID; import org.apache.http.HttpStatus; import org.apache.http.client.methods.CloseableHttpResponse; @@ -71,6 +72,7 @@ public void testGetTaskVariables() throws Exception { caseVariables.put("dateProcVar", cal.getTime()); caseVariables.put("byteArrayProcVar", "Some raw bytes".getBytes()); caseVariables.put("overlappingVariable", "case-value"); + caseVariables.put("uuidVar", UUID.fromString("a053505c-43c9-479f-ae01-5352ce559786")); CaseInstance caseInstance = runtimeService.createCaseInstanceBuilder().caseDefinitionKey("oneHumanTaskCase").variables(caseVariables).start(); // Set local task variables, including one that has the same name as one @@ -86,6 +88,7 @@ public void testGetTaskVariables() throws Exception { taskVariables.put("dateTaskVar", cal.getTime()); taskVariables.put("byteArrayTaskVar", "Some raw bytes".getBytes()); taskVariables.put("overlappingVariable", "task-value"); + taskVariables.put("uuidVar", UUID.fromString("a053505c-43c9-479f-ae01-5352ce559786")); taskService.setVariablesLocal(task.getId(), taskVariables); // Request all variables (no scope provides) which include global an local @@ -97,7 +100,7 @@ public void testGetTaskVariables() throws Exception { closeResponse(response); assertThat(responseNode).isNotNull(); assertThat(responseNode.isArray()).isTrue(); - assertThat(responseNode).hasSize(17); + assertThat(responseNode).hasSize(18); // Overlapping variable should contain task-value AND be defined as "local" assertThatJson(responseNode) @@ -148,6 +151,9 @@ public void testGetTaskVariables() throws Exception { + " }," + " {" + " scope: 'local'" + + " }," + + " {" + + " scope: 'local'" + " }" + "]"); @@ -160,7 +166,7 @@ public void testGetTaskVariables() throws Exception { closeResponse(response); assertThat(responseNode).isNotNull(); assertThat(responseNode.isArray()).isTrue(); - assertThat(responseNode).hasSize(9); + assertThat(responseNode).hasSize(10); assertThatJson(responseNode) .when(Option.IGNORING_EXTRA_FIELDS, Option.IGNORING_ARRAY_ORDER, Option.IGNORING_EXTRA_ARRAY_ITEMS) .isEqualTo("[" diff --git a/modules/flowable-common-rest/src/main/java/org/flowable/common/rest/variable/UUIDRestVariableConverter.java b/modules/flowable-common-rest/src/main/java/org/flowable/common/rest/variable/UUIDRestVariableConverter.java new file mode 100644 index 00000000000..d271944016e --- /dev/null +++ b/modules/flowable-common-rest/src/main/java/org/flowable/common/rest/variable/UUIDRestVariableConverter.java @@ -0,0 +1,55 @@ +/* Licensed 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.flowable.common.rest.variable; + +import java.util.UUID; + +import org.flowable.common.engine.api.FlowableIllegalArgumentException; + +public class UUIDRestVariableConverter implements RestVariableConverter { + + @Override + public String getRestTypeName() { + return "uuid"; + } + + @Override + public Class getVariableType() { + return UUID.class; + } + + @Override + public Object getVariableValue(EngineRestVariable result) { + if (result.getValue() != null) { + if (!(result.getValue() instanceof String)) { + throw new FlowableIllegalArgumentException("Converter can only convert Strings"); + } + return UUID.fromString((String) result.getValue()); + } + return null; + } + + @Override + public void convertVariableValue(Object variableValue, EngineRestVariable result) { + if (variableValue != null) { + if (!(variableValue instanceof UUID)) { + throw new FlowableIllegalArgumentException("Converter can only convert UUIDs"); + } + result.setValue(((UUID)variableValue).toString()); + } else { + result.setValue(null); + } + } + +} diff --git a/modules/flowable-dmn-rest/src/main/java/org/flowable/dmn/rest/service/api/DmnRestResponseFactory.java b/modules/flowable-dmn-rest/src/main/java/org/flowable/dmn/rest/service/api/DmnRestResponseFactory.java index be0fc8a75d1..c8e9177a6db 100644 --- a/modules/flowable-dmn-rest/src/main/java/org/flowable/dmn/rest/service/api/DmnRestResponseFactory.java +++ b/modules/flowable-dmn-rest/src/main/java/org/flowable/dmn/rest/service/api/DmnRestResponseFactory.java @@ -29,6 +29,7 @@ import org.flowable.common.rest.variable.RestVariableConverter; import org.flowable.common.rest.variable.ShortRestVariableConverter; import org.flowable.common.rest.variable.StringRestVariableConverter; +import org.flowable.common.rest.variable.UUIDRestVariableConverter; import org.flowable.dmn.api.DecisionExecutionAuditContainer; import org.flowable.dmn.api.DecisionServiceExecutionAuditContainer; import org.flowable.dmn.api.DmnDecision; @@ -302,6 +303,7 @@ protected void initializeVariableConverters() { variableConverters.add(new InstantRestVariableConverter()); variableConverters.add(new LocalDateRestVariableConverter()); variableConverters.add(new LocalDateTimeRestVariableConverter()); + variableConverters.add(new UUIDRestVariableConverter()); } protected DmnRestUrlBuilder createUrlBuilder() { diff --git a/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/EngineSqlScriptBasedDbSchemaManager.java b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/EngineSqlScriptBasedDbSchemaManager.java index f8ed9c3450f..f15ccc3c334 100644 --- a/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/EngineSqlScriptBasedDbSchemaManager.java +++ b/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/db/EngineSqlScriptBasedDbSchemaManager.java @@ -198,14 +198,22 @@ protected ChangeLogVersion getChangeLogVersion() { } try (PreparedStatement statement = databaseConfiguration.getConnection() .prepareStatement("select ID from " + changeLogTableName + " order by DATEEXECUTED")) { - String changeLogVersion = null; + int changeLogVersion = 0; try (ResultSet resultSet = statement.executeQuery()) { while (resultSet.next()) { - changeLogVersion = resultSet.getString(1); + String changeLogVersionId = resultSet.getString(1); + int parsedChangeLogVersion = Integer.parseInt(changeLogVersionId); + if (parsedChangeLogVersion > changeLogVersion) { + // Even though we are ordering by DATEEXECUTED, and the last ID should be the last executed one. + // It is still possible that there are multiple entries with the same DATEEXECUTED value and the order might not be correct. + // e.g. MySQL 8.0 sometimes does not return the correct order. + changeLogVersion = parsedChangeLogVersion; + } } } - if (changeLogVersion != null) { - return new ChangeLogVersion(changeLogVersion, getDbVersionForChangelogVersion(changeLogVersion)); + if (changeLogVersion > 0) { + String changeLogVersionString = String.valueOf(changeLogVersion); + return new ChangeLogVersion(changeLogVersionString, getDbVersionForChangelogVersion(changeLogVersionString)); } } catch (SQLException e) { throw new RuntimeException("Failed to get change log version from " + changeLogTableName, e); diff --git a/modules/flowable-rest/src/main/java/org/flowable/rest/service/api/RestResponseFactory.java b/modules/flowable-rest/src/main/java/org/flowable/rest/service/api/RestResponseFactory.java index 63f46c051ae..4fb5a84974e 100644 --- a/modules/flowable-rest/src/main/java/org/flowable/rest/service/api/RestResponseFactory.java +++ b/modules/flowable-rest/src/main/java/org/flowable/rest/service/api/RestResponseFactory.java @@ -38,6 +38,7 @@ import org.flowable.common.rest.variable.RestVariableConverter; import org.flowable.common.rest.variable.ShortRestVariableConverter; import org.flowable.common.rest.variable.StringRestVariableConverter; +import org.flowable.common.rest.variable.UUIDRestVariableConverter; import org.flowable.dmn.api.DmnDecision; import org.flowable.engine.form.FormData; import org.flowable.engine.form.FormProperty; @@ -1528,6 +1529,7 @@ protected void initializeVariableConverters() { variableConverters.add(new LocalDateRestVariableConverter()); variableConverters.add(new LocalDateTimeRestVariableConverter()); variableConverters.add(new JsonObjectRestVariableConverter(objectMapper)); + variableConverters.add(new UUIDRestVariableConverter()); } protected String formatUrl(String serverRootUrl, String[] fragments, Object... arguments) { diff --git a/modules/flowable-rest/src/main/java/org/flowable/rest/service/api/runtime/process/BaseExecutionVariableResource.java b/modules/flowable-rest/src/main/java/org/flowable/rest/service/api/runtime/process/BaseExecutionVariableResource.java index af76e9037fc..224b41c3869 100644 --- a/modules/flowable-rest/src/main/java/org/flowable/rest/service/api/runtime/process/BaseExecutionVariableResource.java +++ b/modules/flowable-rest/src/main/java/org/flowable/rest/service/api/runtime/process/BaseExecutionVariableResource.java @@ -187,7 +187,7 @@ protected RestVariable setSimpleVariable(RestVariable restVariable, Execution ex throw new FlowableIllegalArgumentException("Variable name is required"); } - // Figure out scope, revert to local is omitted + // Figure out scope, revert to local if omitted RestVariableScope scope = restVariable.getVariableScope(); if (scope == null) { scope = RestVariableScope.LOCAL; diff --git a/modules/flowable-rest/src/test/java/org/flowable/rest/service/api/runtime/ProcessInstanceVariableResourceTest.java b/modules/flowable-rest/src/test/java/org/flowable/rest/service/api/runtime/ProcessInstanceVariableResourceTest.java index f4265ec0ca2..e7298b3b11b 100644 --- a/modules/flowable-rest/src/test/java/org/flowable/rest/service/api/runtime/ProcessInstanceVariableResourceTest.java +++ b/modules/flowable-rest/src/test/java/org/flowable/rest/service/api/runtime/ProcessInstanceVariableResourceTest.java @@ -27,6 +27,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.UUID; import org.apache.commons.io.IOUtils; import org.apache.http.HttpStatus; @@ -178,6 +179,31 @@ public void testGetProcessInstanceLocalDateTimeVariable() throws Exception { + "}"); } + @Test + @Deployment(resources = { "org/flowable/rest/service/api/runtime/ProcessInstanceVariableResourceTest.testProcess.bpmn20.xml" }) + public void testGetProcessInstanceUUIDVariable() throws Exception { + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess"); + UUID someUUID = UUID.fromString("239969dd-3310-4068-b558-e4cbce5650ea"); + runtimeService.setVariable(processInstance.getId(), "variable", someUUID); + + CloseableHttpResponse response = executeRequest( + new HttpGet( + SERVER_URL_PREFIX + RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_INSTANCE_VARIABLE, processInstance.getId(), "variable")), + HttpStatus.SC_OK); + + JsonNode responseNode = objectMapper.readTree(response.getEntity().getContent()); + + closeResponse(response); + assertThat(responseNode).isNotNull(); + assertThatJson(responseNode) + .when(Option.IGNORING_EXTRA_FIELDS) + .isEqualTo("{" + + " name: 'variable'," + + " type: 'uuid'," + + " value: '" + someUUID + "'" + + "}"); + } + /** * Test getting a process instance variable data. GET runtime/process-instances/{processInstanceId}/variables/{variableName} */ @@ -415,6 +441,40 @@ public void testUpdateLocalDateTimeProcessVariable() throws Exception { + "}"); } + @Test + @Deployment(resources = { "org/flowable/rest/service/api/runtime/ProcessInstanceVariableResourceTest.testProcess.bpmn20.xml" }) + public void testUpdateUUIDProcessVariable() throws Exception { + UUID someUUID = UUID.fromString("239969dd-3310-4068-b558-e4cbce5650ea"); + UUID someUUID2 = UUID.fromString("c5b16e77-0c15-4d7b-ac12-15352af76355"); + ProcessInstance processInstance = runtimeService + .startProcessInstanceByKey("oneTaskProcess", Collections.singletonMap("overlappingVariable", (Object) "processValue")); + runtimeService.setVariable(processInstance.getId(), "uuidVariable", someUUID); + + // Update variable + ObjectNode requestNode = objectMapper.createObjectNode(); + requestNode.put("name", "uuidVariable"); + requestNode.put("value", someUUID2.toString()); + requestNode.put("type", "uuid"); + + HttpPut httpPut = new HttpPut( + SERVER_URL_PREFIX + RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_INSTANCE_VARIABLE, processInstance.getId(), "uuidVariable")); + httpPut.setEntity(new StringEntity(requestNode.toString())); + CloseableHttpResponse response = executeRequest(httpPut, HttpStatus.SC_OK); + + assertThat(runtimeService.getVariable(processInstance.getId(), "uuidVariable")) + .isEqualTo(someUUID2); + + JsonNode responseNode = objectMapper.readTree(response.getEntity().getContent()); + closeResponse(response); + assertThat(responseNode).isNotNull(); + assertThatJson(responseNode) + .when(Option.IGNORING_EXTRA_FIELDS) + .isEqualTo("{" + + " scope: null," + + " value: 'c5b16e77-0c15-4d7b-ac12-15352af76355'" + + "}"); + } + /** * Test updating a single process variable using a binary stream. PUT runtime/process-instances/{processInstanceId}/variables/{variableName} */