From ab035a5c6f21828ab8cd439c332d0a3f3efa640d Mon Sep 17 00:00:00 2001 From: Valentin Zickner <3200232+vzickner@users.noreply.github.com> Date: Thu, 4 Apr 2024 16:18:08 +0200 Subject: [PATCH] add condition attribute for case migration mappings (#3855) * add condition attribute for case migration mappings * implement plan item instance level condition evaluation for terminate and wait_for_repetition case instance migration * implement plan item instance level condition evaluation for remove wait_for_repetition case instance migration * implement plan item instance level condition evaluation for activate plan item definition during case instance migration * implement plan item instance level condition evaluation for move to available during case instance migration --- .../ActivatePlanItemDefinitionMapping.java | 10 + ...eToAvailablePlanItemDefinitionMapping.java | 4 + .../migration/PlanItemDefinitionMapping.java | 14 + .../PlanItemDefinitionMappingBuilder.java | 28 +- ...orRepetitionPlanItemDefinitionMapping.java | 4 + .../TerminatePlanItemDefinitionMapping.java | 4 + ...orRepetitionPlanItemDefinitionMapping.java | 4 + .../runtime/ChangePlanItemStateBuilder.java | 20 +- .../impl/el/PlanItemInstancesWrapper.java | 4 + ...aseInstanceMigrationDocumentConstants.java | 1 + ...aseInstanceMigrationDocumentConverter.java | 15 + .../CaseInstanceMigrationManagerImpl.java | 10 +- .../AbstractCmmnDynamicStateManager.java | 129 ++- .../ChangePlanItemStateBuilderImpl.java | 18 + .../migration/CaseInstanceMigrationTest.java | 941 ++++++++++++++++++ .../stage-three-tasks-two-new.cmmn.xml | 32 + .../test/migration/stage-three-tasks.cmmn.xml | 28 + .../stage-with-event-listener-inside.cmmn.xml | 34 + ...listener-and-task-with-repetition.cmmn.xml | 30 + ...with-user-event-listener-and-task.cmmn.xml | 23 + ...age-without-event-listener-inside.cmmn.xml | 23 + .../migration/two-task-repetition.cmmn.xml | 18 + ...er-event-listener-with-repetition.cmmn.xml | 29 + ...event-listener-without-repetition.cmmn.xml | 22 + 24 files changed, 1394 insertions(+), 51 deletions(-) create mode 100644 modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-three-tasks-two-new.cmmn.xml create mode 100644 modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-three-tasks.cmmn.xml create mode 100644 modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-with-event-listener-inside.cmmn.xml create mode 100644 modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-with-user-event-listener-and-task-with-repetition.cmmn.xml create mode 100644 modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-with-user-event-listener-and-task.cmmn.xml create mode 100644 modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-without-event-listener-inside.cmmn.xml create mode 100644 modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/two-task-repetition.cmmn.xml create mode 100644 modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/user-event-listener-with-repetition.cmmn.xml create mode 100644 modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/user-event-listener-without-repetition.cmmn.xml diff --git a/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/ActivatePlanItemDefinitionMapping.java b/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/ActivatePlanItemDefinitionMapping.java index cedca747278..cbbd049d22d 100644 --- a/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/ActivatePlanItemDefinitionMapping.java +++ b/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/ActivatePlanItemDefinitionMapping.java @@ -30,6 +30,16 @@ public ActivatePlanItemDefinitionMapping(String planItemDefinitionId) { super(planItemDefinitionId); } + public ActivatePlanItemDefinitionMapping(String planItemDefinitionId, String condition) { + super(planItemDefinitionId, condition); + } + + public ActivatePlanItemDefinitionMapping(String planItemDefinitionId, String newAssignee, String condition, Map withLocalVariables) { + super(planItemDefinitionId, condition); + this.newAssignee = newAssignee; + this.withLocalVariables = withLocalVariables; + } + public String getNewAssignee() { return newAssignee; } diff --git a/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/MoveToAvailablePlanItemDefinitionMapping.java b/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/MoveToAvailablePlanItemDefinitionMapping.java index dd8b8e1145c..f8f64f2ed18 100644 --- a/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/MoveToAvailablePlanItemDefinitionMapping.java +++ b/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/MoveToAvailablePlanItemDefinitionMapping.java @@ -28,6 +28,10 @@ public MoveToAvailablePlanItemDefinitionMapping(String planItemDefinitionId, Map this.withLocalVariables = withLocalVariables; } + public MoveToAvailablePlanItemDefinitionMapping(String planItemDefinitionId, String condition) { + super(planItemDefinitionId, condition); + } + public Map getWithLocalVariables() { return withLocalVariables; } diff --git a/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/PlanItemDefinitionMapping.java b/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/PlanItemDefinitionMapping.java index 331162a89f4..6d9ae112a0c 100644 --- a/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/PlanItemDefinitionMapping.java +++ b/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/PlanItemDefinitionMapping.java @@ -15,11 +15,17 @@ public abstract class PlanItemDefinitionMapping { protected String planItemDefinitionId; + protected String condition; public PlanItemDefinitionMapping(String planItemDefinitionId) { this.planItemDefinitionId = planItemDefinitionId; } + public PlanItemDefinitionMapping(String planItemDefinitionId, String condition) { + this.planItemDefinitionId = planItemDefinitionId; + this.condition = condition; + } + public String getPlanItemDefinitionId() { return planItemDefinitionId; } @@ -27,4 +33,12 @@ public String getPlanItemDefinitionId() { public void setPlanItemDefinitionId(String planItemDefinitionId) { this.planItemDefinitionId = planItemDefinitionId; } + + public String getCondition() { + return condition; + } + + public void setCondition(String condition) { + this.condition = condition; + } } diff --git a/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/PlanItemDefinitionMappingBuilder.java b/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/PlanItemDefinitionMappingBuilder.java index 0bc49afde71..335962d451e 100644 --- a/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/PlanItemDefinitionMappingBuilder.java +++ b/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/PlanItemDefinitionMappingBuilder.java @@ -29,15 +29,27 @@ public static ActivatePlanItemDefinitionMapping createActivatePlanItemDefinition return new ActivatePlanItemDefinitionMapping(planItemDefinitionId, newAssignee, withLocalVariables); } - + + public static ActivatePlanItemDefinitionMapping createActivatePlanItemDefinitionMappingFor(String planItemDefinitionId, String condition) { + return new ActivatePlanItemDefinitionMapping(planItemDefinitionId, condition); + } + public static TerminatePlanItemDefinitionMapping createTerminatePlanItemDefinitionMappingFor(String planItemDefinitionId) { return new TerminatePlanItemDefinitionMapping(planItemDefinitionId); } - + + public static TerminatePlanItemDefinitionMapping createTerminatePlanItemDefinitionMappingFor(String planItemDefinitionId, String condition) { + return new TerminatePlanItemDefinitionMapping(planItemDefinitionId, condition); + } + public static MoveToAvailablePlanItemDefinitionMapping createMoveToAvailablePlanItemDefinitionMappingFor(String planItemDefinitionId) { return new MoveToAvailablePlanItemDefinitionMapping(planItemDefinitionId); } - + + public static MoveToAvailablePlanItemDefinitionMapping createMoveToAvailablePlanItemDefinitionMappingFor(String planItemDefinitionId, String condition) { + return new MoveToAvailablePlanItemDefinitionMapping(planItemDefinitionId, condition); + } + public static MoveToAvailablePlanItemDefinitionMapping createMoveToAvailablePlanItemDefinitionMappingFor( String planItemDefinitionId, Map withLocalVariables) { @@ -47,8 +59,16 @@ public static MoveToAvailablePlanItemDefinitionMapping createMoveToAvailablePlan public static WaitingForRepetitionPlanItemDefinitionMapping createWaitingForRepetitionPlanItemDefinitionMappingFor(String planItemDefinitionId) { return new WaitingForRepetitionPlanItemDefinitionMapping(planItemDefinitionId); } - + + public static WaitingForRepetitionPlanItemDefinitionMapping createWaitingForRepetitionPlanItemDefinitionMappingFor(String planItemDefinitionId, String condition) { + return new WaitingForRepetitionPlanItemDefinitionMapping(planItemDefinitionId, condition); + } + public static RemoveWaitingForRepetitionPlanItemDefinitionMapping createRemoveWaitingForRepetitionPlanItemDefinitionMappingFor(String planItemDefinitionId) { return new RemoveWaitingForRepetitionPlanItemDefinitionMapping(planItemDefinitionId); } + + public static RemoveWaitingForRepetitionPlanItemDefinitionMapping createRemoveWaitingForRepetitionPlanItemDefinitionMappingFor(String planItemDefinitionId, String condition) { + return new RemoveWaitingForRepetitionPlanItemDefinitionMapping(planItemDefinitionId, condition); + } } diff --git a/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/RemoveWaitingForRepetitionPlanItemDefinitionMapping.java b/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/RemoveWaitingForRepetitionPlanItemDefinitionMapping.java index 677d2706516..b9778c4c011 100644 --- a/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/RemoveWaitingForRepetitionPlanItemDefinitionMapping.java +++ b/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/RemoveWaitingForRepetitionPlanItemDefinitionMapping.java @@ -17,4 +17,8 @@ public class RemoveWaitingForRepetitionPlanItemDefinitionMapping extends PlanIte public RemoveWaitingForRepetitionPlanItemDefinitionMapping(String planItemDefinitionId) { super(planItemDefinitionId); } + + public RemoveWaitingForRepetitionPlanItemDefinitionMapping(String planItemDefinitionId, String condition) { + super(planItemDefinitionId, condition); + } } diff --git a/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/TerminatePlanItemDefinitionMapping.java b/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/TerminatePlanItemDefinitionMapping.java index 05a3ef9c7b6..0143c25c617 100644 --- a/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/TerminatePlanItemDefinitionMapping.java +++ b/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/TerminatePlanItemDefinitionMapping.java @@ -17,4 +17,8 @@ public class TerminatePlanItemDefinitionMapping extends PlanItemDefinitionMappin public TerminatePlanItemDefinitionMapping(String planItemDefinitionId) { super(planItemDefinitionId); } + + public TerminatePlanItemDefinitionMapping(String planItemDefinitionId, String condition) { + super(planItemDefinitionId, condition); + } } diff --git a/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/WaitingForRepetitionPlanItemDefinitionMapping.java b/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/WaitingForRepetitionPlanItemDefinitionMapping.java index 920cfe14edc..7fbf9a317d3 100644 --- a/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/WaitingForRepetitionPlanItemDefinitionMapping.java +++ b/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/migration/WaitingForRepetitionPlanItemDefinitionMapping.java @@ -17,4 +17,8 @@ public class WaitingForRepetitionPlanItemDefinitionMapping extends PlanItemDefin public WaitingForRepetitionPlanItemDefinitionMapping(String planItemDefinitionId) { super(planItemDefinitionId); } + + public WaitingForRepetitionPlanItemDefinitionMapping(String planItemDefinitionId, String condition) { + super(planItemDefinitionId, condition); + } } diff --git a/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/runtime/ChangePlanItemStateBuilder.java b/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/runtime/ChangePlanItemStateBuilder.java index 232960515d7..77b6ff31137 100644 --- a/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/runtime/ChangePlanItemStateBuilder.java +++ b/modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/runtime/ChangePlanItemStateBuilder.java @@ -17,6 +17,9 @@ import org.flowable.cmmn.api.migration.ActivatePlanItemDefinitionMapping; import org.flowable.cmmn.api.migration.MoveToAvailablePlanItemDefinitionMapping; +import org.flowable.cmmn.api.migration.RemoveWaitingForRepetitionPlanItemDefinitionMapping; +import org.flowable.cmmn.api.migration.TerminatePlanItemDefinitionMapping; +import org.flowable.cmmn.api.migration.WaitingForRepetitionPlanItemDefinitionMapping; import org.flowable.common.engine.api.FlowableException; import org.flowable.common.engine.api.FlowableObjectNotFoundException; @@ -73,7 +76,12 @@ public interface ChangePlanItemStateBuilder { * Terminate a plan item by definition id without terminating another plan item instance. */ ChangePlanItemStateBuilder terminatePlanItemDefinitionId(String planItemDefinitionId); - + + /** + * Terminate a plan item by definition mapping without terminating another plan item instance. + */ + ChangePlanItemStateBuilder terminatePlanItemDefinition(TerminatePlanItemDefinitionMapping planItemDefinition); + /** * Terminate multiple plan items by definition id without terminating another plan item instance. */ @@ -84,6 +92,11 @@ public interface ChangePlanItemStateBuilder { */ ChangePlanItemStateBuilder addWaitingForRepetitionPlanItemDefinitionId(String planItemDefinitionId); + /** + * Add waiting for repetition to a plan item by definition mapping. + */ + ChangePlanItemStateBuilder addWaitingForRepetitionPlanItemDefinition(WaitingForRepetitionPlanItemDefinitionMapping planItemDefinitionMapping); + /** * Add multiple waiting for repetitions to a plan item by definition id. */ @@ -94,6 +107,11 @@ public interface ChangePlanItemStateBuilder { */ ChangePlanItemStateBuilder removeWaitingForRepetitionPlanItemDefinitionId(String planItemDefinitionId); + /** + * Remove waiting for repetition from a plan item by definition. + */ + ChangePlanItemStateBuilder removeWaitingForRepetitionPlanItemDefinition(RemoveWaitingForRepetitionPlanItemDefinitionMapping planItemDefinitionId); + /** * Remove multiple waiting for repetitions from a plan item by definition id. */ diff --git a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/el/PlanItemInstancesWrapper.java b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/el/PlanItemInstancesWrapper.java index 68b99778e55..8a09bf510be 100644 --- a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/el/PlanItemInstancesWrapper.java +++ b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/el/PlanItemInstancesWrapper.java @@ -64,6 +64,10 @@ public int count() { return planItemInstances.size(); } + public boolean exists() { + return count() > 0; + } + protected void ensurePlanItemInstanceInitialized() { if (planItemInstances == null) { planItemInstances = collectAllChildPlanItemInstances(caseInstanceEntity); diff --git a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/migration/CaseInstanceMigrationDocumentConstants.java b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/migration/CaseInstanceMigrationDocumentConstants.java index 7eae3415bb0..fd4436210ff 100644 --- a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/migration/CaseInstanceMigrationDocumentConstants.java +++ b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/migration/CaseInstanceMigrationDocumentConstants.java @@ -28,6 +28,7 @@ public interface CaseInstanceMigrationDocumentConstants { String EXISTING_PLAN_ITEM_DEFINITION_ID_JSON_PROPERTY = "existingPlanItemDefinitionId"; String NEW_PLAN_ITEM_DEFINITION_ID_JSON_PROPERTY = "newPlanItemDefinitionId"; String NEW_ASSIGNEE_JSON_PROPERTY = "newAssignee"; + String CONDITION_JSON_PROPERTY = "condition"; String ACTIVATE_PLAN_ITEM_DEFINITIONS_JSON_SECTION = "activatePlanItemDefinitions"; String TERMINATE_PLAN_ITEM_DEFINITIONS_JSON_SECTION = "terminatePlanItemDefinitions"; diff --git a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/migration/CaseInstanceMigrationDocumentConverter.java b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/migration/CaseInstanceMigrationDocumentConverter.java index aaed005bd12..e5b134b9f5d 100644 --- a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/migration/CaseInstanceMigrationDocumentConverter.java +++ b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/migration/CaseInstanceMigrationDocumentConverter.java @@ -133,6 +133,7 @@ protected static ArrayNode convertToJsonActivatePlanItemDefinitionMappings(List< ObjectNode mappingNode = objectMapper.createObjectNode(); mappingNode.put(PLAN_ITEM_DEFINITION_ID_JSON_PROPERTY, mapping.getPlanItemDefinitionId()); mappingNode.put(NEW_ASSIGNEE_JSON_PROPERTY, mapping.getNewAssignee()); + mappingNode.put(CONDITION_JSON_PROPERTY, mapping.getCondition()); mappingsArray.add(mappingNode); } @@ -145,6 +146,7 @@ protected static ArrayNode convertToJsonTerminatePlanItemDefinitionMappings(List for (TerminatePlanItemDefinitionMapping mapping : planItemDefinitionMappings) { ObjectNode mappingNode = objectMapper.createObjectNode(); mappingNode.put(PLAN_ITEM_DEFINITION_ID_JSON_PROPERTY, mapping.getPlanItemDefinitionId()); + mappingNode.put(CONDITION_JSON_PROPERTY, mapping.getCondition()); mappingsArray.add(mappingNode); } @@ -157,6 +159,7 @@ protected static ArrayNode convertToJsonMoveToAvailablePlanItemDefinitionMapping for (MoveToAvailablePlanItemDefinitionMapping mapping : planItemDefinitionMappings) { ObjectNode mappingNode = objectMapper.createObjectNode(); mappingNode.put(PLAN_ITEM_DEFINITION_ID_JSON_PROPERTY, mapping.getPlanItemDefinitionId()); + mappingNode.put(CONDITION_JSON_PROPERTY, mapping.getCondition()); mappingsArray.add(mappingNode); } @@ -169,6 +172,7 @@ protected static ArrayNode convertToJsonWaitingForRepetitionPlanItemDefinitionMa for (WaitingForRepetitionPlanItemDefinitionMapping mapping : planItemDefinitionMappings) { ObjectNode mappingNode = objectMapper.createObjectNode(); mappingNode.put(PLAN_ITEM_DEFINITION_ID_JSON_PROPERTY, mapping.getPlanItemDefinitionId()); + mappingNode.put(CONDITION_JSON_PROPERTY, mapping.getCondition()); mappingsArray.add(mappingNode); } @@ -181,6 +185,7 @@ protected static ArrayNode convertToJsonRemoveWaitingForRepetitionPlanItemDefini for (RemoveWaitingForRepetitionPlanItemDefinitionMapping mapping : planItemDefinitionMappings) { ObjectNode mappingNode = objectMapper.createObjectNode(); mappingNode.put(PLAN_ITEM_DEFINITION_ID_JSON_PROPERTY, mapping.getPlanItemDefinitionId()); + mappingNode.put(CONDITION_JSON_PROPERTY, mapping.getCondition()); mappingsArray.add(mappingNode); } @@ -234,6 +239,8 @@ public static CaseInstanceMigrationDocument convertFromJson(String jsonCaseInsta ActivatePlanItemDefinitionMapping activateDefinitionMapping = new ActivatePlanItemDefinitionMapping(planItemDefinitionId); String newAssginee = getJsonProperty(NEW_ASSIGNEE_JSON_PROPERTY, mappingNode); activateDefinitionMapping.setNewAssignee(newAssginee); + String condition = getJsonProperty(CONDITION_JSON_PROPERTY, mappingNode); + activateDefinitionMapping.setCondition(condition); documentBuilder.addActivatePlanItemDefinitionMapping(activateDefinitionMapping); } @@ -244,6 +251,8 @@ public static CaseInstanceMigrationDocument convertFromJson(String jsonCaseInsta for (JsonNode mappingNode : terminateMappingNodes) { String planItemDefinitionId = getJsonProperty(PLAN_ITEM_DEFINITION_ID_JSON_PROPERTY, mappingNode); TerminatePlanItemDefinitionMapping terminateDefinitionMapping = new TerminatePlanItemDefinitionMapping(planItemDefinitionId); + String condition = getJsonProperty(CONDITION_JSON_PROPERTY, mappingNode); + terminateDefinitionMapping.setCondition(condition); documentBuilder.addTerminatePlanItemDefinitionMapping(terminateDefinitionMapping); } } @@ -253,6 +262,8 @@ public static CaseInstanceMigrationDocument convertFromJson(String jsonCaseInsta for (JsonNode mappingNode : moveToAvailableMappingNodes) { String planItemDefinitionId = getJsonProperty(PLAN_ITEM_DEFINITION_ID_JSON_PROPERTY, mappingNode); MoveToAvailablePlanItemDefinitionMapping moveToAvailableDefinitionMapping = new MoveToAvailablePlanItemDefinitionMapping(planItemDefinitionId); + String condition = getJsonProperty(CONDITION_JSON_PROPERTY, mappingNode); + moveToAvailableDefinitionMapping.setCondition(condition); documentBuilder.addMoveToAvailablePlanItemDefinitionMapping(moveToAvailableDefinitionMapping); } } @@ -262,6 +273,8 @@ public static CaseInstanceMigrationDocument convertFromJson(String jsonCaseInsta for (JsonNode mappingNode : waitingForRepetitionMappingNodes) { String planItemDefinitionId = getJsonProperty(PLAN_ITEM_DEFINITION_ID_JSON_PROPERTY, mappingNode); WaitingForRepetitionPlanItemDefinitionMapping waitingForRepetitionDefinitionMapping = new WaitingForRepetitionPlanItemDefinitionMapping(planItemDefinitionId); + String condition = getJsonProperty(CONDITION_JSON_PROPERTY, mappingNode); + waitingForRepetitionDefinitionMapping.setCondition(condition); documentBuilder.addWaitingForRepetitionPlanItemDefinitionMapping(waitingForRepetitionDefinitionMapping); } } @@ -271,6 +284,8 @@ public static CaseInstanceMigrationDocument convertFromJson(String jsonCaseInsta for (JsonNode mappingNode : removeWaitingForRepetitionMappingNodes) { String planItemDefinitionId = getJsonProperty(PLAN_ITEM_DEFINITION_ID_JSON_PROPERTY, mappingNode); RemoveWaitingForRepetitionPlanItemDefinitionMapping removeWaitingForRepetitionDefinitionMapping = new RemoveWaitingForRepetitionPlanItemDefinitionMapping(planItemDefinitionId); + String condition = getJsonProperty(CONDITION_JSON_PROPERTY, mappingNode); + removeWaitingForRepetitionDefinitionMapping.setCondition(condition); documentBuilder.addRemoveWaitingForRepetitionPlanItemDefinitionMapping(removeWaitingForRepetitionDefinitionMapping); } } diff --git a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/migration/CaseInstanceMigrationManagerImpl.java b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/migration/CaseInstanceMigrationManagerImpl.java index ec189f7ef67..983cc543cc5 100644 --- a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/migration/CaseInstanceMigrationManagerImpl.java +++ b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/migration/CaseInstanceMigrationManagerImpl.java @@ -14,9 +14,11 @@ package org.flowable.cmmn.engine.impl.migration; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import org.flowable.batch.api.Batch; import org.flowable.batch.api.BatchPart; @@ -302,7 +304,7 @@ protected void doMigrateCaseInstance(CaseInstanceEntity caseInstance, CaseDefini cmmnEngineConfiguration.getExpressionManager().createExpression(document.getPostUpgradeExpression()).getValue(caseInstance); } } - + protected void doMigrateHistoricCaseInstance(HistoricCaseInstanceEntity historicCaseInstance, CaseDefinition caseDefinitionToMigrateTo, HistoricCaseInstanceMigrationDocument document, CommandContext commandContext) { LOGGER.debug("Start migration of historic case instance with Id:'{}' to case definition identified by {}", historicCaseInstance.getId(), printCaseDefinitionIdentifierMessage(document)); @@ -367,7 +369,7 @@ protected ChangePlanItemStateBuilderImpl prepareChangeStateBuilder(CaseInstance } for (TerminatePlanItemDefinitionMapping planItemDefinitionMapping : document.getTerminatePlanItemDefinitionMappings()) { - changePlanItemStateBuilder.terminatePlanItemDefinitionId(planItemDefinitionMapping.getPlanItemDefinitionId()); + changePlanItemStateBuilder.terminatePlanItemDefinition(planItemDefinitionMapping); } for (MoveToAvailablePlanItemDefinitionMapping planItemDefinitionMapping : document.getMoveToAvailablePlanItemDefinitionMappings()) { @@ -375,11 +377,11 @@ protected ChangePlanItemStateBuilderImpl prepareChangeStateBuilder(CaseInstance } for (WaitingForRepetitionPlanItemDefinitionMapping planItemDefinitionMapping : document.getWaitingForRepetitionPlanItemDefinitionMappings()) { - changePlanItemStateBuilder.addWaitingForRepetitionPlanItemDefinitionId(planItemDefinitionMapping.getPlanItemDefinitionId()); + changePlanItemStateBuilder.addWaitingForRepetitionPlanItemDefinition(planItemDefinitionMapping); } for (RemoveWaitingForRepetitionPlanItemDefinitionMapping planItemDefinitionMapping : document.getRemoveWaitingForRepetitionPlanItemDefinitionMappings()) { - changePlanItemStateBuilder.removeWaitingForRepetitionPlanItemDefinitionId(planItemDefinitionMapping.getPlanItemDefinitionId()); + changePlanItemStateBuilder.removeWaitingForRepetitionPlanItemDefinition(planItemDefinitionMapping); } for (ChangePlanItemIdMapping changePlanItemIdMapping : document.getChangePlanItemIdMappings()) { 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 2079fb26d21..7b110c2d382 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 @@ -17,6 +17,7 @@ import java.util.Collections; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -29,6 +30,7 @@ import org.flowable.cmmn.api.migration.ChangePlanItemIdMapping; import org.flowable.cmmn.api.migration.ChangePlanItemIdWithDefinitionIdMapping; import org.flowable.cmmn.api.migration.MoveToAvailablePlanItemDefinitionMapping; +import org.flowable.cmmn.api.migration.PlanItemDefinitionMapping; import org.flowable.cmmn.api.migration.RemoveWaitingForRepetitionPlanItemDefinitionMapping; import org.flowable.cmmn.api.migration.TerminatePlanItemDefinitionMapping; import org.flowable.cmmn.api.migration.WaitingForRepetitionPlanItemDefinitionMapping; @@ -229,7 +231,11 @@ protected void executeActivatePlanItemInstances(CaseInstanceChangeState caseInst } PlanItemInstanceEntity newPlanItemInstance = createStagesAndPlanItemInstances(planItem, - caseInstance, caseInstanceChangeState, commandContext); + caseInstance, caseInstanceChangeState, planItemDefinitionMapping, commandContext); + + if (newPlanItemInstance == null) { + continue; // condition evaluated to false, this plan item should not be activated + } if (planItemDefinitionMapping.getWithLocalVariables() != null && !planItemDefinitionMapping.getWithLocalVariables().isEmpty()) { newPlanItemInstance.setVariablesLocal(planItemDefinitionMapping.getWithLocalVariables()); @@ -311,6 +317,12 @@ protected void executeChangePlanItemInstancesToAvailableState(CaseInstanceChange } } } + + boolean conditionResult = (parentPlanItemInstance != null && evaluateCondition(parentPlanItemInstance, planItemDefinitionMapping)) + || (parentPlanItemInstance == null && evaluateCondition(caseInstance, planItemDefinitionMapping)); + if (!conditionResult) { + continue; + } PlanItemInstanceEntity availablePlanItemInstance = planItemInstanceEntityManager.createPlanItemInstanceEntityBuilder() .planItem(planItem) @@ -397,10 +409,13 @@ protected void executeAddWaitingForRepetitionPlanItemInstances(CaseInstanceChang .list(); List waitingForRepetitionPlanItemInstances = new ArrayList<>(); + PlanItemInstance activePlanItemInstance = null; if (planItemInstances != null && !planItemInstances.isEmpty()) { for (PlanItemInstance planItemInstance : planItemInstances) { if (planItemInstance.getState().equalsIgnoreCase(PlanItemInstanceState.WAITING_FOR_REPETITION)) { waitingForRepetitionPlanItemInstances.add(planItemInstance); + } else if (planItemInstance.getState().equalsIgnoreCase(PlanItemInstanceState.ACTIVE)) { + activePlanItemInstance = planItemInstance; } } } @@ -419,27 +434,31 @@ protected void executeAddWaitingForRepetitionPlanItemInstances(CaseInstanceChang } } } - - PlanItemInstanceEntity waitingForRepetitionPlanItemInstance = planItemInstanceEntityManager.createPlanItemInstanceEntityBuilder() - .planItem(planItem) - .caseDefinitionId(caseInstance.getCaseDefinitionId()) - .caseInstanceId(caseInstance.getId()) - .stagePlanItemInstance(parentPlanItemInstance) - .tenantId(caseInstance.getTenantId()) - .addToParent(true) - .create(); - - if (planItem.getPlanItemDefinition() instanceof Stage) { - caseInstanceChangeState.addCreatedStageInstance(planItemDefinitionMapping.getPlanItemDefinitionId(), waitingForRepetitionPlanItemInstance); + + if ((activePlanItemInstance == null && parentPlanItemInstance == null && evaluateCondition(caseInstance, planItemDefinitionMapping)) + || (activePlanItemInstance instanceof PlanItemInstanceEntity + && evaluateCondition((PlanItemInstanceEntity) activePlanItemInstance, planItemDefinitionMapping)) + || (parentPlanItemInstance != null && evaluateCondition(parentPlanItemInstance, planItemDefinitionMapping))) { + + PlanItemInstanceEntity waitingForRepetitionPlanItemInstance = planItemInstanceEntityManager.createPlanItemInstanceEntityBuilder() + .planItem(planItem) + .caseDefinitionId(caseInstance.getCaseDefinitionId()) + .caseInstanceId(caseInstance.getId()) + .stagePlanItemInstance(parentPlanItemInstance) + .tenantId(caseInstance.getTenantId()) + .addToParent(true) + .create(); + + if (planItem.getPlanItemDefinition() instanceof Stage) { + caseInstanceChangeState.addCreatedStageInstance(planItemDefinitionMapping.getPlanItemDefinitionId(), waitingForRepetitionPlanItemInstance); + } + + CmmnHistoryManager cmmnHistoryManager = cmmnEngineConfiguration.getCmmnHistoryManager(); + cmmnHistoryManager.recordPlanItemInstanceCreated(waitingForRepetitionPlanItemInstance); + + waitingForRepetitionPlanItemInstance.setState(PlanItemInstanceState.WAITING_FOR_REPETITION); + cmmnHistoryManager.recordPlanItemInstanceAvailable(waitingForRepetitionPlanItemInstance); } - - CmmnHistoryManager cmmnHistoryManager = cmmnEngineConfiguration.getCmmnHistoryManager(); - cmmnHistoryManager.recordPlanItemInstanceCreated(waitingForRepetitionPlanItemInstance); - - waitingForRepetitionPlanItemInstance.setState(PlanItemInstanceState.WAITING_FOR_REPETITION); - cmmnHistoryManager.recordPlanItemInstanceAvailable(waitingForRepetitionPlanItemInstance); - - continue; } } } @@ -463,15 +482,17 @@ protected void executeRemoveWaitingForRepetitionPlanItemInstances(CaseInstanceCh for (PlanItemInstance planItemInstance : planItemInstances) { if (planItemInstance.getState().equalsIgnoreCase(PlanItemInstanceState.WAITING_FOR_REPETITION)) { PlanItemInstanceEntity planItemInstanceEntity = (PlanItemInstanceEntity) planItemInstance; - - Date currentTime = cmmnEngineConfiguration.getClock().getCurrentTime(); - CmmnHistoryManager cmmnHistoryManager = cmmnEngineConfiguration.getCmmnHistoryManager(); - planItemInstanceEntity.setState(PlanItemInstanceState.TERMINATED); - planItemInstanceEntity.setEndedTime(currentTime); - planItemInstanceEntity.setTerminatedTime(currentTime); - cmmnHistoryManager.recordPlanItemInstanceTerminated(planItemInstanceEntity); - - planItemInstanceEntityManager.delete(planItemInstanceEntity); + + if (evaluateCondition(planItemInstanceEntity, planItemDefinitionMapping)) { + Date currentTime = cmmnEngineConfiguration.getClock().getCurrentTime(); + CmmnHistoryManager cmmnHistoryManager = cmmnEngineConfiguration.getCmmnHistoryManager(); + planItemInstanceEntity.setState(PlanItemInstanceState.TERMINATED); + planItemInstanceEntity.setEndedTime(currentTime); + planItemInstanceEntity.setTerminatedTime(currentTime); + cmmnHistoryManager.recordPlanItemInstanceTerminated(planItemInstanceEntity); + + planItemInstanceEntityManager.delete(planItemInstanceEntity); + } } } } @@ -741,7 +762,8 @@ protected void executeTerminatePlanItemInstances(CaseInstanceChangeState caseIns List currentPlanItemInstanceList = currentPlanItemInstanceMap.get(planItemDefinitionMapping.getPlanItemDefinitionId()); for (PlanItemInstanceEntity planItemInstance : currentPlanItemInstanceList) { if (!PlanItemInstanceState.TERMINAL_STATES.contains(planItemInstance.getState()) && - !PlanItemInstanceState.WAITING_FOR_REPETITION.equals(planItemInstance.getState())) { + !PlanItemInstanceState.WAITING_FOR_REPETITION.equals(planItemInstance.getState()) && + evaluateCondition(planItemInstance, planItemDefinitionMapping)) { terminatePlanItemInstance(planItemInstance, commandContext); caseInstanceChangeState.addTerminatedPlanItemInstance(planItemInstance.getPlanItemDefinitionId(), planItemInstance); @@ -824,7 +846,7 @@ protected PlanItemInstanceEntity resolveParentPlanItemInstanceToDelete(PlanItemI } protected PlanItemInstanceEntity createStagesAndPlanItemInstances(PlanItem planItem, CaseInstanceEntity caseInstance, - CaseInstanceChangeState caseInstanceChangeState, CommandContext commandContext) { + CaseInstanceChangeState caseInstanceChangeState, ActivatePlanItemDefinitionMapping planItemDefinitionMapping, CommandContext commandContext) { PlanItemInstanceEntityManager planItemInstanceEntityManager = cmmnEngineConfiguration.getPlanItemInstanceEntityManager(); @@ -833,15 +855,29 @@ protected PlanItemInstanceEntity createStagesAndPlanItemInstances(PlanItem planI Stage stage = planItemDefinition.getParentStage(); Map> runtimePlanItemInstanceMap = caseInstanceChangeState.getRuntimePlanItemInstances(); + PlanItemInstanceEntity closestAncestorPlanItemInstance = null; while (stage != null) { - if (!stage.isPlanModel() && !caseInstanceChangeState.getCreatedStageInstances().containsKey(stage.getId()) && - !isStageAncestorOfAnyPlanItemInstance(stage.getId(), runtimePlanItemInstanceMap)) { - - stagesToCreate.put(stage.getId(), stage); + if (!stage.isPlanModel() && !caseInstanceChangeState.getCreatedStageInstances().containsKey(stage.getId())) { + PlanItemInstanceEntity stageAncestorInstance = getStageAncestorOfAnyPlanItemInstance(stage.getId(), runtimePlanItemInstanceMap); + if (stageAncestorInstance == null) { + stagesToCreate.put(stage.getId(), stage); + } else if (closestAncestorPlanItemInstance == null) { + closestAncestorPlanItemInstance = stageAncestorInstance; + } } stage = stage.getParentStage(); } + if (closestAncestorPlanItemInstance == null) { + if (!evaluateCondition(caseInstance, planItemDefinitionMapping)) { + return null; + } + } else { + if (!evaluateCondition(closestAncestorPlanItemInstance, planItemDefinitionMapping)) { + return null; + } + } + // Build the stage hierarchy for (Stage stageToCreate : stagesToCreate.values()) { if (!caseInstanceChangeState.getCreatedStageInstances().containsKey(stageToCreate.getId())) { @@ -947,23 +983,23 @@ protected void createChildPlanItemInstancesForStage(List } } - protected boolean isStageAncestorOfAnyPlanItemInstance(String stageId, Map> planItemInstanceMap) { + protected PlanItemInstanceEntity getStageAncestorOfAnyPlanItemInstance(String stageId, Map> planItemInstanceMap) { for (List planItemInstanceList : planItemInstanceMap.values()) { for (PlanItemInstanceEntity planItemInstance : planItemInstanceList) { if (planItemInstance.getPlanItem() != null) { PlanItemDefinition planItemDefinition = planItemInstance.getPlanItem().getPlanItemDefinition(); - + if (planItemDefinition.getId().equals(stageId)) { - return true; + return planItemInstance; } - + if (isStageAncestor(stageId, planItemDefinition)) { - return true; + return planItemInstance; } } } } - return false; + return null; } protected boolean isStageAncestor(String stageId, PlanItemDefinition planItemDefinition) { @@ -1223,4 +1259,13 @@ protected String getCaseDefinitionIdToMigrateTo(CaseInstanceChangeState caseInst return caseDefinitionIdToMigrateTo; } + protected boolean evaluateCondition(VariableContainer variableContainer, T planItemDefinitionMapping) { + if (planItemDefinitionMapping.getCondition() != null) { + Object conditionResult = cmmnEngineConfiguration.getExpressionManager().createExpression(planItemDefinitionMapping.getCondition()) + .getValue(variableContainer); + return conditionResult instanceof Boolean condition && condition; + } + return true; + } + } diff --git a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/runtime/ChangePlanItemStateBuilderImpl.java b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/runtime/ChangePlanItemStateBuilderImpl.java index 72205d50e02..c6dc04e1f3d 100644 --- a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/runtime/ChangePlanItemStateBuilderImpl.java +++ b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/runtime/ChangePlanItemStateBuilderImpl.java @@ -115,6 +115,12 @@ public ChangePlanItemStateBuilder terminatePlanItemDefinitionId(String planItemD return this; } + @Override + public ChangePlanItemStateBuilder terminatePlanItemDefinition(TerminatePlanItemDefinitionMapping planItemDefinition) { + terminatePlanItemDefinitions.add(planItemDefinition); + return this; + } + @Override public ChangePlanItemStateBuilder terminatePlanItemDefinitionIds(List planItemDefinitionIds) { if (planItemDefinitionIds != null) { @@ -131,6 +137,12 @@ public ChangePlanItemStateBuilder addWaitingForRepetitionPlanItemDefinitionId(St return this; } + @Override + public ChangePlanItemStateBuilder addWaitingForRepetitionPlanItemDefinition(WaitingForRepetitionPlanItemDefinitionMapping planItemDefinitionMapping) { + waitingForRepetitionPlanItemDefinitions.add(planItemDefinitionMapping); + return this; + } + @Override public ChangePlanItemStateBuilder addWaitingForRepetitionPlanItemDefinitionIds(List planItemDefinitionIds) { if (planItemDefinitionIds != null) { @@ -147,6 +159,12 @@ public ChangePlanItemStateBuilder removeWaitingForRepetitionPlanItemDefinitionId return this; } + @Override + public ChangePlanItemStateBuilder removeWaitingForRepetitionPlanItemDefinition(RemoveWaitingForRepetitionPlanItemDefinitionMapping planItemDefinition) { + removeWaitingForRepetitionPlanItemDefinitions.add(planItemDefinition); + return this; + } + @Override public ChangePlanItemStateBuilder removeWaitingForRepetitionPlanItemDefinitionIds(List planItemDefinitionIds) { if (planItemDefinitionIds != 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 a6e7a667372..2b736b344ec 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 @@ -15,10 +15,12 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.flowable.cmmn.converter.CmmnXmlConstants.ELEMENT_STAGE; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; import org.flowable.cmmn.api.history.HistoricMilestoneInstance; @@ -4795,6 +4797,945 @@ void migrateCaseInstancesWithLocalVariablesForAvailableStage() { } } + @Test + void migrateCaseInstancesWithConditionalActivate() { + // Arrange + deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/two-task.cmmn.xml"); + CaseInstance caseInstance1 = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("testCase").variable("activateTask3", true).start(); + CaseInstance caseInstance2 = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("testCase").variable("activateTask3", false).start(); + CaseDefinition destinationDefinition = deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/three-task.cmmn.xml"); + + assertThat(cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance1.getId()).count()).isEqualTo(2); + + // Act + cmmnMigrationService.createCaseInstanceMigrationBuilder() + .migrateToCaseDefinition(destinationDefinition.getId()) + .addMoveToAvailablePlanItemDefinitionMapping( + PlanItemDefinitionMappingBuilder.createMoveToAvailablePlanItemDefinitionMappingFor("humanTask3", "${activateTask3}")) + .migrateCaseInstances(caseInstance1.getCaseDefinitionId()); + + // Assert + CaseInstance caseInstance1AfterMigration = cmmnRuntimeService.createCaseInstanceQuery() + .caseInstanceId(caseInstance1.getId()) + .singleResult(); + assertThat(caseInstance1AfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + List planItemInstances1 = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance1.getId()) + .includeEnded() + .list(); + assertThat(planItemInstances1).hasSize(3); + assertThat(planItemInstances1) + .extracting(PlanItemInstance::getCaseDefinitionId) + .containsOnly(destinationDefinition.getId()); + assertThat(planItemInstances1) + .extracting(PlanItemInstance::getName) + .containsExactlyInAnyOrder("Task 1", "Task 2", "Task 3"); + assertThat(planItemInstances1) + .filteredOn("name", "Task 3") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.ACTIVE); + + CaseInstance caseInstance2AfterMigration = cmmnRuntimeService.createCaseInstanceQuery() + .caseInstanceId(caseInstance2.getId()) + .singleResult(); + assertThat(caseInstance2AfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + List planItemInstances2 = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance2.getId()) + .includeEnded() + .list(); + assertThat(planItemInstances2).hasSize(2); + assertThat(planItemInstances2) + .extracting(PlanItemInstance::getCaseDefinitionId) + .containsOnly(destinationDefinition.getId()); + assertThat(planItemInstances2) + .extracting(PlanItemInstance::getName) + .containsExactlyInAnyOrder("Task 1", "Task 2"); + } + + @Test + void migrateCaseInstancesWithConditionalTerminateAndLocalVariable() { + // Arrange + deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/two-task.cmmn.xml"); + CaseInstance caseInstance1 = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("testCase").start(); + CaseInstance caseInstance2 = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("testCase").start(); + CaseDefinition destinationDefinition = deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/second-task-with-sentry.cmmn.xml"); + + PlanItemInstance planItemInstanceCase1Task2 = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance1.getId()) + .planItemInstanceName("Task 2") + .singleResult(); + PlanItemInstance planItemInstanceCase2Task2 = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance2.getId()) + .planItemInstanceName("Task 2") + .singleResult(); + cmmnRuntimeService.setLocalVariable(planItemInstanceCase1Task2.getId(), "disableTask2", false); + cmmnRuntimeService.setLocalVariable(planItemInstanceCase2Task2.getId(), "disableTask2", true); + + assertThat(cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance1.getId()).count()).isEqualTo(2); + + // Act + cmmnMigrationService.createCaseInstanceMigrationBuilder() + .migrateToCaseDefinition(destinationDefinition.getId()) + .addTerminatePlanItemDefinitionMapping( + PlanItemDefinitionMappingBuilder.createTerminatePlanItemDefinitionMappingFor("humanTask2", "${disableTask2}")) + .migrateCaseInstances(caseInstance1.getCaseDefinitionId()); + + // Assert + CaseInstance caseInstance1AfterMigration = cmmnRuntimeService.createCaseInstanceQuery() + .caseInstanceId(caseInstance1.getId()) + .singleResult(); + assertThat(caseInstance1AfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + List planItemInstances1 = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance1.getId()) + .includeEnded() + .list(); + assertThat(planItemInstances1).hasSize(2); + assertThat(planItemInstances1) + .extracting(PlanItemInstance::getCaseDefinitionId) + .containsOnly(destinationDefinition.getId()); + assertThat(planItemInstances1) + .extracting(PlanItemInstance::getName) + .containsExactlyInAnyOrder("Task 1", "Task 2"); + assertThat(planItemInstances1) + .filteredOn("name", "Task 2") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.ACTIVE); + + CaseInstance caseInstance2AfterMigration = cmmnRuntimeService.createCaseInstanceQuery() + .caseInstanceId(caseInstance2.getId()) + .singleResult(); + assertThat(caseInstance2AfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + List planItemInstances2 = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance2.getId()) + .includeEnded() + .list(); + assertThat(planItemInstances2).hasSize(2); + assertThat(planItemInstances2) + .extracting(PlanItemInstance::getCaseDefinitionId) + .containsOnly(destinationDefinition.getId()); + assertThat(planItemInstances2) + .extracting(PlanItemInstance::getName) + .containsExactlyInAnyOrder("Task 1", "Task 2"); + assertThat(planItemInstances2) + .filteredOn("name", "Task 2") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.TERMINATED); + } + + @Test + void migrateCaseInstancesWithStageActive() { + // Arrange + deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/stage-three-tasks.cmmn.xml"); + CaseInstance caseInstance1 = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("example-stage-case").start(); + CaseInstance caseInstance2 = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("example-stage-case").start(); + CaseDefinition destinationDefinition = deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/stage-three-tasks-two-new.cmmn.xml"); + + Task task = cmmnTaskService.createTaskQuery() + .caseInstanceId(caseInstance1.getId()) + .taskName("Task 1") + .singleResult(); + cmmnTaskService.complete(task.getId()); + + assertThat(cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance1.getId()).count()).isEqualTo(2); + assertThat(cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance2.getId()).count()).isEqualTo(2); + + // Act + cmmnMigrationService.createCaseInstanceMigrationBuilder() + .migrateToCaseDefinition(destinationDefinition.getId()) + .addMoveToAvailablePlanItemDefinitionMapping(PlanItemDefinitionMappingBuilder.createMoveToAvailablePlanItemDefinitionMappingFor("task4", + "${planItemInstances.definitionId(\"cmmnStage_1\").active().exists()}")) + .addMoveToAvailablePlanItemDefinitionMapping(PlanItemDefinitionMappingBuilder.createMoveToAvailablePlanItemDefinitionMappingFor("task5", + "${planItemInstances.definitionId(\"cmmnStage_2\").active().exists()}")) + .migrateCaseInstances(caseInstance1.getCaseDefinitionId()); + + // Assert + CaseInstance caseInstance1AfterMigration = cmmnRuntimeService.createCaseInstanceQuery() + .caseInstanceId(caseInstance1.getId()) + .singleResult(); + assertThat(caseInstance1AfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + List planItemInstances1 = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance1.getId()) + .includeEnded() + .list(); + assertThat(planItemInstances1).hasSize(6); + assertThat(planItemInstances1) + .extracting(PlanItemInstance::getCaseDefinitionId) + .containsOnly(destinationDefinition.getId()); + assertThat(planItemInstances1) + .extracting(PlanItemInstance::getName) + .containsExactlyInAnyOrder("Task 2", "Task 5", "Task 3", "Stage 1", "Stage 2", "Task 1"); + assertThat(planItemInstances1) + .filteredOn("name", "Task 5") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.ACTIVE); + + assertThat(planItemInstances1) + .filteredOn("name", "Stage 1") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.COMPLETED); + assertThat(planItemInstances1) + .filteredOn("name", "Task 1") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.COMPLETED); + + CaseInstance caseInstance2AfterMigration = cmmnRuntimeService.createCaseInstanceQuery() + .caseInstanceId(caseInstance2.getId()) + .singleResult(); + assertThat(caseInstance2AfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + List planItemInstances2 = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance2.getId()) + .includeEnded() + .list(); + assertThat(planItemInstances2).hasSize(5); + assertThat(planItemInstances2) + .extracting(PlanItemInstance::getCaseDefinitionId) + .containsOnly(destinationDefinition.getId()); + assertThat(planItemInstances2) + .extracting(PlanItemInstance::getName) + .containsExactlyInAnyOrder("Task 1", "Task 4", "Task 3", "Stage 1", "Stage 2"); + assertThat(planItemInstances2) + .filteredOn("name", "Task 4") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.ACTIVE); + assertThat(planItemInstances2) + .filteredOn("name", "Stage 1") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.ACTIVE); + assertThat(planItemInstances2) + .filteredOn("name", "Stage 2") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.AVAILABLE); + } + + @Test + void migrateCaseInstancesWithStageActiveBasedOnStageCondition() { + // Arrange + deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/stage-three-tasks.cmmn.xml"); + CaseInstance caseInstance1 = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("example-stage-case").variable("activateTask", false).start(); + CaseInstance caseInstance2 = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("example-stage-case").variable("activateTask", false).start(); + CaseDefinition destinationDefinition = deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/stage-three-tasks-two-new.cmmn.xml"); + + Task task = cmmnTaskService.createTaskQuery() + .caseInstanceId(caseInstance1.getId()) + .taskName("Task 1") + .singleResult(); + cmmnTaskService.complete(task.getId()); + + // write the local variables activateTask to true to make the information available for every stage in which we are + List activeStages = cmmnRuntimeService.createPlanItemInstanceQuery() + .planItemDefinitionType(ELEMENT_STAGE) + .planItemInstanceStateActive() + .list(); + for (PlanItemInstance activeStage : activeStages) { + cmmnRuntimeService.setLocalVariable(activeStage.getId(), "activateTask", true); + } + + assertThat(cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance1.getId()).count()).isEqualTo(2); + assertThat(cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance2.getId()).count()).isEqualTo(2); + + // Act + cmmnMigrationService.createCaseInstanceMigrationBuilder() + .migrateToCaseDefinition(destinationDefinition.getId()) + // we can't assume that it's the direct parent, since it's always taking the closest active ancestor + .addActivatePlanItemDefinitionMapping(PlanItemDefinitionMappingBuilder.createActivatePlanItemDefinitionMappingFor("task4", + "${activateTask}")) + .addActivatePlanItemDefinitionMapping(PlanItemDefinitionMappingBuilder.createActivatePlanItemDefinitionMappingFor("task5", + "${activateTask}")) + .migrateCaseInstances(caseInstance1.getCaseDefinitionId()); + + // Assert + CaseInstance caseInstance1AfterMigration = cmmnRuntimeService.createCaseInstanceQuery() + .caseInstanceId(caseInstance1.getId()) + .singleResult(); + assertThat(caseInstance1AfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + List planItemInstances1 = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance1.getId()) + .includeEnded() + .list(); + assertThat(planItemInstances1).hasSize(6); + assertThat(planItemInstances1) + .extracting(PlanItemInstance::getCaseDefinitionId) + .containsOnly(destinationDefinition.getId()); + assertThat(planItemInstances1) + .extracting(PlanItemInstance::getName) + .containsExactlyInAnyOrder("Task 2", "Task 5", "Task 3", "Stage 1", "Stage 2", "Task 1"); + assertThat(planItemInstances1) + .filteredOn("name", "Task 5") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.ACTIVE); + + assertThat(planItemInstances1) + .filteredOn("name", "Stage 1") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.COMPLETED); + assertThat(planItemInstances1) + .filteredOn("name", "Task 1") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.COMPLETED); + + CaseInstance caseInstance2AfterMigration = cmmnRuntimeService.createCaseInstanceQuery() + .caseInstanceId(caseInstance2.getId()) + .singleResult(); + assertThat(caseInstance2AfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + List planItemInstances2 = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance2.getId()) + .includeEnded() + .list(); + assertThat(planItemInstances2).hasSize(5); + assertThat(planItemInstances2) + .extracting(PlanItemInstance::getCaseDefinitionId) + .containsOnly(destinationDefinition.getId()); + assertThat(planItemInstances2) + .extracting(PlanItemInstance::getName) + .containsExactlyInAnyOrder("Task 1", "Task 4", "Task 3", "Stage 1", "Stage 2"); + assertThat(planItemInstances2) + .filteredOn("name", "Task 4") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.ACTIVE); + assertThat(planItemInstances2) + .filteredOn("name", "Stage 1") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.ACTIVE); + assertThat(planItemInstances2) + .filteredOn("name", "Stage 2") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.AVAILABLE); + } + + @Test + void migrateCaseInstancesWithStageAvailableBasedOnStageCondition() { + // Arrange + deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/stage-three-tasks.cmmn.xml"); + CaseInstance caseInstance1 = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("example-stage-case").variable("activateTask", false).start(); + CaseInstance caseInstance2 = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("example-stage-case").variable("activateTask", false).start(); + CaseDefinition destinationDefinition = deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/stage-three-tasks-two-new.cmmn.xml"); + + Task task = cmmnTaskService.createTaskQuery() + .caseInstanceId(caseInstance1.getId()) + .taskName("Task 1") + .singleResult(); + cmmnTaskService.complete(task.getId()); + + // write the local variables activateTask to true to make the information available for every stage in which we are + List activeStages = cmmnRuntimeService.createPlanItemInstanceQuery() + .planItemDefinitionType(ELEMENT_STAGE) + .planItemInstanceStateActive() + .list(); + for (PlanItemInstance activeStage : activeStages) { + cmmnRuntimeService.setLocalVariable(activeStage.getId(), "activateTask", true); + } + + assertThat(cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance1.getId()).count()).isEqualTo(2); + assertThat(cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance2.getId()).count()).isEqualTo(2); + + // Act + cmmnMigrationService.createCaseInstanceMigrationBuilder() + .migrateToCaseDefinition(destinationDefinition.getId()) + // we can't assume that it's the direct parent, since it's always taking the closest active ancestor + .addMoveToAvailablePlanItemDefinitionMapping(PlanItemDefinitionMappingBuilder.createMoveToAvailablePlanItemDefinitionMappingFor("task4", + "${activateTask}")) + .addMoveToAvailablePlanItemDefinitionMapping(PlanItemDefinitionMappingBuilder.createMoveToAvailablePlanItemDefinitionMappingFor("task5", + "${activateTask}")) + .migrateCaseInstances(caseInstance1.getCaseDefinitionId()); + + // Assert + CaseInstance caseInstance1AfterMigration = cmmnRuntimeService.createCaseInstanceQuery() + .caseInstanceId(caseInstance1.getId()) + .singleResult(); + assertThat(caseInstance1AfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + List planItemInstances1 = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance1.getId()) + .includeEnded() + .list(); + assertThat(planItemInstances1).hasSize(6); + assertThat(planItemInstances1) + .extracting(PlanItemInstance::getCaseDefinitionId) + .containsOnly(destinationDefinition.getId()); + assertThat(planItemInstances1) + .extracting(PlanItemInstance::getName) + .containsExactlyInAnyOrder("Task 2", "Task 5", "Task 3", "Stage 1", "Stage 2", "Task 1"); + assertThat(planItemInstances1) + .filteredOn("name", "Task 5") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.ACTIVE); + + assertThat(planItemInstances1) + .filteredOn("name", "Stage 1") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.COMPLETED); + assertThat(planItemInstances1) + .filteredOn("name", "Task 1") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.COMPLETED); + + CaseInstance caseInstance2AfterMigration = cmmnRuntimeService.createCaseInstanceQuery() + .caseInstanceId(caseInstance2.getId()) + .singleResult(); + assertThat(caseInstance2AfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + List planItemInstances2 = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance2.getId()) + .includeEnded() + .list(); + assertThat(planItemInstances2).hasSize(5); + assertThat(planItemInstances2) + .extracting(PlanItemInstance::getCaseDefinitionId) + .containsOnly(destinationDefinition.getId()); + assertThat(planItemInstances2) + .extracting(PlanItemInstance::getName) + .containsExactlyInAnyOrder("Task 1", "Task 4", "Task 3", "Stage 1", "Stage 2"); + assertThat(planItemInstances2) + .filteredOn("name", "Task 4") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.ACTIVE); + assertThat(planItemInstances2) + .filteredOn("name", "Stage 1") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.ACTIVE); + assertThat(planItemInstances2) + .filteredOn("name", "Stage 2") + .extracting(PlanItemInstance::getState) + .containsOnly(PlanItemInstanceState.AVAILABLE); + } + + @Test + void migrateCaseInstancesWithRepetitionAndStageVariable() { + // Arrange + deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/stage-with-user-event-listener-and-task.cmmn.xml"); + CaseInstance caseInstance1 = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("repetitionCase").start(); + CaseInstance caseInstance2 = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("repetitionCase").start(); + CaseDefinition destinationDefinition = deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/stage-with-user-event-listener-and-task-with-repetition.cmmn.xml"); + + PlanItemInstance caseInstance1Stage = cmmnRuntimeService.createPlanItemInstanceQuery() + .planItemInstanceName("Stage") + .caseInstanceId(caseInstance1.getId()) + .singleResult(); + cmmnRuntimeService.setLocalVariable(caseInstance1Stage.getId(), "addRepetition", true); + + PlanItemInstance userEventListenerPlanItemInstance1 = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance1.getId()) + .planItemDefinitionId("userEventListener") + .singleResult(); + cmmnRuntimeService.triggerPlanItemInstance(userEventListenerPlanItemInstance1.getId()); + + PlanItemInstance caseInstance2Stage = cmmnRuntimeService.createPlanItemInstanceQuery() + .planItemInstanceName("Stage") + .caseInstanceId(caseInstance2.getId()) + .singleResult(); + cmmnRuntimeService.setLocalVariable(caseInstance2Stage.getId(), "addRepetition", false); + + // Act + cmmnMigrationService.createCaseInstanceMigrationBuilder() + .migrateToCaseDefinition(destinationDefinition.getId()) + .addWaitingForRepetitionPlanItemDefinitionMapping( + PlanItemDefinitionMappingBuilder.createWaitingForRepetitionPlanItemDefinitionMappingFor("humanTask", "${addRepetition}") + ) + .migrateCaseInstances(caseInstance1.getCaseDefinitionId()); + + // Assert + CaseInstance caseInstance1AfterMigration = cmmnRuntimeService.createCaseInstanceQuery() + .caseInstanceId(caseInstance1.getId()) + .singleResult(); + assertThat(caseInstance1AfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + List planItemInstances1 = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance1.getId()) + .includeEnded() + .list(); + assertThat(planItemInstances1).hasSize(4); + assertThat(planItemInstances1) + .extracting(PlanItemInstance::getCaseDefinitionId) + .containsOnly(destinationDefinition.getId()); + assertThat(planItemInstances1) + .extracting(PlanItemInstance::getName) + .containsExactlyInAnyOrder("Stage", "User Event Listener", "Human Task", "Human Task"); + assertThat(planItemInstances1) + .filteredOn("name", "Human Task") + .extracting(PlanItemInstance::getState) + .containsExactlyInAnyOrder(PlanItemInstanceState.ACTIVE, PlanItemInstanceState.WAITING_FOR_REPETITION); + + CaseInstance caseInstance2AfterMigration = cmmnRuntimeService.createCaseInstanceQuery() + .caseInstanceId(caseInstance2.getId()) + .singleResult(); + assertThat(caseInstance2AfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + List planItemInstances2 = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance2.getId()) + .includeEnded() + .list(); + assertThat(planItemInstances2).hasSize(3); + assertThat(planItemInstances2) + .extracting(PlanItemInstance::getCaseDefinitionId) + .containsOnly(destinationDefinition.getId()); + assertThat(planItemInstances2) + .extracting(PlanItemInstance::getName) + .containsExactlyInAnyOrder("Stage", "User Event Listener", "Human Task"); + assertThat(planItemInstances2) + .filteredOn("name", "Human Task") + .extracting(PlanItemInstance::getState) + .containsExactlyInAnyOrder(PlanItemInstanceState.AVAILABLE); + } + + @Test + void migrateCaseInstancesWithRepetitionAndStageVariableAndInactiveHumanTask() { + // Arrange + deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/stage-with-user-event-listener-and-task.cmmn.xml"); + CaseInstance caseInstance1 = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("repetitionCase").start(); + CaseInstance caseInstance2 = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("repetitionCase").start(); + CaseDefinition destinationDefinition = deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/stage-with-user-event-listener-and-task-with-repetition.cmmn.xml"); + + PlanItemInstance caseInstance1Stage = cmmnRuntimeService.createPlanItemInstanceQuery() + .planItemInstanceName("Stage") + .caseInstanceId(caseInstance1.getId()) + .singleResult(); + cmmnRuntimeService.setLocalVariable(caseInstance1Stage.getId(), "addRepetition", true); + + PlanItemInstance caseInstance2Stage = cmmnRuntimeService.createPlanItemInstanceQuery() + .planItemInstanceName("Stage") + .caseInstanceId(caseInstance2.getId()) + .singleResult(); + cmmnRuntimeService.setLocalVariable(caseInstance2Stage.getId(), "addRepetition", false); + + // Act + cmmnMigrationService.createCaseInstanceMigrationBuilder() + .migrateToCaseDefinition(destinationDefinition.getId()) + .addWaitingForRepetitionPlanItemDefinitionMapping( + PlanItemDefinitionMappingBuilder.createWaitingForRepetitionPlanItemDefinitionMappingFor("humanTask", "${addRepetition}") + ) + .migrateCaseInstances(caseInstance1.getCaseDefinitionId()); + + // Assert + CaseInstance caseInstance1AfterMigration = cmmnRuntimeService.createCaseInstanceQuery() + .caseInstanceId(caseInstance1.getId()) + .singleResult(); + assertThat(caseInstance1AfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + List planItemInstances1 = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance1.getId()) + .includeEnded() + .list(); + assertThat(planItemInstances1).hasSize(4); + assertThat(planItemInstances1) + .extracting(PlanItemInstance::getCaseDefinitionId) + .containsOnly(destinationDefinition.getId()); + assertThat(planItemInstances1) + .extracting(PlanItemInstance::getName) + .containsExactlyInAnyOrder("Stage", "User Event Listener", "Human Task", "Human Task"); + assertThat(planItemInstances1) + .filteredOn("name", "Human Task") + .extracting(PlanItemInstance::getState) + .containsExactlyInAnyOrder(PlanItemInstanceState.AVAILABLE, PlanItemInstanceState.WAITING_FOR_REPETITION); + + CaseInstance caseInstance2AfterMigration = cmmnRuntimeService.createCaseInstanceQuery() + .caseInstanceId(caseInstance2.getId()) + .singleResult(); + assertThat(caseInstance2AfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + List planItemInstances2 = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance2.getId()) + .includeEnded() + .list(); + assertThat(planItemInstances2).hasSize(3); + assertThat(planItemInstances2) + .extracting(PlanItemInstance::getCaseDefinitionId) + .containsOnly(destinationDefinition.getId()); + assertThat(planItemInstances2) + .extracting(PlanItemInstance::getName) + .containsExactlyInAnyOrder("Stage", "User Event Listener", "Human Task"); + assertThat(planItemInstances2) + .filteredOn("name", "Human Task") + .extracting(PlanItemInstance::getState) + .containsExactlyInAnyOrder(PlanItemInstanceState.AVAILABLE); + } + + @Test + void migrateCaseInstancesWithRepetitionAndPlanItemVariable() { + // Arrange + deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/stage-with-user-event-listener-and-task.cmmn.xml"); + CaseInstance caseInstance1 = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("repetitionCase").start(); + CaseInstance caseInstance2 = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("repetitionCase").start(); + CaseDefinition destinationDefinition = deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/stage-with-user-event-listener-and-task-with-repetition.cmmn.xml"); + + PlanItemInstance humanTaskCase1 = cmmnRuntimeService.createPlanItemInstanceQuery() + .planItemInstanceName("Human Task") + .caseInstanceId(caseInstance1.getId()) + .singleResult(); + cmmnRuntimeService.setLocalVariable(humanTaskCase1.getId(), "addRepetition", true); + + PlanItemInstance userEventListenerPlanItemInstance1 = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance1.getId()) + .planItemDefinitionId("userEventListener") + .singleResult(); + cmmnRuntimeService.triggerPlanItemInstance(userEventListenerPlanItemInstance1.getId()); + + PlanItemInstance caseInstance2Stage = cmmnRuntimeService.createPlanItemInstanceQuery() + .planItemInstanceName("Stage") + .caseInstanceId(caseInstance2.getId()) + .singleResult(); + cmmnRuntimeService.setLocalVariable(caseInstance2Stage.getId(), "addRepetition", false); + + // Act + cmmnMigrationService.createCaseInstanceMigrationBuilder() + .migrateToCaseDefinition(destinationDefinition.getId()) + .addWaitingForRepetitionPlanItemDefinitionMapping( + PlanItemDefinitionMappingBuilder.createWaitingForRepetitionPlanItemDefinitionMappingFor("humanTask", "${addRepetition}") + ) + .migrateCaseInstances(caseInstance1.getCaseDefinitionId()); + + // Assert + CaseInstance caseInstance1AfterMigration = cmmnRuntimeService.createCaseInstanceQuery() + .caseInstanceId(caseInstance1.getId()) + .singleResult(); + assertThat(caseInstance1AfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + List planItemInstances1 = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance1.getId()) + .includeEnded() + .list(); + assertThat(planItemInstances1).hasSize(4); + assertThat(planItemInstances1) + .extracting(PlanItemInstance::getCaseDefinitionId) + .containsOnly(destinationDefinition.getId()); + assertThat(planItemInstances1) + .extracting(PlanItemInstance::getName) + .containsExactlyInAnyOrder("Stage", "User Event Listener", "Human Task", "Human Task"); + assertThat(planItemInstances1) + .filteredOn("name", "Human Task") + .extracting(PlanItemInstance::getState) + .containsExactlyInAnyOrder(PlanItemInstanceState.ACTIVE, PlanItemInstanceState.WAITING_FOR_REPETITION); + + CaseInstance caseInstance2AfterMigration = cmmnRuntimeService.createCaseInstanceQuery() + .caseInstanceId(caseInstance2.getId()) + .singleResult(); + assertThat(caseInstance2AfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + List planItemInstances2 = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance2.getId()) + .includeEnded() + .list(); + assertThat(planItemInstances2).hasSize(3); + assertThat(planItemInstances2) + .extracting(PlanItemInstance::getCaseDefinitionId) + .containsOnly(destinationDefinition.getId()); + assertThat(planItemInstances2) + .extracting(PlanItemInstance::getName) + .containsExactlyInAnyOrder("Stage", "User Event Listener", "Human Task"); + assertThat(planItemInstances2) + .filteredOn("name", "Human Task") + .extracting(PlanItemInstance::getState) + .containsExactlyInAnyOrder(PlanItemInstanceState.AVAILABLE); + } + + @Test + void migrateCaseInstancesWithTerminateBasedOnCondition() { + // Arrange + deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/two-task-repetition.cmmn.xml"); + CaseInstance caseInstance1 = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("testCase").start(); + CaseInstance caseInstance2 = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("testCase").start(); + CaseDefinition destinationDefinition = deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/two-task.cmmn.xml"); + + Task task = cmmnTaskService.createTaskQuery() + .caseInstanceId(caseInstance1.getId()) + .taskName("Task 2") + .singleResult(); + cmmnTaskService.complete(task.getId()); + + List planItemInstances1Before = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance1.getId()) + .includeEnded() + .list(); + assertThat(planItemInstances1Before) + .filteredOn("name", "Task 2") + .hasSize(2) + .extracting(PlanItemInstance::getState) + .containsExactlyInAnyOrder(PlanItemInstanceState.ACTIVE, PlanItemInstanceState.COMPLETED); + + assertThat(cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance1.getId()).count()).isEqualTo(2); + assertThat(cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance2.getId()).count()).isEqualTo(2); + + // Act + cmmnMigrationService.createCaseInstanceMigrationBuilder() + .migrateToCaseDefinition(destinationDefinition.getId()) + .addTerminatePlanItemDefinitionMapping(PlanItemDefinitionMappingBuilder.createTerminatePlanItemDefinitionMappingFor("humanTask2", + "${planItemInstances.definitionId(\"humanTask2\").completed().exists()}")) + .migrateCaseInstances(caseInstance1.getCaseDefinitionId()); + + // Assert + CaseInstance caseInstance1AfterMigration = cmmnRuntimeService.createCaseInstanceQuery() + .caseInstanceId(caseInstance1.getId()) + .singleResult(); + assertThat(caseInstance1AfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + List planItemInstances1 = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance1.getId()) + .includeEnded() + .list(); + assertThat(planItemInstances1).hasSize(3); + assertThat(planItemInstances1) + .extracting(PlanItemInstance::getCaseDefinitionId) + .containsOnly(destinationDefinition.getId()); + assertThat(planItemInstances1) + .extracting(PlanItemInstance::getName) + .containsExactlyInAnyOrder("Task 1", "Task 2", "Task 2"); + assertThat(planItemInstances1) + .filteredOn("name", "Task 2") + .extracting(PlanItemInstance::getState) + .containsExactlyInAnyOrder(PlanItemInstanceState.TERMINATED, PlanItemInstanceState.COMPLETED); + + CaseInstance caseInstance2AfterMigration = cmmnRuntimeService.createCaseInstanceQuery() + .caseInstanceId(caseInstance2.getId()) + .singleResult(); + assertThat(caseInstance2AfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + List planItemInstances2 = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance2.getId()) + .includeEnded() + .list(); + assertThat(planItemInstances2).hasSize(2); + assertThat(planItemInstances2) + .extracting(PlanItemInstance::getCaseDefinitionId) + .containsOnly(destinationDefinition.getId()); + assertThat(planItemInstances2) + .extracting(PlanItemInstance::getName) + .containsExactlyInAnyOrder("Task 1", "Task 2"); + assertThat(planItemInstances2) + .filteredOn("name", "Task 2") + .extracting(PlanItemInstance::getState) + .containsExactlyInAnyOrder(PlanItemInstanceState.ACTIVE); + } + + @Test + void migrateCaseInstancesWithRepetitionRemoveWaitingForRepetitionAndPlanItemVariable() { + // Arrange + deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/stage-with-user-event-listener-and-task-with-repetition.cmmn.xml"); + CaseInstance caseInstance1 = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("repetitionCase").start(); + CaseInstance caseInstance2 = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("repetitionCase").start(); + CaseDefinition destinationDefinition = deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/stage-with-user-event-listener-and-task.cmmn.xml"); + + PlanItemInstance userEventListenerPlanItemInstance1 = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance1.getId()) + .planItemDefinitionId("userEventListener") + .singleResult(); + cmmnRuntimeService.triggerPlanItemInstance(userEventListenerPlanItemInstance1.getId()); + + PlanItemInstance humanTaskCase1 = cmmnRuntimeService.createPlanItemInstanceQuery() + .planItemInstanceName("Human Task") + .caseInstanceId(caseInstance1.getId()) + .planItemInstanceStateWaitingForRepetition() + .singleResult(); + cmmnRuntimeService.setLocalVariable(humanTaskCase1.getId(), "removeRepetition", true); + + PlanItemInstance userEventListenerPlanItemInstance2 = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance2.getId()) + .planItemDefinitionId("userEventListener") + .singleResult(); + cmmnRuntimeService.triggerPlanItemInstance(userEventListenerPlanItemInstance2.getId()); + + PlanItemInstance humanTaskCase2 = cmmnRuntimeService.createPlanItemInstanceQuery() + .planItemInstanceName("Human Task") + .caseInstanceId(caseInstance2.getId()) + .planItemInstanceStateWaitingForRepetition() + .singleResult(); + cmmnRuntimeService.setLocalVariable(humanTaskCase2.getId(), "removeRepetition", false); + + // Act + cmmnMigrationService.createCaseInstanceMigrationBuilder() + .migrateToCaseDefinition(destinationDefinition.getId()) + .removeWaitingForRepetitionPlanItemDefinitionMapping( + PlanItemDefinitionMappingBuilder.createRemoveWaitingForRepetitionPlanItemDefinitionMappingFor("humanTask", "${removeRepetition}") + ) + .migrateCaseInstances(caseInstance1.getCaseDefinitionId()); + + // Assert + CaseInstance caseInstance1AfterMigration = cmmnRuntimeService.createCaseInstanceQuery() + .caseInstanceId(caseInstance1.getId()) + .singleResult(); + assertThat(caseInstance1AfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + List planItemInstances1 = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance1.getId()) + .includeEnded() + .list(); + assertThat(planItemInstances1) + .extracting(PlanItemInstance::getCaseDefinitionId) + .containsOnly(destinationDefinition.getId()); + assertThat(planItemInstances1) + .extracting(PlanItemInstance::getName) + // User event listener is twice since we triggered it once already + .containsExactlyInAnyOrder("Stage", "User Event Listener", "User Event Listener", "Human Task"); + assertThat(planItemInstances1) + .filteredOn("name", "Human Task") + .extracting(PlanItemInstance::getState) + .containsExactlyInAnyOrder(PlanItemInstanceState.ACTIVE); + + CaseInstance caseInstance2AfterMigration = cmmnRuntimeService.createCaseInstanceQuery() + .caseInstanceId(caseInstance2.getId()) + .singleResult(); + assertThat(caseInstance2AfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + List planItemInstances2 = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance2.getId()) + .includeEnded() + .list(); + assertThat(planItemInstances2) + .extracting(PlanItemInstance::getCaseDefinitionId) + .containsOnly(destinationDefinition.getId()); + assertThat(planItemInstances2) + .extracting(PlanItemInstance::getName) + .containsExactlyInAnyOrder("Stage", "User Event Listener", "User Event Listener", "Human Task", "Human Task"); + assertThat(planItemInstances2) + .filteredOn("name", "Human Task") + .extracting(PlanItemInstance::getState) + .containsExactlyInAnyOrder(PlanItemInstanceState.ACTIVE, PlanItemInstanceState.WAITING_FOR_REPETITION); + } + + + @Test + void migrateCaseInstancesWithRemoveWaitingForRepetitionBasedOnCondition() { + // Arrange + deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/user-event-listener-with-repetition.cmmn.xml"); + CaseInstance caseInstance1 = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("stageCase").start(); + CaseInstance caseInstance2 = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("stageCase").start(); + CaseDefinition destinationDefinition = deployCaseDefinition("test1", + "org/flowable/cmmn/test/migration/user-event-listener-without-repetition.cmmn.xml"); + + String caseInstanceId1 = caseInstance1.getId(); + PlanItemInstance userEventListener = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstanceId1) + .planItemDefinitionId("userEventListener") + .singleResult(); + cmmnRuntimeService.createPlanItemInstanceTransitionBuilder(userEventListener.getId()) + .trigger(); + + List planItemInstances1Before = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstanceId1) + .includeEnded() + .list(); + assertThat(planItemInstances1Before) + .filteredOn("name", "Task 1") + .hasSize(2) + .extracting(PlanItemInstance::getState) + .containsExactlyInAnyOrder(PlanItemInstanceState.WAITING_FOR_REPETITION, PlanItemInstanceState.ACTIVE); + + assertThat(cmmnTaskService.createTaskQuery().caseInstanceId(caseInstanceId1).count()).isEqualTo(2); + assertThat(cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance2.getId()).count()).isEqualTo(1); + + // Act + String condition = "${planItemInstances.definitionId(\"humanTask1\").waitingForRepetition().exists()}"; + cmmnMigrationService.createCaseInstanceMigrationBuilder() + .migrateToCaseDefinition(destinationDefinition.getId()) + .addTerminatePlanItemDefinitionMapping(PlanItemDefinitionMappingBuilder.createTerminatePlanItemDefinitionMappingFor("userEventListener", + condition)) // end the event listener first + .removeWaitingForRepetitionPlanItemDefinitionMapping( + PlanItemDefinitionMappingBuilder.createRemoveWaitingForRepetitionPlanItemDefinitionMappingFor("humanTask1", condition)) + .migrateCaseInstances(caseInstance1.getCaseDefinitionId()); + + // Assert + CaseInstance caseInstance1AfterMigration = cmmnRuntimeService.createCaseInstanceQuery() + .caseInstanceId(caseInstanceId1) + .singleResult(); + assertThat(caseInstance1AfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + List planItemInstances1 = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstanceId1) + .includeEnded() + .list(); + assertThat(planItemInstances1) + .extracting(PlanItemInstance::getCaseDefinitionId) + .containsOnly(destinationDefinition.getId()); + assertThat(planItemInstances1) + .extracting(PlanItemInstance::getName) + .filteredOn(Objects::nonNull) + .containsExactlyInAnyOrder("Task 1", "Task 2"); // waiting for repetition isn't visible anymore, thus Task 1 is only once in here + assertThat(planItemInstances1) + .filteredOn("name", "Task 1") + .extracting(PlanItemInstance::getState) + .containsExactlyInAnyOrder(PlanItemInstanceState.ACTIVE); + assertThat(planItemInstances1) + .filteredOn("planItemDefinitionId", "userEventListener") + .extracting(PlanItemInstance::getState) + .containsExactlyInAnyOrder(PlanItemInstanceState.TERMINATED, PlanItemInstanceState.COMPLETED); + + CaseInstance caseInstance2AfterMigration = cmmnRuntimeService.createCaseInstanceQuery() + .caseInstanceId(caseInstance2.getId()) + .singleResult(); + assertThat(caseInstance2AfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + List planItemInstances2 = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance2.getId()) + .includeEnded() + .list(); + assertThat(planItemInstances2) + .extracting(PlanItemInstance::getCaseDefinitionId) + .containsOnly(destinationDefinition.getId()); + assertThat(planItemInstances2) + .extracting(PlanItemInstance::getName) + .filteredOn(Objects::nonNull) + .containsExactlyInAnyOrder("Task 1", "Task 2"); + assertThat(planItemInstances2) + .filteredOn("name", "Task 1") + .extracting(PlanItemInstance::getState) + .containsExactlyInAnyOrder(PlanItemInstanceState.AVAILABLE); + assertThat(planItemInstances2) + .filteredOn("planItemDefinitionId", "userEventListener") + .extracting(PlanItemInstance::getState) + .containsExactlyInAnyOrder(PlanItemInstanceState.AVAILABLE); + } + + @Test + void migrateCaseInstancesWithMarkAsActiveBasedOnCondition() { + // Arrange + deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/stage-without-event-listener-inside.cmmn.xml"); + CaseInstance caseInstance1 = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("testCase").start(); + CaseInstance caseInstance2 = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("testCase").start(); + CaseDefinition destinationDefinition = deployCaseDefinition("test1", "org/flowable/cmmn/test/migration/stage-with-event-listener-inside.cmmn.xml"); + + String caseInstanceId1 = caseInstance1.getId(); + + Task task = cmmnTaskService.createTaskQuery() + .caseInstanceId(caseInstanceId1) + .taskName("Task 1") + .singleResult(); + cmmnTaskService.complete(task.getId()); + // now the stage is active + + assertThat(cmmnTaskService.createTaskQuery().caseInstanceId(caseInstanceId1).count()).isEqualTo(1); // Task 2 + assertThat(cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance2.getId()).count()).isEqualTo(1); // Task 1 + + // Act + cmmnMigrationService.createCaseInstanceMigrationBuilder() + .migrateToCaseDefinition(destinationDefinition.getId()) + .addMoveToAvailablePlanItemDefinitionMapping(PlanItemDefinitionMappingBuilder.createMoveToAvailablePlanItemDefinitionMappingFor( + "eventListener", + "${planItemInstances.definitionId(\"cmmnStage_6\").active().exists()}" + )) + .migrateCaseInstances(caseInstance1.getCaseDefinitionId()); + + // Assert + CaseInstance caseInstance1AfterMigration = cmmnRuntimeService.createCaseInstanceQuery() + .caseInstanceId(caseInstanceId1) + .singleResult(); + assertThat(caseInstance1AfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + List planItemInstances1 = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstanceId1) + .includeEnded() + .list(); + assertThat(planItemInstances1) + .extracting(PlanItemInstance::getCaseDefinitionId) + .containsOnly(destinationDefinition.getId()); + assertThat(planItemInstances1) + .extracting(PlanItemInstance::getName) + .filteredOn(Objects::nonNull) + .contains("Event Listener"); + assertThat(planItemInstances1) + .filteredOn("name", "Event Listener") + .extracting(PlanItemInstance::getState) + .containsExactlyInAnyOrder(PlanItemInstanceState.AVAILABLE); + + CaseInstance caseInstance2AfterMigration = cmmnRuntimeService.createCaseInstanceQuery() + .caseInstanceId(caseInstance2.getId()) + .singleResult(); + assertThat(caseInstance2AfterMigration.getCaseDefinitionId()).isEqualTo(destinationDefinition.getId()); + List planItemInstances2 = cmmnRuntimeService.createPlanItemInstanceQuery() + .caseInstanceId(caseInstance2.getId()) + .includeEnded() + .list(); + assertThat(planItemInstances2) + .extracting(PlanItemInstance::getCaseDefinitionId) + .containsOnly(destinationDefinition.getId()); + assertThat(planItemInstances2) + .extracting(PlanItemInstance::getName) + .filteredOn(Objects::nonNull) + .doesNotContain("Event Listener"); + } + protected class CustomTenantProvider implements DefaultTenantProvider { @Override diff --git a/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-three-tasks-two-new.cmmn.xml b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-three-tasks-two-new.cmmn.xml new file mode 100644 index 00000000000..d91a681b365 --- /dev/null +++ b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-three-tasks-two-new.cmmn.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + complete + + + + + + + + + + + + + + + + + + diff --git a/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-three-tasks.cmmn.xml b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-three-tasks.cmmn.xml new file mode 100644 index 00000000000..83ff764a94c --- /dev/null +++ b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-three-tasks.cmmn.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + complete + + + + + + + + + + + + + + diff --git a/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-with-event-listener-inside.cmmn.xml b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-with-event-listener-inside.cmmn.xml new file mode 100644 index 00000000000..0c076e089b1 --- /dev/null +++ b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-with-event-listener-inside.cmmn.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + complete + + + + + + + + + + + occur + + + + + + + + + + diff --git a/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-with-user-event-listener-and-task-with-repetition.cmmn.xml b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-with-user-event-listener-and-task-with-repetition.cmmn.xml new file mode 100644 index 00000000000..f7878a67c88 --- /dev/null +++ b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-with-user-event-listener-and-task-with-repetition.cmmn.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + occur + + + + + + + + diff --git a/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-with-user-event-listener-and-task.cmmn.xml b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-with-user-event-listener-and-task.cmmn.xml new file mode 100644 index 00000000000..cc582683fb5 --- /dev/null +++ b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-with-user-event-listener-and-task.cmmn.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + occur + + + + + + + + diff --git a/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-without-event-listener-inside.cmmn.xml b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-without-event-listener-inside.cmmn.xml new file mode 100644 index 00000000000..b383efa5ef4 --- /dev/null +++ b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/stage-without-event-listener-inside.cmmn.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + complete + + + + + + + + + + diff --git a/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/two-task-repetition.cmmn.xml b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/two-task-repetition.cmmn.xml new file mode 100644 index 00000000000..1fae9fcc6f4 --- /dev/null +++ b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/two-task-repetition.cmmn.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + diff --git a/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/user-event-listener-with-repetition.cmmn.xml b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/user-event-listener-with-repetition.cmmn.xml new file mode 100644 index 00000000000..d2b639d4280 --- /dev/null +++ b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/user-event-listener-with-repetition.cmmn.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + occur + + + + + + + + diff --git a/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/user-event-listener-without-repetition.cmmn.xml b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/user-event-listener-without-repetition.cmmn.xml new file mode 100644 index 00000000000..976ae017a90 --- /dev/null +++ b/modules/flowable-cmmn-engine/src/test/resources/org/flowable/cmmn/test/migration/user-event-listener-without-repetition.cmmn.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + occur + + + + + + + +