From 82885732bd4e6066b390a5f123d749d44c5edacd Mon Sep 17 00:00:00 2001 From: Elias Ricken de Medeiros <26007058+erdemedeiros@users.noreply.github.com> Date: Tue, 21 Dec 2021 12:30:06 +0100 Subject: [PATCH] Fix variable propagation for user tasks (#3788) In order to keep consistency with service tasks, we also need to update the current execution variables in addition to propagate them to process variables. While evaluating expressions on sequence flows, variables set in the current execution will have higher priority over process variables. Fixes https://github.com/Activiti/Activiti/issues/3787 --- .../behavior/UserTaskActivityBehavior.java | 6 +- .../spring/boot/RuntimeTestConfiguration.java | 8 ++ .../tasks/TaskRuntimeVariableMappingIT.java | 58 ++++++++++++ ...variable-mapping-with-loop-extensions.json | 50 +++++++++++ .../variable-mapping-with-loop.bpmn20.xml | 89 +++++++++++++++++++ 5 files changed, 207 insertions(+), 4 deletions(-) create mode 100644 activiti-core/activiti-spring-boot-starter/src/test/resources/processes/variable-mapping-with-loop-extensions.json create mode 100644 activiti-core/activiti-spring-boot-starter/src/test/resources/processes/variable-mapping-with-loop.bpmn20.xml diff --git a/activiti-core/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/UserTaskActivityBehavior.java b/activiti-core/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/UserTaskActivityBehavior.java index 3458de709e..304d031c73 100755 --- a/activiti-core/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/UserTaskActivityBehavior.java +++ b/activiti-core/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/UserTaskActivityBehavior.java @@ -52,9 +52,6 @@ import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -/** - - */ public class UserTaskActivityBehavior extends TaskActivityBehavior { private static final long serialVersionUID = 1L; @@ -255,7 +252,7 @@ public class UserTaskActivityBehavior extends TaskActivityBehavior { protected Map calculateOutBoundVariables(DelegateExecution execution, Map taskVariables) { CommandContext commandContext = Context.getCommandContext(); - if(commandContext.getProcessEngineConfiguration().isCopyVariablesToLocalForTasks()){ + if (commandContext.getProcessEngineConfiguration().isCopyVariablesToLocalForTasks()) { return taskVariables; } return emptyMap(); @@ -294,6 +291,7 @@ public class UserTaskActivityBehavior extends TaskActivityBehavior { Map outboundVariables = calculateOutBoundVariables(execution, taskVariables); processInstanceEntity.setVariables(outboundVariables); + execution.setVariablesLocal(outboundVariables); } } diff --git a/activiti-core/activiti-spring-boot-starter/src/test/java/org/activiti/spring/boot/RuntimeTestConfiguration.java b/activiti-core/activiti-spring-boot-starter/src/test/java/org/activiti/spring/boot/RuntimeTestConfiguration.java index d46c345bde..c81741b515 100644 --- a/activiti-core/activiti-spring-boot-starter/src/test/java/org/activiti/spring/boot/RuntimeTestConfiguration.java +++ b/activiti-core/activiti-spring-boot-starter/src/test/java/org/activiti/spring/boot/RuntimeTestConfiguration.java @@ -162,6 +162,14 @@ public class RuntimeTestConfiguration { }; } + @Bean(name = "value-processor.process") + public Connector valueProcessorConnector() { + return integrationContext -> { + integrationContext.addOutBoundVariable("providedValue",integrationContext.getInBoundVariable("input")); + return integrationContext; + }; + } + @Bean(name = "Tag Image Connector.tagImageActionName") public Connector tagImageActionName() { return integrationContext -> { diff --git a/activiti-core/activiti-spring-boot-starter/src/test/java/org/activiti/spring/boot/tasks/TaskRuntimeVariableMappingIT.java b/activiti-core/activiti-spring-boot-starter/src/test/java/org/activiti/spring/boot/tasks/TaskRuntimeVariableMappingIT.java index c9a9209c50..603d4b042c 100644 --- a/activiti-core/activiti-spring-boot-starter/src/test/java/org/activiti/spring/boot/tasks/TaskRuntimeVariableMappingIT.java +++ b/activiti-core/activiti-spring-boot-starter/src/test/java/org/activiti/spring/boot/tasks/TaskRuntimeVariableMappingIT.java @@ -15,8 +15,10 @@ */ package org.activiti.spring.boot.tasks; +import static java.util.Collections.singletonMap; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; +import static org.awaitility.Awaitility.await; import java.util.Date; import java.util.HashMap; @@ -27,6 +29,7 @@ import org.activiti.api.process.model.ProcessInstance; import org.activiti.api.task.model.Task; import org.activiti.common.util.DateFormatterProvider; import org.activiti.spring.boot.process.ProcessBaseRuntime; +import org.activiti.spring.boot.security.util.SecurityUtil; import org.activiti.spring.boot.test.util.ProcessCleanUpUtil; import org.activiti.spring.boot.test.util.TaskCleanUpUtil; import org.junit.jupiter.api.AfterEach; @@ -49,6 +52,9 @@ public class TaskRuntimeVariableMappingIT { private static final String TASK_MAP_ALL_PREVALENCE = "taskVariableMappingSendAllPrevalence"; + @Autowired + private SecurityUtil securityUtil; + @Autowired private TaskBaseRuntime taskBaseRuntime; @@ -459,4 +465,56 @@ public class TaskRuntimeVariableMappingIT { assertThat(tasks).hasSize(1); return tasks.get(0); } + + @Test + public void should_supportVariableMappingAfterLoopingBack() { + //given + final ProcessInstance processInstance = processBaseRuntime.startProcessWithProcessDefinitionKey("Process_N4qkN051N"); + List tasks = taskBaseRuntime.getTasksByProcessInstanceId(processInstance.getId()); + assertThat(tasks) + .extracting(Task::getName) + .containsExactly("Enter values"); + + //when the task completes with a variable value causing a loop back + taskBaseRuntime.completeTask(tasks.get(0), singletonMap("formInput", "provided-it1")); + + //then process loops back to the first task + waitForTaskOnProcessInstance(processInstance, "Enter values"); + List variables = processBaseRuntime.getVariables(processInstance); + assertThat(variables) + .extracting( + VariableInstance::getName, + VariableInstance::getValue + ).containsExactly( + tuple("providedValue", "provided-it1") + ); + + //when the task completes with a variable value not causing a loop back + tasks = taskBaseRuntime.getTasksByProcessInstanceId( + processInstance.getId()); + taskBaseRuntime.completeTask(tasks.get(0), + singletonMap("formInput", "go")); + + //then the process reaches the next task + waitForTaskOnProcessInstance(processInstance, "Wait"); + variables = processBaseRuntime.getVariables(processInstance); + assertThat(variables) + .extracting( + VariableInstance::getName, + VariableInstance::getValue + ).containsExactly( + tuple("providedValue", "go") + ); + + } + + private void waitForTaskOnProcessInstance(ProcessInstance processInstance, String taskName) { + await().untilAsserted(() -> { + securityUtil.logInAs("user"); + assertThat(taskBaseRuntime.getTasksByProcessInstanceId(processInstance.getId())) + .extracting(Task::getName) + .containsExactly(taskName); + }); + } + } diff --git a/activiti-core/activiti-spring-boot-starter/src/test/resources/processes/variable-mapping-with-loop-extensions.json b/activiti-core/activiti-spring-boot-starter/src/test/resources/processes/variable-mapping-with-loop-extensions.json new file mode 100644 index 0000000000..d1e6726ea2 --- /dev/null +++ b/activiti-core/activiti-spring-boot-starter/src/test/resources/processes/variable-mapping-with-loop-extensions.json @@ -0,0 +1,50 @@ +{ + "id": "process-fdee92a0-5a2b-48be-8696-245eff49aa07", + "type": "PROCESS", + "name": "variable-mapping-with-loop", + "extensions": { + "Process_N4qkN051N": { + "constants": {}, + "mappings": { + "Task_0od1320": { + "outputs": { + "providedValue": { + "type": "variable", + "value": "providedValue" + } + }, + "inputs": { + "input": { + "type": "variable", + "value": "providedValue" + } + } + }, + "Task_125yjke": { + "outputs": { + "providedValue": { + "type": "variable", + "value": "formInput" + } + } + } + }, + "properties": { + "e974d84d-aa76-4acb-903d-97582fe89861": { + "id": "e974d84d-aa76-4acb-903d-97582fe89861", + "name": "providedValue", + "type": "string", + "value": "", + "required": false + } + }, + "assignments": { + "Task_125yjke": { + "type": "identity", + "assignment": "assignee", + "id": "Task_125yjke" + } + } + } + } +} diff --git a/activiti-core/activiti-spring-boot-starter/src/test/resources/processes/variable-mapping-with-loop.bpmn20.xml b/activiti-core/activiti-spring-boot-starter/src/test/resources/processes/variable-mapping-with-loop.bpmn20.xml new file mode 100644 index 0000000000..bd438dab9c --- /dev/null +++ b/activiti-core/activiti-spring-boot-starter/src/test/resources/processes/variable-mapping-with-loop.bpmn20.xml @@ -0,0 +1,89 @@ + + + + + + SequenceFlow_1anr2ek + + + + SequenceFlow_1anr2ek + SequenceFlow_0g8zsg1 + SequenceFlow_0urignn + + + + SequenceFlow_03x97kf + SequenceFlow_1xda66c + SequenceFlow_0g8zsg1 + + + + SequenceFlow_1b0cog0 + + + ${(providedValue eq "go")} + + + + SequenceFlow_0urignn + SequenceFlow_03x97kf + + + SequenceFlow_1xda66c + SequenceFlow_1b0cog0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- GitLab