diff --git a/activiti-api-impl/activiti-api-process-runtime-impl/src/main/java/org/activiti/runtime/api/impl/MappingAwareActivityBehaviorFactory.java b/activiti-api-impl/activiti-api-process-runtime-impl/src/main/java/org/activiti/runtime/api/impl/MappingAwareActivityBehaviorFactory.java new file mode 100644 index 00000000000..9a70de0314c --- /dev/null +++ b/activiti-api-impl/activiti-api-process-runtime-impl/src/main/java/org/activiti/runtime/api/impl/MappingAwareActivityBehaviorFactory.java @@ -0,0 +1,65 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.activiti.runtime.api.impl; + +import java.util.List; + +import org.activiti.bpmn.model.MapExceptionEntry; +import org.activiti.bpmn.model.UserTask; +import org.activiti.engine.delegate.Expression; +import org.activiti.engine.impl.bpmn.behavior.CallActivityBehavior; +import org.activiti.engine.impl.bpmn.behavior.UserTaskActivityBehavior; +import org.activiti.engine.impl.bpmn.parser.factory.ActivityBehaviorFactory; +import org.activiti.engine.impl.bpmn.parser.factory.DefaultActivityBehaviorFactory; +import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl; +import org.activiti.spring.process.ProcessVariablesInitiator; + +/** + * Default implementation of the {@link ActivityBehaviorFactory}. Used when no custom {@link ActivityBehaviorFactory} is injected on the {@link ProcessEngineConfigurationImpl}. + */ +public class MappingAwareActivityBehaviorFactory extends DefaultActivityBehaviorFactory { + + private VariablesMappingProvider variablesMappingProvider; + + private ProcessVariablesInitiator processVariablesInitiator; + + public MappingAwareActivityBehaviorFactory(VariablesMappingProvider variablesMappingProvider, + ProcessVariablesInitiator processVariablesInitiator) { + super(); + this.variablesMappingProvider = variablesMappingProvider; + this.processVariablesInitiator = processVariablesInitiator; + } + + @Override + public UserTaskActivityBehavior createUserTaskActivityBehavior(UserTask userTask) { + return new MappingAwareUserTaskBehavior(userTask, + variablesMappingProvider); + } + + @Override + protected CallActivityBehavior createCallActivityBehavior(Expression expression, List mapExceptions) { + return new MappingAwareCallActivityBehavior(expression, + mapExceptions, + variablesMappingProvider, + processVariablesInitiator); + } + + @Override + protected CallActivityBehavior createCallActivityBehavior(String calledElement, + List mapExceptions) { + return new MappingAwareCallActivityBehavior(calledElement, + mapExceptions, + variablesMappingProvider, + processVariablesInitiator); + } +} diff --git a/activiti-api-impl/activiti-api-process-runtime-impl/src/main/java/org/activiti/runtime/api/impl/MappingAwareCallActivityBehavior.java b/activiti-api-impl/activiti-api-process-runtime-impl/src/main/java/org/activiti/runtime/api/impl/MappingAwareCallActivityBehavior.java new file mode 100644 index 00000000000..7578fd59759 --- /dev/null +++ b/activiti-api-impl/activiti-api-process-runtime-impl/src/main/java/org/activiti/runtime/api/impl/MappingAwareCallActivityBehavior.java @@ -0,0 +1,70 @@ +/* + * Copyright 2018 Alfresco, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.activiti.runtime.api.impl; + +import java.util.List; +import java.util.Map; + +import org.activiti.bpmn.model.MapExceptionEntry; +import org.activiti.engine.delegate.DelegateExecution; +import org.activiti.engine.delegate.Expression; +import org.activiti.engine.impl.bpmn.behavior.CallActivityBehavior; +import org.activiti.engine.repository.ProcessDefinition; +import org.activiti.spring.process.ProcessVariablesInitiator; + +public class MappingAwareCallActivityBehavior extends CallActivityBehavior { + + private VariablesMappingProvider mappingProvider; + private ProcessVariablesInitiator processVariablesInitiator; + + public MappingAwareCallActivityBehavior(String processDefinitionKey, + List mapExceptions, + VariablesMappingProvider mappingProvider, + ProcessVariablesInitiator processVariablesInitiator) { + super(processDefinitionKey, + mapExceptions); + this.mappingProvider = mappingProvider; + this.processVariablesInitiator = processVariablesInitiator; + } + + public MappingAwareCallActivityBehavior(Expression processDefinitionExpression, + List mapExceptions, + VariablesMappingProvider mappingProvider, + ProcessVariablesInitiator processVariablesInitiator) { + super(processDefinitionExpression, + mapExceptions); + this.mappingProvider = mappingProvider; + this.processVariablesInitiator = processVariablesInitiator; + } + + @Override + protected Map calculateInboundVariables(DelegateExecution execution, + ProcessDefinition processDefinition) { + + Map inputVariables = mappingProvider.calculateInputVariables(execution); + return processVariablesInitiator.calculateVariablesFromExtensionFile(processDefinition, + inputVariables); + } + + @Override + protected Map calculateOutBoundVariables(DelegateExecution execution, + Map availableVariables) { + + return mappingProvider.calculateOutPutVariables(execution, + availableVariables); + } +} diff --git a/activiti-api-impl/activiti-api-process-runtime-impl/src/main/java/org/activiti/runtime/api/impl/MappingAwareUserTaskBehavior.java b/activiti-api-impl/activiti-api-process-runtime-impl/src/main/java/org/activiti/runtime/api/impl/MappingAwareUserTaskBehavior.java new file mode 100644 index 00000000000..afb850bb143 --- /dev/null +++ b/activiti-api-impl/activiti-api-process-runtime-impl/src/main/java/org/activiti/runtime/api/impl/MappingAwareUserTaskBehavior.java @@ -0,0 +1,51 @@ +/* + * Copyright 2018 Alfresco, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.activiti.runtime.api.impl; + +import java.util.Map; + +import org.activiti.bpmn.model.UserTask; +import org.activiti.engine.delegate.DelegateExecution; +import org.activiti.engine.impl.bpmn.behavior.UserTaskActivityBehavior; + +public class MappingAwareUserTaskBehavior extends UserTaskActivityBehavior { + + private VariablesMappingProvider mappingProvider; + + public MappingAwareUserTaskBehavior(UserTask userTask, + VariablesMappingProvider mappingProvider) { + super(userTask); + this.mappingProvider = mappingProvider; + } + + + @Override + protected Map calculateInputVariables(DelegateExecution execution) { + return mappingProvider.calculateInputVariables(execution); + } + + @Override + protected Map calculateOutBoundVariables(DelegateExecution execution, + Map taskCompleteVariables) { + + return mappingProvider.calculateOutPutVariables(execution, + taskCompleteVariables); + } + + + +} diff --git a/activiti-api-impl/activiti-api-process-runtime-impl/src/main/java/org/activiti/runtime/api/impl/VariablesMappingProvider.java b/activiti-api-impl/activiti-api-process-runtime-impl/src/main/java/org/activiti/runtime/api/impl/VariablesMappingProvider.java new file mode 100644 index 00000000000..c00c1381a11 --- /dev/null +++ b/activiti-api-impl/activiti-api-process-runtime-impl/src/main/java/org/activiti/runtime/api/impl/VariablesMappingProvider.java @@ -0,0 +1,152 @@ +/* + * Copyright 2019 Alfresco, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.activiti.runtime.api.impl; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import org.activiti.engine.delegate.DelegateExecution; +import org.activiti.spring.process.ProcessExtensionService; +import org.activiti.spring.process.model.Mapping; +import org.activiti.spring.process.model.ProcessExtensionModel; +import org.activiti.spring.process.model.ProcessVariablesMapping; +import org.activiti.spring.process.model.VariableDefinition; + +public class VariablesMappingProvider { + + private ProcessExtensionService processExtensionService; + + public VariablesMappingProvider(ProcessExtensionService processExtensionService) { + this.processExtensionService = processExtensionService; + } + + protected Optional calculateMappedValue(Mapping inputMapping, + DelegateExecution execution, + ProcessExtensionModel extensions) { + if (inputMapping != null) { + if (Mapping.SourceMappingType.VALUE.equals(inputMapping.getType()) || Mapping.SourceMappingType.STATIC_VALUE.equals(inputMapping.getType())) { + return Optional.of(inputMapping.getValue()); + } + + if (Mapping.SourceMappingType.VARIABLE.equals(inputMapping.getType())) { + String name = inputMapping.getValue().toString(); + + VariableDefinition processVariableDefinition = extensions.getExtensions().getPropertyByName(name); + if (processVariableDefinition != null) { + return Optional.of(execution.getVariable(processVariableDefinition.getName())); + } + } + } + return Optional.empty(); + } + + public Map calculateInputVariables(DelegateExecution execution) { + + ProcessExtensionModel extensions = processExtensionService.getExtensionsForId(execution.getProcessDefinitionId()); + + if(extensions.getExtensions().hasEmptyInputsMapping(execution.getCurrentActivityId())){ + return Collections.emptyMap(); + } + + if(extensions.getExtensions().hasNoMapping(execution.getCurrentActivityId())){ + return new HashMap<>(execution.getVariables()); + } + + return calculateInputVariables(execution, extensions); + } + + protected Map calculateInputVariables(DelegateExecution execution, + ProcessExtensionModel extensions) { + Map inboundVariables = new HashMap<>(); + + ProcessVariablesMapping processVariablesMapping = extensions.getExtensions().getMappingForFlowElement(execution.getCurrentActivityId()); + Map inputMappings = processVariablesMapping.getInputs(); + + for (Map.Entry mapping : inputMappings.entrySet()) { + Optional mappedValue = calculateMappedValue(mapping.getValue(), + execution, + extensions); + mappedValue.ifPresent(value -> inboundVariables.put(mapping.getKey(), + value)); + } + + return inboundVariables; + } + + protected Optional calculateOutPutMappedValue(Mapping mapping, + Map currentContextVariables) { + if (mapping != null) { + if (Mapping.SourceMappingType.VALUE.equals(mapping.getType())) { + return Optional.of(mapping.getValue()); + } else { + if (Mapping.SourceMappingType.VARIABLE.equals(mapping.getType())) { + String name = mapping.getValue().toString(); + + return currentContextVariables != null ? + Optional.of(currentContextVariables.get(name)) : + Optional.empty(); + } + } + } + return Optional.empty(); + } + + public Map calculateOutPutVariables(DelegateExecution execution, + Map availableVariables) { + + ProcessExtensionModel extensions = processExtensionService.getExtensionsForId(execution.getProcessDefinitionId()); + + if(extensions.getExtensions().hasEmptyOutputsMapping(execution.getCurrentActivityId())){ + return Collections.emptyMap(); + } + + if(extensions.getExtensions().hasNoMapping(execution.getCurrentActivityId())){ + return new HashMap<>(availableVariables); + } + + if (!availableVariables.isEmpty()) { + return calculateOutPutVariables(execution, extensions, availableVariables); + }else{ + return Collections.emptyMap(); + } + } + + private Map calculateOutPutVariables(DelegateExecution execution, + ProcessExtensionModel extensions, + Map availableVariables){ + Map outboundVariables = new HashMap<>(); + ProcessVariablesMapping processVariablesMapping = extensions.getExtensions().getMappingForFlowElement(execution.getCurrentActivityId()); + Map outputMappings = processVariablesMapping.getOutputs(); + + for (Map.Entry mapping : outputMappings.entrySet()) { + + String name = mapping.getKey(); + + VariableDefinition processVariableDefinition = extensions.getExtensions().getPropertyByName(name); + + if (processVariableDefinition != null && calculateOutPutMappedValue(mapping.getValue(), + availableVariables).isPresent()) { + outboundVariables.put(name, calculateOutPutMappedValue(mapping.getValue(), + availableVariables).get()); + } + } + + return outboundVariables; + } +} \ No newline at end of file diff --git a/activiti-api-impl/activiti-api-process-runtime-impl/src/main/java/org/conf/activiti/runtime/api/ConnectorsAutoConfiguration.java b/activiti-api-impl/activiti-api-process-runtime-impl/src/main/java/org/conf/activiti/runtime/api/ConnectorsAutoConfiguration.java index f20d0df8a59..d474d1d2ade 100644 --- a/activiti-api-impl/activiti-api-process-runtime-impl/src/main/java/org/conf/activiti/runtime/api/ConnectorsAutoConfiguration.java +++ b/activiti-api-impl/activiti-api-process-runtime-impl/src/main/java/org/conf/activiti/runtime/api/ConnectorsAutoConfiguration.java @@ -26,6 +26,7 @@ import org.activiti.runtime.api.connector.InboundVariablesProvider; import org.activiti.runtime.api.connector.IntegrationContextBuilder; import org.activiti.runtime.api.connector.OutboundVariablesProvider; +import org.activiti.runtime.api.impl.VariablesMappingProvider; import org.activiti.spring.process.ProcessExtensionService; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.ApplicationContext; @@ -63,4 +64,10 @@ public OutboundVariablesProvider outboundVariablesProvider(ConnectorActionDefini public ConnectorActionDefinitionFinder connectorActionDefinitionFinder(List connectorDefinitions) { return new ConnectorActionDefinitionFinder(connectorDefinitions); } + + + @Bean + public VariablesMappingProvider variablesMappingProvider(ProcessExtensionService processExtensionService){ + return new VariablesMappingProvider(processExtensionService); + } } diff --git a/activiti-api-impl/activiti-api-process-runtime-impl/src/main/resources/task-variable-empty-mapping-extensions.json b/activiti-api-impl/activiti-api-process-runtime-impl/src/main/resources/task-variable-empty-mapping-extensions.json new file mode 100644 index 00000000000..2a56d3c628d --- /dev/null +++ b/activiti-api-impl/activiti-api-process-runtime-impl/src/main/resources/task-variable-empty-mapping-extensions.json @@ -0,0 +1,34 @@ +{ + "id":"taskVariableEmptyMapping", + "extensions": { + "properties": { + "process-variable-unmapped-1-id": { + "id": "process-variable-unmapped-1-id", + "name": "process_variable_unmapped_1", + "type": "string", + "required": true, + "value": "unmapped1Value" + }, + "process-variable-inputmap-1-id": { + "id": "process-variable-inputmap-1-id", + "name": "process_variable_inputmap_1", + "type": "string", + "required": true, + "value": "inputmap1Value" + }, + "process-variable-outputmap-1-id": { + "id": "process-variable-outputmap-1-id", + "name": "process_variable_outputmap_1", + "type": "string", + "required": true, + "value": "outputmap1Value" + } + }, + "mappings": { + "simpleTask": { + "inputs": {}, + "outputs": {} + } + } + } +} \ No newline at end of file diff --git a/activiti-api-impl/activiti-api-process-runtime-impl/src/main/resources/task-variable-mapping-extensions.json b/activiti-api-impl/activiti-api-process-runtime-impl/src/main/resources/task-variable-mapping-extensions.json new file mode 100644 index 00000000000..1d280b4f36e --- /dev/null +++ b/activiti-api-impl/activiti-api-process-runtime-impl/src/main/resources/task-variable-mapping-extensions.json @@ -0,0 +1,44 @@ +{ + "id":"taskVarMapping", + "extensions": { + "properties": { + "process-variable-unmapped-1-id": { + "id": "process-variable-unmapped-1-id", + "name": "process_variable_unmapped_1", + "type": "string", + "required": true, + "value": "unmapped1Value" + }, + "process-variable-inputmap-1-id": { + "id": "process-variable-inputmap-1-id", + "name": "process_variable_inputmap_1", + "type": "string", + "required": true, + "value": "inputmap1Value" + }, + "process-variable-outputmap-1-id": { + "id": "process-variable-outputmap-1-id", + "name": "process_variable_outputmap_1", + "type": "string", + "required": true, + "value": "outputmap1Value" + } + }, + "mappings": { + "simpleTask": { + "inputs": { + "task_input_variable_name_1": { + "type": "VARIABLE", + "value": "process_variable_inputmap_1" + } + }, + "outputs": { + "process_variable_outputmap_1": { + "type": "VARIABLE", + "value": "task_output_variable_name_1" + } + } + } + } + } +} \ No newline at end of file diff --git a/activiti-api-impl/activiti-api-process-runtime-impl/src/main/resources/task-variable-no-mapping-extensions.json b/activiti-api-impl/activiti-api-process-runtime-impl/src/main/resources/task-variable-no-mapping-extensions.json new file mode 100644 index 00000000000..a1791efc842 --- /dev/null +++ b/activiti-api-impl/activiti-api-process-runtime-impl/src/main/resources/task-variable-no-mapping-extensions.json @@ -0,0 +1,29 @@ +{ + "id":"taskVariableNoMapping", + "extensions": { + "properties": { + "process-variable-unmapped-1-id": { + "id": "process-variable-unmapped-1-id", + "name": "process_variable_unmapped_1", + "type": "string", + "required": true, + "value": "unmapped1Value" + }, + "process-variable-inputmap-1-id": { + "id": "process-variable-inputmap-1-id", + "name": "process_variable_inputmap_1", + "type": "string", + "required": true, + "value": "inputmap1Value" + }, + "process-variable-outputmap-1-id": { + "id": "process-variable-outputmap-1-id", + "name": "process_variable_outputmap_1", + "type": "string", + "required": true, + "value": "outputmap1Value" + } + }, + "mappings": {} + } +} \ No newline at end of file diff --git a/activiti-api-impl/activiti-api-process-runtime-impl/src/test/java/org/activiti/runtime/api/impl/MappingAwareActivityBehaviorFactoryTest.java b/activiti-api-impl/activiti-api-process-runtime-impl/src/test/java/org/activiti/runtime/api/impl/MappingAwareActivityBehaviorFactoryTest.java new file mode 100644 index 00000000000..bf3430b01cb --- /dev/null +++ b/activiti-api-impl/activiti-api-process-runtime-impl/src/test/java/org/activiti/runtime/api/impl/MappingAwareActivityBehaviorFactoryTest.java @@ -0,0 +1,52 @@ +/* + * Copyright 2019 Alfresco, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.activiti.runtime.api.impl; + +import java.util.Collections; + +import org.activiti.bpmn.model.UserTask; +import org.activiti.engine.delegate.Expression; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +public class MappingAwareActivityBehaviorFactoryTest { + + private MappingAwareActivityBehaviorFactory factory = new MappingAwareActivityBehaviorFactory(null, + null); + + @Test + public void createUserTaskActivityBehaviorShouldReturnMappingAwareUserTaskBehavior() { + assertThat(factory.createUserTaskActivityBehavior(mock(UserTask.class))) + .isInstanceOf(MappingAwareUserTaskBehavior.class); + } + + @Test + public void createCallActivityBehaviorShouldReturnMappingAwareCallActivityBehavior() { + assertThat(factory.createCallActivityBehavior("element", + Collections.emptyList())) + .isInstanceOf(MappingAwareCallActivityBehavior.class); + } + + @Test + public void createCallActivityBehaviorWithExpressionShouldReturnMappingAwareCallActivityBehavior() { + assertThat(factory.createCallActivityBehavior(mock(Expression.class), + Collections.emptyList())) + .isInstanceOf(MappingAwareCallActivityBehavior.class); + } +} \ No newline at end of file diff --git a/activiti-api-impl/activiti-api-process-runtime-impl/src/test/java/org/activiti/runtime/api/impl/MappingAwareCallActivityBehaviorTest.java b/activiti-api-impl/activiti-api-process-runtime-impl/src/test/java/org/activiti/runtime/api/impl/MappingAwareCallActivityBehaviorTest.java new file mode 100644 index 00000000000..f17f8849321 --- /dev/null +++ b/activiti-api-impl/activiti-api-process-runtime-impl/src/test/java/org/activiti/runtime/api/impl/MappingAwareCallActivityBehaviorTest.java @@ -0,0 +1,94 @@ +/* + * Copyright 2019 Alfresco, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.activiti.runtime.api.impl; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.activiti.engine.delegate.DelegateExecution; +import org.activiti.engine.repository.ProcessDefinition; +import org.activiti.spring.process.ProcessVariablesInitiator; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.*; +import static org.mockito.MockitoAnnotations.initMocks; + +public class MappingAwareCallActivityBehaviorTest { + + @InjectMocks + private MappingAwareCallActivityBehavior behavior; + + @Mock + private VariablesMappingProvider mappingProvider; + + @Mock + private ProcessVariablesInitiator processVariablesInitiator; + + @Before + public void setUp() { + initMocks(this); + } + + + @Test + public void calculateInboundVariablesShouldTakeIntoAccountMappingProviderAndProcessVariablesInitiator() { + //given + DelegateExecution execution = buildExecution(); + ProcessDefinition processDefinition = mock(ProcessDefinition.class); + Map providerVariables = Collections.singletonMap("var1", + "v1"); + given(mappingProvider.calculateInputVariables(execution)).willReturn(providerVariables); + + HashMap initiatorVariables = new HashMap<>(providerVariables); + initiatorVariables.put("var2", "default"); + given(processVariablesInitiator.calculateVariablesFromExtensionFile(processDefinition, providerVariables)) + .willReturn(initiatorVariables); + + //when + Map inboundVariables = behavior.calculateInboundVariables(execution, + processDefinition); + //then + assertThat(inboundVariables).isEqualTo(initiatorVariables); + } + + private DelegateExecution buildExecution() { + return mock(DelegateExecution.class); + } + + @Test + public void calculateOutBoundVariablesShouldReturnValueFromMappingProvider() { + //given + DelegateExecution execution = buildExecution(); + Map availableVariables = Collections.emptyMap(); + Map providerVariables = Collections.singletonMap("var", + "value"); + given(mappingProvider.calculateOutPutVariables(execution, availableVariables)) + .willReturn(providerVariables); + + //when + Map outBoundVariables = behavior.calculateOutBoundVariables(execution, + availableVariables); + //then + assertThat(outBoundVariables).isEqualTo(providerVariables); + } +} \ No newline at end of file diff --git a/activiti-api-impl/activiti-api-process-runtime-impl/src/test/java/org/activiti/runtime/api/impl/MappingAwareUserTaskBehaviorTest.java b/activiti-api-impl/activiti-api-process-runtime-impl/src/test/java/org/activiti/runtime/api/impl/MappingAwareUserTaskBehaviorTest.java new file mode 100644 index 00000000000..c4a93ab5516 --- /dev/null +++ b/activiti-api-impl/activiti-api-process-runtime-impl/src/test/java/org/activiti/runtime/api/impl/MappingAwareUserTaskBehaviorTest.java @@ -0,0 +1,80 @@ +/* + * Copyright 2019 Alfresco, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.activiti.runtime.api.impl; + +import java.util.Collections; +import java.util.Map; + +import org.activiti.engine.delegate.DelegateExecution; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.*; +import static org.mockito.MockitoAnnotations.initMocks; + +public class MappingAwareUserTaskBehaviorTest { + + @InjectMocks + private MappingAwareUserTaskBehavior behavior; + + @Mock + private VariablesMappingProvider mappingProvider; + + @Before + public void setUp() throws Exception { + initMocks(this); + } + + @Test + public void calculateInputVariablesShouldReturnValueFromMappingProvider() { + //given + DelegateExecution execution = buildExecution(); + Map providerVariables = Collections.singletonMap("var", + "value"); + given(mappingProvider.calculateInputVariables(execution)).willReturn(providerVariables); + + //when + Map inputVariables = behavior.calculateInputVariables(execution); + + //then + assertThat(inputVariables).isEqualTo(providerVariables); + } + + @Test + public void calculateOutBoundVariablesShouldReturnValueFromMappingProvider() { + //given + DelegateExecution execution = buildExecution(); + Map availableVariables = Collections.emptyMap(); + Map providerVariables = Collections.singletonMap("var", + "value"); + given(mappingProvider.calculateOutPutVariables(execution, availableVariables)).willReturn(providerVariables); + + //when + Map outBoundVariables = behavior.calculateOutBoundVariables(execution, + availableVariables); + //then + assertThat(outBoundVariables).isEqualTo(providerVariables); + } + + private DelegateExecution buildExecution() { + return mock(DelegateExecution.class); + } +} \ No newline at end of file diff --git a/activiti-api-impl/activiti-api-process-runtime-impl/src/test/java/org/activiti/runtime/api/impl/VariablesMappingProviderTest.java b/activiti-api-impl/activiti-api-process-runtime-impl/src/test/java/org/activiti/runtime/api/impl/VariablesMappingProviderTest.java new file mode 100644 index 00000000000..d9aabb29280 --- /dev/null +++ b/activiti-api-impl/activiti-api-process-runtime-impl/src/test/java/org/activiti/runtime/api/impl/VariablesMappingProviderTest.java @@ -0,0 +1,153 @@ +package org.activiti.runtime.api.impl; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.activiti.engine.delegate.DelegateExecution; +import org.activiti.spring.process.ProcessExtensionService; +import org.activiti.spring.process.model.ProcessExtensionModel; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.MockitoAnnotations.initMocks; + +public class VariablesMappingProviderTest { + + @InjectMocks + private VariablesMappingProvider variablesMappingProvider; + + @Mock + private ProcessExtensionService processExtensionService; + + @Before + public void setUp() { + initMocks(this); + } + + @Test + public void calculateInputVariablesShouldDoMappingWhenThereIsMappingSet() throws Exception{ + + //given + ObjectMapper objectMapper = new ObjectMapper(); + ProcessExtensionModel extensions = objectMapper.readValue(new File("src/main/resources/task-variable-mapping-extensions.json"), ProcessExtensionModel.class); + + DelegateExecution execution = buildExecution(extensions); + given(execution.getVariable("process_variable_inputmap_1")).willReturn("new-input-value"); + + //when + Map inputVariables = variablesMappingProvider.calculateInputVariables(execution); + + //then + assertThat(inputVariables.get("task_input_variable_name_1")).isEqualTo("new-input-value"); + } + + private DelegateExecution buildExecution(ProcessExtensionModel extensions) { + DelegateExecution execution = mock(DelegateExecution.class); + given(processExtensionService.getExtensionsForId("taskVarMapping")).willReturn(extensions); + given(execution.getProcessDefinitionId()).willReturn("taskVarMapping"); + given(execution.getCurrentActivityId()).willReturn("simpleTask"); + return execution; + } + + @Test + public void calculateInputVariablesShouldPassAllVariablesWhenThereIsNoMapping() throws Exception{ + //given + ObjectMapper objectMapper = new ObjectMapper(); + ProcessExtensionModel extensions = objectMapper.readValue(new File("src/main/resources/task-variable-no-mapping-extensions.json"), ProcessExtensionModel.class); + + DelegateExecution execution = buildExecution(extensions); + Map variables = new HashMap<>(); + variables.put("var-one", "one"); + variables.put("var-two", 2); + given(execution.getVariables()).willReturn(variables); + + //when + Map inputVariables = variablesMappingProvider.calculateInputVariables(execution); + + //then + assertThat(inputVariables).isEqualTo(variables); + } + + @Test + public void calculateInputVariablesShouldNotPassAnyVariablesWhenTheMappingIsEmpty () throws Exception{ + + //given + ObjectMapper objectMapper = new ObjectMapper(); + ProcessExtensionModel extensions = objectMapper.readValue(new File("src/main/resources/task-variable-empty-mapping-extensions.json"), ProcessExtensionModel.class); + + DelegateExecution execution = buildExecution(extensions); + + //when + Map inputVariables = variablesMappingProvider.calculateInputVariables(execution); + + //then + assertThat(inputVariables).isEmpty(); + } + + @Test + public void calculateOutputVariablesShouldDoMappingWhenThereIsMappingSet() throws Exception{ + + //given + ObjectMapper objectMapper = new ObjectMapper(); + ProcessExtensionModel extensions = objectMapper.readValue(new File("src/main/resources/task-variable-mapping-extensions.json"), ProcessExtensionModel.class); + + DelegateExecution execution = buildExecution(extensions); + + Map entityVariables = new HashMap<>(); + entityVariables.put("task_output_variable_name_1", "var-one"); + + //when + Map outPutVariables = variablesMappingProvider.calculateOutPutVariables(execution, entityVariables); + + //then + assertThat(outPutVariables.get("process_variable_outputmap_1")).isEqualTo("var-one"); + } + + @Test + public void calculateOutputVariablesShouldPassAllVariablesWhenThereIsNoMapping() throws Exception{ + //given + ObjectMapper objectMapper = new ObjectMapper(); + ProcessExtensionModel extensions = objectMapper.readValue(new File("src/main/resources/task-variable-no-mapping-extensions.json"), ProcessExtensionModel.class); + + DelegateExecution execution = buildExecution(extensions); + + Map entityVariables = new HashMap<>(); + entityVariables.put("task_output_variable_name_1", "var-one"); + entityVariables.put("non-mapped-output_variable_name_2", "var-two"); + + //when + Map outPutVariables = variablesMappingProvider.calculateOutPutVariables(execution, entityVariables); + + //then + assertThat(outPutVariables).isEqualTo(entityVariables); + } + + @Test + public void calculateOutputVariablesShouldNotPassAnyVariablesWhenTheMappingIsEmpty () throws Exception{ + + //given + ObjectMapper objectMapper = new ObjectMapper(); + ProcessExtensionModel extensions = objectMapper.readValue(new File("src/main/resources/task-variable-empty-mapping-extensions.json"), ProcessExtensionModel.class); + + DelegateExecution execution = buildExecution(extensions); + + Map entityVariables = new HashMap<>(); + entityVariables.put("task_output_variable_name_1", "var-one"); + entityVariables.put("non-mapped-output_variable_name_2", "var-two"); + + //when + Map inputVariables = variablesMappingProvider.calculateOutPutVariables(execution, entityVariables); + + //then + assertThat(inputVariables).isEmpty(); + } + + +} diff --git a/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/CallActivityBehavior.java b/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/CallActivityBehavior.java index 3170ca5c351..e77c15fd70c 100644 --- a/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/CallActivityBehavior.java +++ b/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/CallActivityBehavior.java @@ -13,8 +13,17 @@ package org.activiti.engine.impl.bpmn.behavior; -import org.activiti.bpmn.model.*; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.activiti.bpmn.model.CallActivity; +import org.activiti.bpmn.model.FlowElement; +import org.activiti.bpmn.model.IOParameter; +import org.activiti.bpmn.model.MapExceptionEntry; import org.activiti.bpmn.model.Process; +import org.activiti.bpmn.model.ValuedDataObject; import org.activiti.engine.ActivitiException; import org.activiti.engine.ProcessEngineConfiguration; import org.activiti.engine.delegate.DelegateExecution; @@ -30,11 +39,6 @@ import org.activiti.engine.repository.ProcessDefinition; import org.apache.commons.lang3.StringUtils; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - /** * Implementation of the BPMN 2.0 call activity (limited currently to calling a subprocess and not (yet) a global task). * @@ -117,6 +121,9 @@ public void execute(DelegateExecution execution) { variables.put(entry.getKey(), entry.getValue()); } } + Map variablesFromExtensionFile = calculateInboundVariables(execution, processDefinition); + + variables.putAll(variablesFromExtensionFile); // copy process variables for (IOParameter ioParameter : callActivity.getInParameters()) { @@ -147,7 +154,6 @@ public void execute(DelegateExecution execution) { public void completing(DelegateExecution execution, DelegateExecution subProcessInstance) throws Exception { // only data. no control flow available on this execution. - ExpressionManager expressionManager = Context.getProcessEngineConfiguration().getExpressionManager(); // copy process variables @@ -164,6 +170,10 @@ public void completing(DelegateExecution execution, DelegateExecution subProcess } execution.setVariable(ioParameter.getTarget(), value); } + Map outboundVariables = calculateOutBoundVariables(execution, subProcessInstance.getVariables()); + if (outboundVariables != null) { + execution.setVariables(outboundVariables); + } } public void completed(DelegateExecution execution) throws Exception { @@ -204,4 +214,15 @@ public void setProcessDefinitonKey(String processDefinitonKey) { public String getProcessDefinitonKey() { return processDefinitonKey; } + + protected Map calculateInboundVariables(DelegateExecution execution, + ProcessDefinition processDefinition) { + return new HashMap(); + } + + protected Map calculateOutBoundVariables(DelegateExecution execution, + Map subProcessVariables) { + return new HashMap(); + } + } diff --git a/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/UserTaskActivityBehavior.java b/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/UserTaskActivityBehavior.java index d90e7537842..592becfb094 100755 --- a/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/UserTaskActivityBehavior.java +++ b/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/UserTaskActivityBehavior.java @@ -14,10 +14,14 @@ import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.Date; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; +import com.fasterxml.jackson.databind.node.ObjectNode; import org.activiti.bpmn.model.UserTask; import org.activiti.engine.ActivitiException; import org.activiti.engine.ActivitiIllegalArgumentException; @@ -29,10 +33,10 @@ import org.activiti.engine.delegate.event.ActivitiEventType; import org.activiti.engine.delegate.event.impl.ActivitiEventBuilder; import org.activiti.engine.impl.bpmn.helper.SkipExpressionUtil; -import org.activiti.engine.impl.bpmn.helper.TaskVariableCopier; import org.activiti.engine.impl.calendar.BusinessCalendar; import org.activiti.engine.impl.calendar.DueDateBusinessCalendar; import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl; +import org.activiti.engine.impl.cmd.CompleteTaskCmd; import org.activiti.engine.impl.context.Context; import org.activiti.engine.impl.el.ExpressionManager; import org.activiti.engine.impl.interceptor.CommandContext; @@ -43,8 +47,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.fasterxml.jackson.databind.node.ObjectNode; - /** */ @@ -198,9 +200,7 @@ public void execute(DelegateExecution execution) { taskEntityManager.insert(task, (ExecutionEntity) execution); - if(commandContext.getProcessEngineConfiguration().isCopyVariablesToLocalForTasks()) { - TaskVariableCopier.copyVariablesIntoTaskLocal(task); - } + task.setVariablesLocal(calculateInputVariables(execution)); boolean skipUserTask = false; if (StringUtils.isNotEmpty(activeTaskSkipExpression)) { @@ -235,8 +235,27 @@ public void execute(DelegateExecution execution) { } + protected Map calculateInputVariables(DelegateExecution execution) { + CommandContext commandContext = Context.getCommandContext(); + if (commandContext.getProcessEngineConfiguration().isCopyVariablesToLocalForTasks()) { + return execution.getVariables(); + } else { + return Collections.emptyMap(); + } + } + + protected Map calculateOutBoundVariables(DelegateExecution execution, + Map taskVariables) { + CommandContext commandContext = Context.getCommandContext(); + if(commandContext.getProcessEngineConfiguration().isCopyVariablesToLocalForTasks()){ + return taskVariables; + } + return Collections.emptyMap(); + } + public void trigger(DelegateExecution execution, String signalName, Object signalData) { CommandContext commandContext = Context.getCommandContext(); + TaskEntityManager taskEntityManager = commandContext.getTaskEntityManager(); List taskEntities = taskEntityManager.findTasksByExecutionId(execution.getId()); // Should be only one for (TaskEntity taskEntity : taskEntities) { @@ -244,9 +263,32 @@ public void trigger(DelegateExecution execution, String signalName, Object signa throw new ActivitiException("UserTask should not be signalled before complete"); } } + + propagateVariablesToProcess(execution, + commandContext); + leave(execution); } + private void propagateVariablesToProcess(DelegateExecution execution, + CommandContext commandContext) { + String processInstanceId = execution.getProcessInstanceId(); + ExecutionEntity processInstanceEntity = processInstanceId != null ? + commandContext.getExecutionEntityManager().findById(processInstanceId) : + null; + + if (processInstanceEntity != null) { + Map taskVariables = new HashMap<>(); + + if (commandContext.getCommand() instanceof CompleteTaskCmd) { + taskVariables = ((CompleteTaskCmd) commandContext.getCommand()).getTaskVariables(); + } + Map outboundVariables = calculateOutBoundVariables(execution, + taskVariables); + processInstanceEntity.setVariables(outboundVariables); + } + } + @SuppressWarnings({ "unchecked", "rawtypes" }) protected void handleAssignments(TaskEntityManager taskEntityManager, String assignee, String owner, List candidateUsers, List candidateGroups, TaskEntity task, ExpressionManager expressionManager, DelegateExecution execution) { diff --git a/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/helper/TaskVariableCopier.java b/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/helper/TaskVariableCopier.java deleted file mode 100644 index c2d140d5097..00000000000 --- a/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/helper/TaskVariableCopier.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.activiti.engine.impl.bpmn.helper; - -import org.activiti.engine.impl.persistence.entity.TaskEntity; - -public class TaskVariableCopier { - - public static void copyVariablesIntoTaskLocal(TaskEntity task){ - - //TODO: would like to filter which variables copy much as with subProcesses - - task.setVariablesLocal(task.getVariables()); - - - } - - public static void copyVariablesOutFromTaskLocal(TaskEntity task){ - - //TODO: would like to filter which variables copy much as with subProcesses - - //provided not a standalone task - if(task.getProcessInstance()!=null) { - task.getProcessInstance().setVariables(task.getVariablesLocal()); - } - } -} diff --git a/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/parser/factory/DefaultActivityBehaviorFactory.java b/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/parser/factory/DefaultActivityBehaviorFactory.java index e9387f6a756..284c46fabed 100644 --- a/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/parser/factory/DefaultActivityBehaviorFactory.java +++ b/activiti-engine/src/main/java/org/activiti/engine/impl/bpmn/parser/factory/DefaultActivityBehaviorFactory.java @@ -105,6 +105,7 @@ public class DefaultActivityBehaviorFactory extends AbstractBehaviorFactory implements ActivityBehaviorFactory { public static final String DEFAULT_SERVICE_TASK_BEAN_NAME = "defaultServiceTaskBehavior"; + private final ClassDelegateFactory classDelegateFactory; public DefaultActivityBehaviorFactory(ClassDelegateFactory classDelegateFactory) { @@ -411,16 +412,24 @@ public CallActivityBehavior createCallActivityBehavior(CallActivity callActivity CallActivityBehavior callActivityBehaviour = null; if (StringUtils.isNotEmpty(callActivity.getCalledElement()) && callActivity.getCalledElement().matches(expressionRegex)) { - callActivityBehaviour = new CallActivityBehavior(expressionManager.createExpression(callActivity.getCalledElement()), - callActivity.getMapExceptions()); + callActivityBehaviour = createCallActivityBehavior(expressionManager.createExpression(callActivity.getCalledElement()), callActivity.getMapExceptions()); } else { - callActivityBehaviour = new CallActivityBehavior(callActivity.getCalledElement(), - callActivity.getMapExceptions()); + callActivityBehaviour = createCallActivityBehavior(callActivity.getCalledElement(), callActivity.getMapExceptions()); } return callActivityBehaviour; } + protected CallActivityBehavior createCallActivityBehavior(String calledElement, List mapExceptions) { + return new CallActivityBehavior(calledElement, + mapExceptions); + } + + protected CallActivityBehavior createCallActivityBehavior(Expression expression, List mapExceptions) { + return new CallActivityBehavior(expression, + mapExceptions); + } + // Transaction public TransactionActivityBehavior createTransactionActivityBehavior(Transaction transaction) { @@ -541,4 +550,5 @@ public BoundaryMessageEventActivityBehavior createBoundaryMessageEventActivityBe return new BoundaryMessageEventActivityBehavior(messageEventDefinition, interrupting); } + } diff --git a/activiti-engine/src/main/java/org/activiti/engine/impl/cmd/CompleteTaskCmd.java b/activiti-engine/src/main/java/org/activiti/engine/impl/cmd/CompleteTaskCmd.java index cfa0a3f3074..b87c3826f21 100644 --- a/activiti-engine/src/main/java/org/activiti/engine/impl/cmd/CompleteTaskCmd.java +++ b/activiti-engine/src/main/java/org/activiti/engine/impl/cmd/CompleteTaskCmd.java @@ -1,9 +1,9 @@ /* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,7 +16,6 @@ import org.activiti.engine.impl.interceptor.CommandContext; import org.activiti.engine.impl.persistence.entity.TaskEntity; -import org.activiti.engine.impl.bpmn.helper.TaskVariableCopier; /** @@ -27,6 +26,7 @@ public class CompleteTaskCmd extends AbstractCompleteTaskCmd { private static final long serialVersionUID = 1L; protected Map variables; protected Map transientVariables; + protected Map taskVariables; protected boolean localScope; public CompleteTaskCmd(String taskId, Map variables) { @@ -38,7 +38,7 @@ public CompleteTaskCmd(String taskId, Map variables, boolean loc this(taskId, variables); this.localScope = localScope; } - + public CompleteTaskCmd(String taskId, Map variables, Map transientVariables) { this(taskId, variables); this.transientVariables = transientVariables; @@ -46,15 +46,15 @@ public CompleteTaskCmd(String taskId, Map variables, Map getTaskVariables() { + return taskVariables; + } + + private void setTaskVariables(Map taskVariables) { + this.taskVariables = taskVariables; + } + + } diff --git a/activiti-spring-boot-starter/src/main/java/org/activiti/spring/boot/DefaultActivityBehaviorFactoryMappingConfigurer.java b/activiti-spring-boot-starter/src/main/java/org/activiti/spring/boot/DefaultActivityBehaviorFactoryMappingConfigurer.java new file mode 100644 index 00000000000..3384684395c --- /dev/null +++ b/activiti-spring-boot-starter/src/main/java/org/activiti/spring/boot/DefaultActivityBehaviorFactoryMappingConfigurer.java @@ -0,0 +1,24 @@ +package org.activiti.spring.boot; + +import org.activiti.runtime.api.impl.MappingAwareActivityBehaviorFactory; +import org.activiti.runtime.api.impl.VariablesMappingProvider; +import org.activiti.spring.SpringProcessEngineConfiguration; +import org.activiti.spring.process.ProcessVariablesInitiator; + +public class DefaultActivityBehaviorFactoryMappingConfigurer implements ProcessEngineConfigurationConfigurer { + + private VariablesMappingProvider variablesMappingProvider; + + private ProcessVariablesInitiator processVariablesInitiator; + + public DefaultActivityBehaviorFactoryMappingConfigurer(VariablesMappingProvider variablesMappingProvider, + ProcessVariablesInitiator processVariablesInitiator){ + this.variablesMappingProvider = variablesMappingProvider; + this.processVariablesInitiator = processVariablesInitiator; + } + @Override + public void configure(SpringProcessEngineConfiguration processEngineConfiguration){ + processEngineConfiguration.setActivityBehaviorFactory(new MappingAwareActivityBehaviorFactory(variablesMappingProvider, + processVariablesInitiator)); + } +} \ No newline at end of file diff --git a/activiti-spring-boot-starter/src/main/java/org/activiti/spring/boot/ProcessEngineAutoConfiguration.java b/activiti-spring-boot-starter/src/main/java/org/activiti/spring/boot/ProcessEngineAutoConfiguration.java index 8efc731a352..9ef5698198a 100644 --- a/activiti-spring-boot-starter/src/main/java/org/activiti/spring/boot/ProcessEngineAutoConfiguration.java +++ b/activiti-spring-boot-starter/src/main/java/org/activiti/spring/boot/ProcessEngineAutoConfiguration.java @@ -25,11 +25,13 @@ import org.activiti.engine.RepositoryService; import org.activiti.engine.cfg.ProcessEngineConfigurator; import org.activiti.engine.impl.persistence.StrongUuidGenerator; +import org.activiti.runtime.api.impl.VariablesMappingProvider; import org.activiti.runtime.api.model.impl.APIProcessDefinitionConverter; import org.activiti.spring.ProcessDeployedEventProducer; import org.activiti.spring.SpringAsyncExecutor; import org.activiti.spring.SpringProcessEngineConfiguration; import org.activiti.spring.boot.process.validation.AsyncPropertyValidator; +import org.activiti.spring.process.ProcessVariablesInitiator; import org.activiti.validation.ProcessValidatorImpl; import org.activiti.validation.validator.ValidatorSet; import org.springframework.beans.factory.annotation.Autowired; @@ -170,5 +172,13 @@ public ProcessDeployedEventProducer processDeployedEventProducer(RepositoryServi .orElse(Collections.emptyList()), eventPublisher); } + + @Bean + @ConditionalOnMissingBean + public DefaultActivityBehaviorFactoryMappingConfigurer defaultActivityBehaviorFactoryMappingConfigurer(VariablesMappingProvider variablesMappingProvider, + ProcessVariablesInitiator processVariablesInitiator) { + return new DefaultActivityBehaviorFactoryMappingConfigurer(variablesMappingProvider, + processVariablesInitiator); + } } diff --git a/activiti-spring-boot-starter/src/test/java/org/activiti/spring/boot/DefaultActivityBehaviorFactoryMappingConfigurerIT.java b/activiti-spring-boot-starter/src/test/java/org/activiti/spring/boot/DefaultActivityBehaviorFactoryMappingConfigurerIT.java new file mode 100644 index 00000000000..59697f9d94a --- /dev/null +++ b/activiti-spring-boot-starter/src/test/java/org/activiti/spring/boot/DefaultActivityBehaviorFactoryMappingConfigurerIT.java @@ -0,0 +1,30 @@ +package org.activiti.spring.boot; + +import org.activiti.runtime.api.impl.MappingAwareActivityBehaviorFactory; +import org.activiti.spring.SpringProcessEngineConfiguration; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) +public class DefaultActivityBehaviorFactoryMappingConfigurerIT { + + @Autowired + private SpringProcessEngineConfiguration processEngineConfiguration; + + @Test + public void processEngineConfigurationShouldHaveSetMappingAwareActivityBehaviorFactoryAsActivityBehaviorFactory(){ + assertThat(processEngineConfiguration.getActivityBehaviorFactory()) + .isInstanceOf(MappingAwareActivityBehaviorFactory.class); + assertThat(processEngineConfiguration.getBpmnParser().getActivityBehaviorFactory()) + .isInstanceOf(MappingAwareActivityBehaviorFactory.class); + + } + + +} diff --git a/activiti-spring-boot-starter/src/test/java/org/activiti/spring/boot/RuntimeTestConfiguration.java b/activiti-spring-boot-starter/src/test/java/org/activiti/spring/boot/RuntimeTestConfiguration.java index 96a49f32c57..a5ea6184360 100644 --- a/activiti-spring-boot-starter/src/test/java/org/activiti/spring/boot/RuntimeTestConfiguration.java +++ b/activiti-spring-boot-starter/src/test/java/org/activiti/spring/boot/RuntimeTestConfiguration.java @@ -68,6 +68,11 @@ public UserDetailsService myUserDetailsService() { "password", salaboyAuthorities)); + extendedInMemoryUserDetailsManager.createUser(new User("user", + "password", + salaboyAuthorities)); + + List adminAuthorities = new ArrayList<>(); adminAuthorities.add(new SimpleGrantedAuthority("ROLE_ACTIVITI_ADMIN")); diff --git a/activiti-spring-boot-starter/src/test/java/org/activiti/spring/boot/process/ProcessRuntimeCallActivityMappingIT.java b/activiti-spring-boot-starter/src/test/java/org/activiti/spring/boot/process/ProcessRuntimeCallActivityMappingIT.java new file mode 100644 index 00000000000..699a822d4f4 --- /dev/null +++ b/activiti-spring-boot-starter/src/test/java/org/activiti/spring/boot/process/ProcessRuntimeCallActivityMappingIT.java @@ -0,0 +1,346 @@ +package org.activiti.spring.boot.process; + +import org.activiti.api.model.shared.model.VariableInstance; +import org.activiti.api.process.model.ProcessInstance; +import org.activiti.api.process.model.builders.ProcessPayloadBuilder; +import org.activiti.api.process.runtime.ProcessRuntime; +import org.activiti.api.runtime.shared.query.Pageable; +import org.activiti.api.task.model.Task; +import org.activiti.api.task.model.builders.TaskPayloadBuilder; +import org.activiti.api.task.runtime.TaskRuntime; +import org.activiti.spring.boot.security.util.SecurityUtil; +import org.activiti.spring.boot.test.util.ProcessCleanUpUtil; +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) +public class ProcessRuntimeCallActivityMappingIT { + + private static final String PARENT_PROCESS_CALL_ACTIVITY = "parentproc-843144bc-3797-40db-8edc-d23190b118e5"; + private static final String SUB_PROCESS_CALL_ACTIVITY = "subprocess-fb5f2386-709a-4947-9aa0-bbf31497384g"; + + private static final String PARENT_PROCESS_CALL_ACTIVITY_EMPTY_MAPPING_NO_TASK = "parentproc-843144bc-3797-40db-8edc-d23190b118e6"; + private static final String PARENT_PROCESS_CALL_ACTIVITY_EMPTY_MAPPING_WITH_TASK = "parentproc-843144bc-3797-40db-8edc-d23190b118e7"; + + @Autowired + private ProcessRuntime processRuntime; + + @Autowired + private TaskRuntime taskRuntime; + + @Autowired + private SecurityUtil securityUtil; + + @Autowired + private ProcessCleanUpUtil processCleanUpUtil; + + @After + public void cleanUp() { + processCleanUpUtil.cleanUpWithAdmin(); + } + + @Test + public void basicCallActivityMappingTest() { + securityUtil.logInAs("user"); + + ProcessInstance processInstance = processRuntime.start( + ProcessPayloadBuilder + .start() + .withProcessDefinitionKey(PARENT_PROCESS_CALL_ACTIVITY) + .build()); + assertThat(processInstance).isNotNull(); + + ProcessInstance subProcessInstance = getSubProcess(processInstance); + assertThat(subProcessInstance.getProcessDefinitionKey()).isEqualTo(SUB_PROCESS_CALL_ACTIVITY); + + Task task = getTask(subProcessInstance); + + taskRuntime.claim(TaskPayloadBuilder.claim().withTaskId(task.getId()).build()); + + List subProcVariables = processRuntime.variables(ProcessPayloadBuilder + .variables() + .withProcessInstanceId(subProcessInstance.getId()) + .build()); + + assertThat(subProcVariables).extracting(VariableInstance::getName, + VariableInstance::getValue) + .containsOnly( + tuple("subprocess-input-var1", + "inName"), + tuple("subprocess-input-var2", + 20), + tuple("subprocess-input-var3", + 5), + tuple("input-static-value", +// + "a static value"), + tuple("subprocess-out-var1", + "outValue"), + tuple("subprocess-out-var2", + 222), + tuple("subprocess-static-value", + "static some value"), + tuple("var-not-exist-in-subprocess-extension-file", + 20) + + ); + + assertThat("my-task-call-activity"). + + isEqualTo(task.getName()); + + Map variablesForTask = new HashMap<>(); + variablesForTask.put("input-variable-name-1", + "fromSubprocessName"); + variablesForTask.put("input-variable-name-2", + 39); + variablesForTask.put("out-variable-name-1", + 176); + + completeTask(task.getId(), + + variablesForTask); + + List parentVariables = processRuntime.variables(ProcessPayloadBuilder + .variables() + .withProcessInstanceId(processInstance.getId()) + .build()); + + assertThat(parentVariables).extracting(VariableInstance::getName, + VariableInstance::getValue) + .containsOnly( + tuple("name", + "outValue"), + tuple("age", + 222), + tuple("output-unmapped-variable-with-non-matching-connector-output-name", + "default"), + tuple("input-unmapped-variable-with-non-matching-connector-input-name", + "inTest") + + ); + cleanUp(); + + } + + @Test + public void haveToPassAllVariablesCallActivityEmptyMappingNoTaskTest() { + securityUtil.logInAs("user"); + // After the process has started, the subProcess task should be active + ProcessInstance processInstance = processRuntime.start( + ProcessPayloadBuilder + .start() + .withProcessDefinitionKey(PARENT_PROCESS_CALL_ACTIVITY_EMPTY_MAPPING_NO_TASK) + .build()); + assertThat(processInstance).isNotNull(); + + ProcessInstance subProcessInstance = getSubProcess(processInstance); + assertThat(subProcessInstance.getProcessDefinitionKey()).isEqualTo(SUB_PROCESS_CALL_ACTIVITY); + + Task task = getTask(subProcessInstance); + + taskRuntime.claim(TaskPayloadBuilder.claim().withTaskId(task.getId()).build()); + + List subProcVariables = processRuntime.variables(ProcessPayloadBuilder + .variables() + .withProcessInstanceId(subProcessInstance.getId()) + .build()); + + assertThat(subProcVariables).extracting(VariableInstance::getName, + VariableInstance::getValue) + .containsOnly( + tuple("output-unmapped-variable-with-non-matching-connector-output-name", + "default"), + tuple("input-unmapped-variable-with-non-matching-connector-input-name", + "inTest"), + tuple("name", + "inName"), + tuple("age", + 20), + tuple("subprocess-input-var2", + 2), + tuple("subprocess-input-var3", + 3), + tuple("subprocess-out-var2", + 222), + tuple("subprocess-out-var1", + "outValue"), + + tuple("subprocess-input-var1", + "value1"), + tuple("subprocess-static-value", + "static some value") + ); + + assertThat("my-task-call-activity").isEqualTo(task.getName()); + + Map variablesForTask = new HashMap<>(); + variablesForTask.put("input-variable-name-1", + "fromSubprocessName"); + + completeTask(task.getId(), + variablesForTask); + List parentVariablesAfterComnplete = processRuntime.variables(ProcessPayloadBuilder + .variables() + .withProcessInstanceId(processInstance.getId()) + .build()); + + assertThat(parentVariablesAfterComnplete).extracting(VariableInstance::getName, + VariableInstance::getValue) + .containsOnly( + tuple("output-unmapped-variable-with-non-matching-connector-output-name", + "default"), + tuple("input-unmapped-variable-with-non-matching-connector-input-name", + "inTest"), + tuple("name", + "inName"), + tuple("age", + 20), + tuple("subprocess-input-var2", + 2), + tuple("subprocess-input-var3", + 3), + tuple("subprocess-out-var2", + 222), + tuple("subprocess-out-var1", + "outValue"), + + tuple("subprocess-input-var1", + "value1"), + tuple("subprocess-static-value", + "static some value"), + tuple("input-variable-name-1", + "fromSubprocessName") + ); + } + + private Task getTask(ProcessInstance subProcessInstance) { + List taskList = taskRuntime.tasks( + Pageable.of(0, + 50), + TaskPayloadBuilder + .tasks() + .withProcessInstanceId(subProcessInstance.getId()) + .build()) + .getContent(); + + assertThat(taskList).isNotEmpty(); + Task task = taskList.get(0); + + assertThat(task.getAssignee()).isNull(); + assertThat(task.getStatus()).isEqualTo(Task.TaskStatus.CREATED); + return task; + } + + private ProcessInstance getSubProcess(ProcessInstance processInstance) { + List subProcessInstanceList = processRuntime.processInstances( + Pageable.of(0, + 50), + ProcessPayloadBuilder + .processInstances() + .withParentProcessInstanceId(processInstance.getId()) + .build()) + .getContent(); + + assertThat(subProcessInstanceList).isNotEmpty(); + + ProcessInstance subProcessInstance = subProcessInstanceList.get(0); + + assertThat(subProcessInstance).isNotNull(); + assertThat(subProcessInstance.getParentId()).isEqualTo(processInstance.getId()); + return subProcessInstance; + } + + @Test + public void haveToPassNoVariablesCallActivityEmptyMappingWithTaskTest() { + securityUtil.logInAs("user"); + + ProcessInstance processInstance = processRuntime.start( + ProcessPayloadBuilder + .start() + .withProcessDefinitionKey(PARENT_PROCESS_CALL_ACTIVITY_EMPTY_MAPPING_WITH_TASK) + .build()); + assertThat(processInstance).isNotNull(); + + ProcessInstance subProcessInstance = getSubProcess(processInstance); + assertThat(subProcessInstance.getProcessDefinitionKey()).isEqualTo(SUB_PROCESS_CALL_ACTIVITY); + + Task task = getTask(subProcessInstance); + + taskRuntime.claim(TaskPayloadBuilder.claim().withTaskId(task.getId()).build()); + + List subProcVariables = processRuntime.variables(ProcessPayloadBuilder + .variables() + .withProcessInstanceId(subProcessInstance.getId()) + .build()); + + assertThat(subProcVariables).extracting(VariableInstance::getName, + VariableInstance::getValue).size().isEqualTo(6); + assertThat(subProcVariables).extracting(VariableInstance::getName, + VariableInstance::getValue) + .containsOnly( + tuple("subprocess-input-var2", + 2), + tuple("subprocess-input-var3", + 3), + tuple("subprocess-out-var2", + 222), + tuple("subprocess-out-var1", + "outValue"), + + tuple("subprocess-input-var1", + "value1"), + tuple("subprocess-static-value", + "static some value") + ); + + assertThat("my-task-call-activity").isEqualTo(task.getName()); + + Map variablesForTask = new HashMap<>(); + variablesForTask.put("input-variable-name-1", + "fromSubprocessName"); + + completeTask(task.getId(), + variablesForTask); + List parentVariablesAfterComnplete = processRuntime.variables(ProcessPayloadBuilder + .variables() + .withProcessInstanceId(processInstance.getId()) + .build()); + assertThat(parentVariablesAfterComnplete).extracting(VariableInstance::getName, + VariableInstance::getValue) + .containsOnly( + tuple("output-unmapped-variable-with-non-matching-connector-output-name", + "default"), + tuple("input-unmapped-variable-with-non-matching-connector-input-name", + "inTest"), + tuple("name", + "inName"), + tuple("age", + 20) + ); + } + + public void completeTask(String taskId, + Map variables) { + + Task completeTask = taskRuntime.complete(TaskPayloadBuilder + .complete() + .withTaskId(taskId) + .withVariables(variables) + .build()); + assertThat(completeTask).isNotNull(); + assertThat(completeTask.getStatus()).isEqualTo(Task.TaskStatus.COMPLETED); + } +} diff --git a/activiti-spring-boot-starter/src/test/java/org/activiti/spring/boot/process/ProcessRuntimeIT.java b/activiti-spring-boot-starter/src/test/java/org/activiti/spring/boot/process/ProcessRuntimeIT.java index 78cb8a45035..ae5ce2d6841 100644 --- a/activiti-spring-boot-starter/src/test/java/org/activiti/spring/boot/process/ProcessRuntimeIT.java +++ b/activiti-spring-boot-starter/src/test/java/org/activiti/spring/boot/process/ProcessRuntimeIT.java @@ -36,7 +36,7 @@ @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) -public class ProcessRuntimeIT { +public class ProcessRuntimeIT { private static final String CATEGORIZE_PROCESS = "categorizeProcess"; private static final String CATEGORIZE_HUMAN_PROCESS = "categorizeHumanProcess"; diff --git a/activiti-spring-boot-starter/src/test/java/org/activiti/spring/boot/tasks/TaskRuntimeVariableMappingIT.java b/activiti-spring-boot-starter/src/test/java/org/activiti/spring/boot/tasks/TaskRuntimeVariableMappingIT.java new file mode 100644 index 00000000000..32b1198ce3a --- /dev/null +++ b/activiti-spring-boot-starter/src/test/java/org/activiti/spring/boot/tasks/TaskRuntimeVariableMappingIT.java @@ -0,0 +1,304 @@ +/* + * Copyright 2018 Alfresco, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.activiti.spring.boot.tasks; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.activiti.api.model.shared.model.VariableInstance; +import org.activiti.api.process.model.ProcessInstance; +import org.activiti.api.process.model.builders.ProcessPayloadBuilder; +import org.activiti.api.process.runtime.ProcessRuntime; +import org.activiti.api.runtime.shared.query.Page; +import org.activiti.api.runtime.shared.query.Pageable; +import org.activiti.api.task.model.Task; +import org.activiti.api.task.model.Task.TaskStatus; +import org.activiti.api.task.model.builders.GetTasksPayloadBuilder; +import org.activiti.api.task.model.builders.TaskPayloadBuilder; +import org.activiti.api.task.model.payloads.GetTasksPayload; +import org.activiti.api.task.runtime.TaskRuntime; +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.After; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) +@ContextConfiguration +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class TaskRuntimeVariableMappingIT { + + private static final String TASK_VAR_MAPPING = "taskVarMapping"; + private static final String TASK_VAR_NO_MAPPING = "taskVariableNoMapping"; + private static final String TASK_EMPTY_VAR_MAPPING = "taskVariableEmptyMapping"; + + @Autowired + private TaskRuntime taskRuntime; + @Autowired + private ProcessRuntime processRuntime; + @Autowired + private SecurityUtil securityUtil; + @Autowired + private TaskCleanUpUtil taskCleanUpUtil; + + @Autowired + private ProcessCleanUpUtil processCleanUpUtil; + + @After + public void cleanUp() { + processCleanUpUtil.cleanUpWithAdmin(); + taskCleanUpUtil.cleanUpWithAdmin(); + } + + @Test + public void processTaskVarMapping() { + securityUtil.logInAs("garth"); + ProcessInstance process = processRuntime.start(ProcessPayloadBuilder.start() + .withProcessDefinitionKey(TASK_VAR_MAPPING) + .build()); + + Task task = checkTasks(process.getId()); + + assertThat(task.getName()).isEqualTo("testSimpleTask"); + + List procVariables = processRuntime.variables(ProcessPayloadBuilder + .variables() + .withProcessInstanceId(process.getId()) + .build()); + assertThat(procVariables) + .isNotNull() + .extracting(VariableInstance::getName, + VariableInstance::getValue) + .containsOnly(tuple("process_variable_unmapped_1", + "unmapped1Value"), + tuple("process_variable_inputmap_1", + "inputmap1Value"), + tuple("process_variable_outputmap_1", + "outputmap1Value")); + + List taskVariables = taskRuntime.variables(TaskPayloadBuilder + .variables() + .withTaskId(task.getId()) + .build()); + assertThat(taskVariables) + .isNotNull() + .extracting(VariableInstance::getName, + VariableInstance::getValue) + .containsOnly(tuple("task_input_variable_name_1", + "inputmap1Value")); + + Map variables = new HashMap<>(); + variables.put("task_input_variable_name_1", + "outputValue"); //This should not be set to 'process_variable_inputmap_1' + variables.put("task_output_variable_name_1", + "outputTaskValue"); //This should be set to 'process_variable_outputmap_1' + + completeTask(task.getId(), + variables); + + procVariables = processRuntime.variables(ProcessPayloadBuilder + .variables() + .withProcessInstanceId(process.getId()) + .build()); + assertThat(procVariables) + .isNotNull() + .extracting(VariableInstance::getName, + VariableInstance::getValue) + .containsOnly( + tuple("process_variable_unmapped_1", + "unmapped1Value"), + tuple("process_variable_inputmap_1", + "inputmap1Value"), + tuple("process_variable_outputmap_1", + "outputTaskValue") + + ); + + processRuntime.delete(ProcessPayloadBuilder.delete().withProcessInstance(process).build()); + } + + private void completeTask(String taskId, + Map variables) { + + Task completeTask = taskRuntime.complete(TaskPayloadBuilder + .complete() + .withTaskId(taskId) + .withVariables(variables) + .build()); + assertThat(completeTask).isNotNull(); + assertThat(completeTask.getStatus()).isEqualTo(TaskStatus.COMPLETED); + } + + @Test + public void allVariablesShouldBePassedWhenThereIsNoMapping() { + + securityUtil.logInAs("garth"); + ProcessInstance process = processRuntime.start(ProcessPayloadBuilder.start() + .withProcessDefinitionKey(TASK_VAR_NO_MAPPING) + .build()); + + Task task = checkTasks(process.getId()); + + assertThat(task.getName()).isEqualTo("testSimpleTask"); + + List procVariables = processRuntime.variables(ProcessPayloadBuilder + .variables() + .withProcessInstanceId(process.getId()) + .build()); + + assertThat(procVariables) + .isNotNull() + .extracting(VariableInstance::getName, + VariableInstance::getValue) + .containsOnly(tuple("process_variable_unmapped_1", + "unmapped1Value"), + tuple("process_variable_inputmap_1", + "inputmap1Value"), + tuple("process_variable_outputmap_1", + "outputmap1Value")); + + List taskVariables = taskRuntime.variables(TaskPayloadBuilder + .variables() + .withTaskId(task.getId()) + .build()); + + assertThat(taskVariables) + .isNotNull() + .extracting(VariableInstance::getName, + VariableInstance::getValue) + .containsOnly(tuple("process_variable_unmapped_1", + "unmapped1Value"), + tuple("process_variable_inputmap_1", + "inputmap1Value"), + tuple("process_variable_outputmap_1", + "outputmap1Value")); + + Map variables = new HashMap<>(); + variables.put("task_input_variable_name_1", + "outputValue"); + variables.put("task_output_variable_name_1", + "outputTaskValue"); + + completeTask(task.getId(), + variables); + + procVariables = processRuntime.variables(ProcessPayloadBuilder + .variables() + .withProcessInstanceId(process.getId()) + .build()); + assertThat(procVariables) + .isNotNull() + .extracting(VariableInstance::getName, + VariableInstance::getValue) + .containsExactlyInAnyOrder( + tuple("process_variable_unmapped_1", + "unmapped1Value"), + tuple("process_variable_inputmap_1", + "inputmap1Value"), + tuple("process_variable_outputmap_1", + "outputmap1Value"), + tuple("task_input_variable_name_1", + "outputValue"), + tuple("task_output_variable_name_1", + "outputTaskValue") + //since there is no mapping for outputs either, all the variables are passed + ); + + processRuntime.delete(ProcessPayloadBuilder.delete().withProcessInstance(process).build()); + } + + private Task checkTasks(String processInstanceId) { + GetTasksPayload getTasksPayload = new GetTasksPayloadBuilder().withProcessInstanceId(processInstanceId).build(); + Page tasks = taskRuntime.tasks(Pageable.of(0, + 50), + getTasksPayload); + + assertThat(tasks.getContent()).hasSize(1); + return tasks.getContent().get(0); + } + + @Test + public void taskShouldHaveNoVariablesWhenMappingIsEmpty() { + + securityUtil.logInAs("garth"); + ProcessInstance process = processRuntime.start(ProcessPayloadBuilder.start() + .withProcessDefinitionKey(TASK_EMPTY_VAR_MAPPING) + .build()); + + Task task = checkTasks(process.getId()); + + assertThat(task.getName()).isEqualTo("testSimpleTask"); + + List procVariables = processRuntime.variables(ProcessPayloadBuilder + .variables() + .withProcessInstanceId(process.getId()) + .build()); + + assertThat(procVariables) + .isNotNull() + .extracting(VariableInstance::getName, + VariableInstance::getValue) + .containsOnly(tuple("process_variable_unmapped_1", + "unmapped1Value"), + tuple("process_variable_inputmap_1", + "inputmap1Value"), + tuple("process_variable_outputmap_1", + "outputmap1Value")); + + List taskVariables = taskRuntime.variables(TaskPayloadBuilder + .variables() + .withTaskId(task.getId()) + .build()); + + assertThat(taskVariables) + .isEmpty(); + + Map variables = new HashMap<>(); + variables.put("task_input_variable_name_1", + "outputValue"); + variables.put("task_output_variable_name_1", + "outputTaskValue"); + + completeTask(task.getId(), + variables); + + assertThat(procVariables) + .isNotNull() + .extracting(VariableInstance::getName, + VariableInstance::getValue) + .containsOnly(tuple("process_variable_unmapped_1", + "unmapped1Value"), + tuple("process_variable_inputmap_1", + "inputmap1Value"), + tuple("process_variable_outputmap_1", + "outputmap1Value")); + + processRuntime.delete(ProcessPayloadBuilder.delete().withProcessInstance(process).build()); + } +} diff --git a/activiti-spring-boot-starter/src/test/resources/processes/call-activity-subprocess-variable-mapping-extensions.json b/activiti-spring-boot-starter/src/test/resources/processes/call-activity-subprocess-variable-mapping-extensions.json new file mode 100644 index 00000000000..8a7b98357b0 --- /dev/null +++ b/activiti-spring-boot-starter/src/test/resources/processes/call-activity-subprocess-variable-mapping-extensions.json @@ -0,0 +1,45 @@ +{ + "id":"subprocess-fb5f2386-709a-4947-9aa0-bbf31497384g", + "name":"mySubProcess123.bpmn", + "extensions": { + "properties": { + "subprocess-input-var1-id": { + "id": "subprocess-input-var1-id", + "name": "subprocess-input-var1", + "type": "string", + "required": true, + "value": "value1" + }, + "subprocess-input-var2-id": { + "id": "subprocess-input-var2-id", + "name": "subprocess-input-var2", + "type": "integer", + "value": 2 + }, + "subprocess-input-var3-id": { + "id": "subprocess-input-var3-id", + "name": "subprocess-input-var3", + "type": "integer", + "value": 3 + }, + "subprocess-static-value-id": { + "id": "subprocess-static-value-id", + "name": "subprocess-static-value", + "type": "string", + "value": "static some value" + }, + "subprocess-out-vari-1id": { + "id": "subprocess-out-var1-id", + "name": "subprocess-out-var1", + "type": "string", + "value": "outValue" + }, + "subprocess-out-var2-id": { + "id": "subprocess-out-var2-id", + "name": "subprocess-out-var2", + "type": "integer", + "value": 222 + } + } + } +} \ No newline at end of file diff --git a/activiti-spring-boot-starter/src/test/resources/processes/call-activity-variable-mapping-empty-mapping-extensions.json b/activiti-spring-boot-starter/src/test/resources/processes/call-activity-variable-mapping-empty-mapping-extensions.json new file mode 100644 index 00000000000..9ad50f30f00 --- /dev/null +++ b/activiti-spring-boot-starter/src/test/resources/processes/call-activity-variable-mapping-empty-mapping-extensions.json @@ -0,0 +1,37 @@ +{ + "id": "parentproc-843144bc-3797-40db-8edc-d23190b118e6", + "name": "parentProcessCallActivityMappingEmptyMapping.bpmn20.xml", + "extensions": { + "properties": { + "age-id": { + "id": "age-id", + "name": "age", + "type": "integer", + "value": 20 + }, + "name-id": { + "id": "name-id", + "name": "name", + "type": "string", + "required": true, + "value": "inName" + }, + "input-unmapped-variable-with-non-matching-name-id": { + "id": "input-unmapped-variable-with-non-matching-connector-input-name-id", + "name": "input-unmapped-variable-with-non-matching-connector-input-name", + "type": "string", + "required": false, + "value": "inTest" + }, + "output-unmapped-variable-with-non-matching-name-id": { + "id": "output-unmapped-variable-with-non-matching-name-id", + "name": "output-unmapped-variable-with-non-matching-connector-output-name", + "type": "string", + "required": false, + "value": "default" + } + }, + "mappings": { + } + } +} \ No newline at end of file diff --git a/activiti-spring-boot-starter/src/test/resources/processes/call-activity-variable-mapping-empty-mapping-with-task-extensions.json b/activiti-spring-boot-starter/src/test/resources/processes/call-activity-variable-mapping-empty-mapping-with-task-extensions.json new file mode 100644 index 00000000000..a17f2a5f6b5 --- /dev/null +++ b/activiti-spring-boot-starter/src/test/resources/processes/call-activity-variable-mapping-empty-mapping-with-task-extensions.json @@ -0,0 +1,43 @@ +{ + "id": "parentproc-843144bc-3797-40db-8edc-d23190b118e7", + "name": "parentProcessCallActivityMappingEmptyMappingWithTask.bpmn20.xml", + "extensions": { + "properties": { + "age-id": { + "id": "age-id", + "name": "age", + "type": "integer", + "value": 20 + }, + "name-id": { + "id": "name-id", + "name": "name", + "type": "string", + "required": true, + "value": "inName" + }, + "input-unmapped-variable-with-non-matching-name-id": { + "id": "input-unmapped-variable-with-non-matching-connector-input-name-id", + "name": "input-unmapped-variable-with-non-matching-connector-input-name", + "type": "string", + "required": false, + "value": "inTest" + }, + "output-unmapped-variable-with-non-matching-name-id": { + "id": "output-unmapped-variable-with-non-matching-name-id", + "name": "output-unmapped-variable-with-non-matching-connector-output-name", + "type": "string", + "required": false, + "value": "default" + } + }, + "mappings": { + "Task_0v0jw9c": { + "inputs": { + }, + "outputs": { + } + } + } + } +} \ No newline at end of file diff --git a/activiti-spring-boot-starter/src/test/resources/processes/call-activity-variable-mapping-extensions.json b/activiti-spring-boot-starter/src/test/resources/processes/call-activity-variable-mapping-extensions.json new file mode 100644 index 00000000000..a6ea1d777f3 --- /dev/null +++ b/activiti-spring-boot-starter/src/test/resources/processes/call-activity-variable-mapping-extensions.json @@ -0,0 +1,80 @@ +{ + "id":"parentproc-843144bc-3797-40db-8edc-d23190b118e5", + "name":"myProcess234.bpmn", + "extensions": { + "properties": { + "age-id": { + "id": "age-id", + "name": "age", + "type": "integer", + "value": 20 + }, + "name-id": { + "id": "name-id", + "name": "name", + "type": "string", + "required": true, + "value": "inName" + }, + "input-unmapped-variable-with-non-matching-name-id": { + "id": "input-unmapped-variable-with-non-matching-connector-input-name-id", + "name": "input-unmapped-variable-with-non-matching-connector-input-name", + "type": "string", + "required": false, + "value": "inTest" + }, + "output-unmapped-variable-with-non-matching-name-id": { + "id": "output-unmapped-variable-with-non-matching-name-id", + "name": "output-unmapped-variable-with-non-matching-connector-output-name", + "type": "string", + "required": false, + "value": "default" + } + }, + "mappings": { + "Task_0v0jw9c": { + "inputs": { + "subprocess-input-var1": { + "type": "variable", + "value": "name" + }, + "subprocess-input-var2": { + "type": "variable", + "value": "age" + }, + "var-not-exist-in-subprocess-extension-file": { + "type": "variable", + "value": "age" + }, + "subprocess-input-var3": { + "type": "value", + "value": 5 + }, + "input-static-value": { + "type": "static_value", + "value": "a static value" + }, + "input-not-existing-value": { + "type": "variable", + "value": "not-existing" + } + }, + "outputs": { + "name": { + "type": "variable", + "value" : "subprocess-out-var1"}, + "age": { + "type": "variable", + "value" : "subprocess-out-var2"}, + "from-subprocess-static-value": { + "type": "static_value", + "value": "output-unmapped-variable-with-non-matching-name-id" + }, + "not-existing": { + "type": "variable", + "value" : "subprocess-out-var1"} + } + } + } + } +} \ No newline at end of file diff --git a/activiti-spring-boot-starter/src/test/resources/processes/parentProcessCallActivityMapping.bpmn20.xml b/activiti-spring-boot-starter/src/test/resources/processes/parentProcessCallActivityMapping.bpmn20.xml new file mode 100644 index 00000000000..083309e06b3 --- /dev/null +++ b/activiti-spring-boot-starter/src/test/resources/processes/parentProcessCallActivityMapping.bpmn20.xml @@ -0,0 +1,52 @@ + + + + + + SequenceFlow_0pb8ld7 + + + + SequenceFlow_0pb8ld7 + SequenceFlow_0cv1cal + + + + SequenceFlow_0cv1cal + SequenceFlow_1e705xf + + + + SequenceFlow_1e705xf + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/activiti-spring-boot-starter/src/test/resources/processes/parentProcessCallActivityMappingEmptyMapping.bpmn20.xml b/activiti-spring-boot-starter/src/test/resources/processes/parentProcessCallActivityMappingEmptyMapping.bpmn20.xml new file mode 100644 index 00000000000..ecdc9964be9 --- /dev/null +++ b/activiti-spring-boot-starter/src/test/resources/processes/parentProcessCallActivityMappingEmptyMapping.bpmn20.xml @@ -0,0 +1,52 @@ + + + + + + SequenceFlow_0pb8ld7 + + + + SequenceFlow_0pb8ld7 + SequenceFlow_0cv1cal + + + + SequenceFlow_0cv1cal + SequenceFlow_1e705xf + + + + SequenceFlow_1e705xf + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/activiti-spring-boot-starter/src/test/resources/processes/parentProcessCallActivityMappingEmptyMappingWithTask.bpmn20.xml b/activiti-spring-boot-starter/src/test/resources/processes/parentProcessCallActivityMappingEmptyMappingWithTask.bpmn20.xml new file mode 100644 index 00000000000..b9180a06edf --- /dev/null +++ b/activiti-spring-boot-starter/src/test/resources/processes/parentProcessCallActivityMappingEmptyMappingWithTask.bpmn20.xml @@ -0,0 +1,52 @@ + + + + + + SequenceFlow_0pb8ld7 + + + + SequenceFlow_0pb8ld7 + SequenceFlow_0cv1cal + + + + SequenceFlow_0cv1cal + SequenceFlow_1e705xf + + + + SequenceFlow_1e705xf + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/activiti-spring-boot-starter/src/test/resources/processes/subProcessCallActivityMapping.bpmn20.xml b/activiti-spring-boot-starter/src/test/resources/processes/subProcessCallActivityMapping.bpmn20.xml new file mode 100644 index 00000000000..58737525dbd --- /dev/null +++ b/activiti-spring-boot-starter/src/test/resources/processes/subProcessCallActivityMapping.bpmn20.xml @@ -0,0 +1,48 @@ + + + + + + SequenceFlow_1wml1p8 + + + SequenceFlow_1wml1p8 + SequenceFlow_0xx61qa + + + + SequenceFlow_0xx61qa + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/activiti-spring-boot-starter/src/test/resources/processes/task-variable-empty-mapping-extensions.json b/activiti-spring-boot-starter/src/test/resources/processes/task-variable-empty-mapping-extensions.json new file mode 100644 index 00000000000..0b00e74aee1 --- /dev/null +++ b/activiti-spring-boot-starter/src/test/resources/processes/task-variable-empty-mapping-extensions.json @@ -0,0 +1,36 @@ +{ + "id":"taskVariableEmptyMapping", + "type": "PROCESS", + "name": "process", + "extensions": { + "properties": { + "process-variable-unmapped-1-id": { + "id": "process-variable-unmapped-1-id", + "name": "process_variable_unmapped_1", + "type": "string", + "required": true, + "value": "unmapped1Value" + }, + "process-variable-inputmap-1-id": { + "id": "process-variable-inputmap-1-id", + "name": "process_variable_inputmap_1", + "type": "string", + "required": true, + "value": "inputmap1Value" + }, + "process-variable-outputmap-1-id": { + "id": "process-variable-outputmap-1-id", + "name": "process_variable_outputmap_1", + "type": "string", + "required": true, + "value": "outputmap1Value" + } + }, + "mappings": { + "simpleTask": { + "inputs": {}, + "outputs": {} + } + } + } +} \ No newline at end of file diff --git a/activiti-spring-boot-starter/src/test/resources/processes/task-variable-empty-mapping.bpmn20.xml b/activiti-spring-boot-starter/src/test/resources/processes/task-variable-empty-mapping.bpmn20.xml new file mode 100644 index 00000000000..1651ccdc005 --- /dev/null +++ b/activiti-spring-boot-starter/src/test/resources/processes/task-variable-empty-mapping.bpmn20.xml @@ -0,0 +1,51 @@ + + + + + + SequenceFlow_0j0jsvc + + + SequenceFlow_1rg63po + + + + SequenceFlow_0j0jsvc + SequenceFlow_1sdpteq + + + + SequenceFlow_1sdpteq + SequenceFlow_1rg63po + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/activiti-spring-boot-starter/src/test/resources/processes/task-variable-mapping-extensions.json b/activiti-spring-boot-starter/src/test/resources/processes/task-variable-mapping-extensions.json new file mode 100755 index 00000000000..4fa1652bd58 --- /dev/null +++ b/activiti-spring-boot-starter/src/test/resources/processes/task-variable-mapping-extensions.json @@ -0,0 +1,46 @@ +{ + "id": "taskVarMapping", + "type": "PROCESS", + "name": "process", + "extensions": { + "properties": { + "process-variable-unmapped-1-id": { + "id": "process-variable-unmapped-1-id", + "name": "process_variable_unmapped_1", + "type": "string", + "required": true, + "value": "unmapped1Value" + }, + "process-variable-inputmap-1-id": { + "id": "process-variable-inputmap-1-id", + "name": "process_variable_inputmap_1", + "type": "string", + "required": true, + "value": "inputmap1Value" + }, + "process-variable-outputmap-1-id": { + "id": "process-variable-outputmap-1-id", + "name": "process_variable_outputmap_1", + "type": "string", + "required": true, + "value": "outputmap1Value" + } + }, + "mappings": { + "simpleTask": { + "inputs": { + "task_input_variable_name_1": { + "type": "variable", + "value": "process_variable_inputmap_1" + } + }, + "outputs": { + "process_variable_outputmap_1": { + "type": "variable", + "value": "task_output_variable_name_1" + } + } + } + } +} +} \ No newline at end of file diff --git a/activiti-spring-boot-starter/src/test/resources/processes/task-variable-mapping.bpmn20.xml b/activiti-spring-boot-starter/src/test/resources/processes/task-variable-mapping.bpmn20.xml new file mode 100755 index 00000000000..9a19040e3ee --- /dev/null +++ b/activiti-spring-boot-starter/src/test/resources/processes/task-variable-mapping.bpmn20.xml @@ -0,0 +1,51 @@ + + + + + + SequenceFlow_0j0jsvc + + + SequenceFlow_1rg63po + + + + SequenceFlow_0j0jsvc + SequenceFlow_1sdpteq + + + + SequenceFlow_1sdpteq + SequenceFlow_1rg63po + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/activiti-spring-boot-starter/src/test/resources/processes/task-variable-no-mapping-extensions.json b/activiti-spring-boot-starter/src/test/resources/processes/task-variable-no-mapping-extensions.json new file mode 100644 index 00000000000..7cded8ed869 --- /dev/null +++ b/activiti-spring-boot-starter/src/test/resources/processes/task-variable-no-mapping-extensions.json @@ -0,0 +1,31 @@ +{ + "id":"taskVariableNoMapping", + "type": "PROCESS", + "name": "process", + "extensions": { + "properties": { + "process-variable-unmapped-1-id": { + "id": "process-variable-unmapped-1-id", + "name": "process_variable_unmapped_1", + "type": "string", + "required": true, + "value": "unmapped1Value" + }, + "process-variable-inputmap-1-id": { + "id": "process-variable-inputmap-1-id", + "name": "process_variable_inputmap_1", + "type": "string", + "required": true, + "value": "inputmap1Value" + }, + "process-variable-outputmap-1-id": { + "id": "process-variable-outputmap-1-id", + "name": "process_variable_outputmap_1", + "type": "string", + "required": true, + "value": "outputmap1Value" + } + }, + "mappings": {} + } +} \ No newline at end of file diff --git a/activiti-spring-boot-starter/src/test/resources/processes/task-variable-no-mapping.bpmn20.xml b/activiti-spring-boot-starter/src/test/resources/processes/task-variable-no-mapping.bpmn20.xml new file mode 100644 index 00000000000..db386a52e24 --- /dev/null +++ b/activiti-spring-boot-starter/src/test/resources/processes/task-variable-no-mapping.bpmn20.xml @@ -0,0 +1,51 @@ + + + + + + SequenceFlow_0j0jsvc + + + SequenceFlow_1rg63po + + + + SequenceFlow_0j0jsvc + SequenceFlow_1sdpteq + + + + SequenceFlow_1sdpteq + SequenceFlow_1rg63po + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/activiti-spring-process-extensions/src/main/java/org/activiti/spring/process/ProcessVariablesInitiator.java b/activiti-spring-process-extensions/src/main/java/org/activiti/spring/process/ProcessVariablesInitiator.java index cfb21f4ca7b..5978367f3be 100644 --- a/activiti-spring-process-extensions/src/main/java/org/activiti/spring/process/ProcessVariablesInitiator.java +++ b/activiti-spring-process-extensions/src/main/java/org/activiti/spring/process/ProcessVariablesInitiator.java @@ -45,21 +45,39 @@ public ProcessVariablesInitiator(ProcessExtensionService processExtensionService this.variableValidationService = variableValidationService; } - @Override - public ProcessInstance createAndStartProcessInstanceWithInitialFlowElement(ProcessDefinition processDefinition, String businessKey, String processInstanceName, FlowElement initialFlowElement, Process process, Map variables, Map transientVariables, boolean startProcessInstance) { + public Map calculateVariablesFromExtensionFile(ProcessDefinition processDefinition, + Map variables) { + Map processedVariables = new HashMap<>(); if (processExtensionService.hasExtensionsFor(processDefinition)) { ProcessExtensionModel processExtensionModel = processExtensionService.getExtensionsFor(processDefinition); + processExtensionService.cache(processDefinition); Map variableDefinitionMap = processExtensionModel.getExtensions().getProperties(); - Map processedVariables = processVariables(variables, variableDefinitionMap); - Set missingRequiredVars = checkRequiredVariables(processedVariables, variableDefinitionMap); + processedVariables = processVariables(variables, + variableDefinitionMap); + + Set missingRequiredVars = checkRequiredVariables(processedVariables, + variableDefinitionMap); if (!missingRequiredVars.isEmpty()) { - throw new ActivitiException("Can't start process '" + processDefinition.getKey() + "' without required variables - " + String.join(", ", missingRequiredVars)); + throw new ActivitiException("Can't start process '" + processDefinition.getKey() + "' without required variables - " + String.join(", ", + missingRequiredVars)); } - Set varsWithMismatchedTypes = validateVariablesAgainstDefinitions(processedVariables, variableDefinitionMap); - if(!varsWithMismatchedTypes.isEmpty()){ - throw new ActivitiException("Can't start process '" + processDefinition.getKey() + "' as variables fail type validation - " + String.join(", ", varsWithMismatchedTypes)); + Set varsWithMismatchedTypes = validateVariablesAgainstDefinitions(processedVariables, + variableDefinitionMap); + if (!varsWithMismatchedTypes.isEmpty()) { + throw new ActivitiException("Can't start process '" + processDefinition.getKey() + "' as variables fail type validation - " + String.join(", ", + varsWithMismatchedTypes)); } + } + + return processedVariables; + } + + + @Override + public ProcessInstance createAndStartProcessInstanceWithInitialFlowElement(ProcessDefinition processDefinition, String businessKey, String processInstanceName, FlowElement initialFlowElement, Process process, Map variables, Map transientVariables, boolean startProcessInstance) { + if (processExtensionService.hasExtensionsFor(processDefinition)) { + Map processedVariables = calculateVariablesFromExtensionFile(processDefinition, variables); return super.createAndStartProcessInstanceWithInitialFlowElement(processDefinition, businessKey, processInstanceName, initialFlowElement, process, processedVariables, transientVariables, startProcessInstance); } return super.createAndStartProcessInstanceWithInitialFlowElement(processDefinition, businessKey, processInstanceName, initialFlowElement, process, variables, transientVariables, startProcessInstance); diff --git a/activiti-spring-process-extensions/src/main/java/org/activiti/spring/process/autoconfigure/ProcessExtensionsAutoConfiguration.java b/activiti-spring-process-extensions/src/main/java/org/activiti/spring/process/autoconfigure/ProcessExtensionsAutoConfiguration.java index 722360283a8..66aab939704 100644 --- a/activiti-spring-process-extensions/src/main/java/org/activiti/spring/process/autoconfigure/ProcessExtensionsAutoConfiguration.java +++ b/activiti-spring-process-extensions/src/main/java/org/activiti/spring/process/autoconfigure/ProcessExtensionsAutoConfiguration.java @@ -14,8 +14,6 @@ package org.activiti.spring.process.autoconfigure; import com.fasterxml.jackson.databind.ObjectMapper; -import org.activiti.engine.cfg.AbstractProcessEngineConfigurator; -import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl; import org.activiti.spring.process.ProcessExtensionService; import org.activiti.spring.process.ProcessVariablesInitiator; import org.activiti.spring.process.model.ProcessExtensionModel; diff --git a/activiti-spring-process-extensions/src/main/java/org/activiti/spring/process/model/Extension.java b/activiti-spring-process-extensions/src/main/java/org/activiti/spring/process/model/Extension.java index a0fe1c8e471..e3de84f6474 100644 --- a/activiti-spring-process-extensions/src/main/java/org/activiti/spring/process/model/Extension.java +++ b/activiti-spring-process-extensions/src/main/java/org/activiti/spring/process/model/Extension.java @@ -61,4 +61,20 @@ public VariableDefinition getPropertyByName(String name){ return null; } + + public boolean hasEmptyInputsMapping(String elementId){ + ProcessVariablesMapping processVariablesMapping = mappings.get(elementId); + return processVariablesMapping != null && processVariablesMapping.getInputs().size() == 0; + } + + public boolean hasEmptyOutputsMapping(String elementId){ + ProcessVariablesMapping processVariablesMapping = mappings.get(elementId); + return processVariablesMapping != null && processVariablesMapping.getOutputs().size() == 0; + } + + public boolean hasNoMapping(String taskId){ + return mappings.get(taskId) == null; + } + + } diff --git a/activiti-spring-process-extensions/src/test/java/org/activiti/spring/process/model/ExtensionTest.java b/activiti-spring-process-extensions/src/test/java/org/activiti/spring/process/model/ExtensionTest.java new file mode 100644 index 00000000000..a8795825a4d --- /dev/null +++ b/activiti-spring-process-extensions/src/test/java/org/activiti/spring/process/model/ExtensionTest.java @@ -0,0 +1,56 @@ +package org.activiti.spring.process.model; + +import java.util.Collections; +import java.util.HashMap; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.MockitoAnnotations.initMocks; + +public class ExtensionTest { + + @Mock + private ProcessVariablesMapping processVariablesMapping; + + @Before + public void setUp() { + initMocks(this); + given(processVariablesMapping.getInputs()).willReturn(Collections.emptyMap()); + given(processVariablesMapping.getOutputs()).willReturn(Collections.emptyMap()); + } + + @Test + public void hasEmptyInputsMappingShouldReturnFalseWhenInputsMapIsEmpty(){ + Extension extension = new Extension(); + HashMap mapping = new HashMap<>(); + mapping.put("elementId", processVariablesMapping); + extension.setMappings(mapping); + + assertThat(extension.hasEmptyInputsMapping("elementId")).isTrue(); + } + + @Test + public void hasEmptyOutputsMappingShouldReturnFalseWhenOutputsMapIsEmpty(){ + Extension extension = new Extension(); + HashMap mapping = new HashMap<>(); + mapping.put("elementId", processVariablesMapping); + extension.setMappings(mapping); + + assertThat(extension.hasEmptyOutputsMapping("elementId")).isTrue(); + } + + @Test + public void hasNoMappingShouldReturnFalseWhenThereIsMapping(){ + Extension extension = new Extension(); + HashMap mapping = new HashMap<>(); + mapping.put("elementId", processVariablesMapping); + extension.setMappings(mapping); + + assertThat(extension.hasNoMapping("elementId")).isFalse(); + } + +}