Skip to content

Support providing initial identity links when starting a process #3407

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public ProcessInstance execute(CommandContext commandContext) {
processInstanceHelper = processEngineConfiguration.getProcessInstanceHelper();
ExecutionEntity processInstance = (ExecutionEntity) processInstanceHelper.createProcessInstance(processDefinition, businessKey, businessStatus,
processInstanceName, overrideDefinitionTenantId, predefinedProcessInstanceId, variables, transientVariables, callbackId, callbackType,
referenceId, referenceType, stageInstanceId, false);
referenceId, referenceType, stageInstanceId, userIdentityLinks, groupIdentityLinks, false);
ExecutionEntity execution = processInstance.getExecutions().get(0);
Process process = ProcessDefinitionUtil.getProcess(processInstance.getProcessDefinitionId());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.BpmnModel;
Expand Down Expand Up @@ -75,6 +76,8 @@ public class StartProcessInstanceCmd<T> implements Command<ProcessInstance>, Ser
protected FormInfo extraFormInfo;
protected String extraFormOutcome;
protected boolean fallbackToDefaultTenant;
protected Map<String, Set<String>> userIdentityLinks;
protected Map<String, Set<String>> groupIdentityLinks;
protected ProcessInstanceHelper processInstanceHelper;

public StartProcessInstanceCmd(String processDefinitionKey, String processDefinitionId, String businessKey, Map<String, Object> variables) {
Expand Down Expand Up @@ -112,6 +115,8 @@ public StartProcessInstanceCmd(ProcessInstanceBuilderImpl processInstanceBuilder
this.extraFormInfo = processInstanceBuilder.getExtraFormInfo();
this.extraFormOutcome = processInstanceBuilder.getExtraFormOutcome();
this.fallbackToDefaultTenant = processInstanceBuilder.isFallbackToDefaultTenant();
this.userIdentityLinks = processInstanceBuilder.getUserIdentityLinks();
this.groupIdentityLinks = processInstanceBuilder.getGroupIdentityLinks();
this.businessStatus = processInstanceBuilder.getBusinessStatus();
}

Expand Down Expand Up @@ -238,7 +243,7 @@ protected boolean isFormFieldValidationEnabled(ProcessEngineConfigurationImpl pr
protected ProcessInstance startProcessInstance(ProcessDefinition processDefinition) {
return processInstanceHelper.createProcessInstance(processDefinition, businessKey, businessStatus, processInstanceName,
overrideDefinitionTenantId, predefinedProcessInstanceId, variables, transientVariables,
callbackId, callbackType, referenceId, referenceType, stageInstanceId, true);
callbackId, callbackType, referenceId, referenceType, stageInstanceId, userIdentityLinks, groupIdentityLinks, true);
}

protected boolean hasStartFormData() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
package org.flowable.engine.impl.runtime;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.flowable.common.engine.api.FlowableIllegalArgumentException;
import org.flowable.engine.impl.RuntimeServiceImpl;
Expand Down Expand Up @@ -52,6 +54,8 @@ public class ProcessInstanceBuilderImpl implements ProcessInstanceBuilder {
protected FormInfo extraFormInfo;
protected String extraFormOutcome;
protected boolean fallbackToDefaultTenant;
protected Map<String, Set<String>> userIdentityLinks;
protected Map<String, Set<String>> groupIdentityLinks;

public ProcessInstanceBuilderImpl(RuntimeServiceImpl runtimeService) {
this.runtimeService = runtimeService;
Expand Down Expand Up @@ -92,7 +96,7 @@ public ProcessInstanceBuilder businessKey(String businessKey) {
this.businessKey = businessKey;
return this;
}

@Override
public ProcessInstanceBuilder businessStatus(String businessStatus) {
this.businessStatus = businessStatus;
Expand All @@ -104,7 +108,7 @@ public ProcessInstanceBuilder callbackId(String callbackId) {
this.callbackId = callbackId;
return this;
}

@Override
public ProcessInstanceBuilder callbackType(String callbackType) {
this.callbackType = callbackType;
Expand Down Expand Up @@ -134,7 +138,7 @@ public ProcessInstanceBuilder tenantId(String tenantId) {
this.tenantId = tenantId;
return this;
}

@Override
public ProcessInstanceBuilder overrideProcessDefinitionTenantId(String tenantId) {
this.overrideDefinitionTenantId = tenantId;
Expand Down Expand Up @@ -232,13 +236,72 @@ public ProcessInstanceBuilder formVariables(Map<String, Object> formVariables, F
return this;
}


@Override
public ProcessInstanceBuilder fallbackToDefaultTenant() {
this.fallbackToDefaultTenant = true;
return this;
}

@Override
public ProcessInstanceBuilder userIdentityLinks(Map<String, Set<String>> userIdentityLinks) {
if (userIdentityLinks != null) {
if (this.userIdentityLinks == null) {
this.userIdentityLinks = new HashMap<>();
}
mergeIdentityLinks(this.userIdentityLinks, userIdentityLinks);
}
return this;
}

@Override
public ProcessInstanceBuilder userIdentityLink(String identityLinkType, String user) {
if (identityLinkType != null && user != null) {
Set<String> users = new HashSet<>();
users.add(user);
Map<String, Set<String>> identityLinkMap = new HashMap<>();
identityLinkMap.put(identityLinkType, users);
userIdentityLinks(identityLinkMap);
}
return this;
}

@Override
public ProcessInstanceBuilder groupIdentityLinks(Map<String, Set<String>> groupIdentityLinks) {
if (groupIdentityLinks != null) {
if (this.groupIdentityLinks == null) {
this.groupIdentityLinks = new HashMap<>();
}
mergeIdentityLinks(this.groupIdentityLinks, groupIdentityLinks);
}
return this;
}

@Override
public ProcessInstanceBuilder groupIdentityLink(String identityLinkType, String group) {
if (identityLinkType != null && group != null) {
Set<String> groups = new HashSet<>();
groups.add(group);
Map<String, Set<String>> identityLinkMap = new HashMap<>();
identityLinkMap.put(identityLinkType, groups);
groupIdentityLinks(identityLinkMap);
}
return this;
}

private void mergeIdentityLinks(Map<String, Set<String>> target, Map<String, Set<String>> additionalLinks) {
for (Map.Entry<String, Set<String>> additionalLink : additionalLinks.entrySet()) {
String linkType = additionalLink.getKey();
Set<String> parties = additionalLink.getValue();
if (linkType != null && parties != null) {
target.merge(linkType, parties, (party1, party2) -> {
Set<String> combinedParties = new HashSet<String>(party1);
combinedParties.addAll(party2);
return combinedParties;
});
}
}
}

@Override
public ProcessInstance start() {
return runtimeService.startProcessInstance(this);
Expand Down Expand Up @@ -272,7 +335,7 @@ public String getProcessInstanceName() {
public String getBusinessKey() {
return businessKey;
}

public String getBusinessStatus() {
return businessStatus;
}
Expand Down Expand Up @@ -340,4 +403,12 @@ public boolean isFallbackToDefaultTenant() {
return fallbackToDefaultTenant;
}

public Map<String, Set<String>> getUserIdentityLinks() {
return userIdentityLinks;
}

public Map<String, Set<String>> getGroupIdentityLinks() {
return groupIdentityLinks;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.constants.BpmnXMLConstants;
Expand Down Expand Up @@ -76,12 +77,13 @@ public ProcessInstance createProcessInstance(ProcessDefinition processDefinition
Map<String, Object> variables, Map<String, Object> transientVariables) {

return createProcessInstance(processDefinition, businessKey, businessStatus, processInstanceName, null, null,
variables, transientVariables, null, null, null, null, null, false);
variables, transientVariables, null, null, null, null, null, null, null, false);
}

public ProcessInstance createProcessInstance(ProcessDefinition processDefinition, String businessKey, String businessStatus, String processInstanceName,
String overrideDefinitionTenantId, String predefinedProcessInstanceId, Map<String, Object> variables, Map<String, Object> transientVariables,
String callbackId, String callbackType, String referenceId, String referenceType, String stageInstanceId, boolean startProcessInstance) {
String callbackId, String callbackType, String referenceId, String referenceType, String stageInstanceId, Map<String, Set<String>> userIdentityLinks,
Map<String, Set<String>> groupIdentityLinks, boolean startProcessInstance) {

CommandContext commandContext = Context.getCommandContext();
if (Flowable5Util.isFlowable5ProcessDefinition(processDefinition, commandContext)) {
Expand Down Expand Up @@ -109,7 +111,7 @@ public ProcessInstance createProcessInstance(ProcessDefinition processDefinition
return createAndStartProcessInstanceWithInitialFlowElement(processDefinition, businessKey, businessStatus, processInstanceName,
overrideDefinitionTenantId,
predefinedProcessInstanceId, initialFlowElement, process, variables, transientVariables,
callbackId, callbackType, referenceId, referenceType, stageInstanceId, startProcessInstance);
callbackId, callbackType, referenceId, referenceType, stageInstanceId, userIdentityLinks, groupIdentityLinks, startProcessInstance);
}

public ProcessInstance createAndStartProcessInstanceByMessage(ProcessDefinition processDefinition, String messageName, String businessKey,
Expand Down Expand Up @@ -153,7 +155,7 @@ public ProcessInstance createAndStartProcessInstanceByMessage(ProcessDefinition
}

return createAndStartProcessInstanceWithInitialFlowElement(processDefinition, businessKey, businessStatus, null, null, null, initialFlowElement,
process, variables, transientVariables, callbackId, callbackType, referenceId, referenceType, null, true);
process, variables, transientVariables, callbackId, callbackType, referenceId, referenceType, null, null, null, true);
}

public ProcessInstance createAndStartProcessInstanceWithInitialFlowElement(ProcessDefinition processDefinition,
Expand All @@ -162,7 +164,7 @@ public ProcessInstance createAndStartProcessInstanceWithInitialFlowElement(Proce
Map<String, Object> transientVariables, boolean startProcessInstance) {

return createAndStartProcessInstanceWithInitialFlowElement(processDefinition, businessKey, businessStatus, processInstanceName, null, null,
initialFlowElement, process, variables, transientVariables, null, null, null, null, null, startProcessInstance);
initialFlowElement, process, variables, transientVariables, null, null, null, null, null, null, null, startProcessInstance);
}

public ProcessInstance createAndStartProcessInstanceWithInitialFlowElement(ProcessDefinition processDefinition,
Expand All @@ -171,7 +173,8 @@ public ProcessInstance createAndStartProcessInstanceWithInitialFlowElement(Proce
FlowElement initialFlowElement, Process process,
Map<String, Object> variables, Map<String, Object> transientVariables,
String callbackId, String callbackType, String referenceId, String referenceType,
String stageInstanceId, boolean startProcessInstance) {
String stageInstanceId, Map<String, Set<String>> userIdentityLinks,
Map<String, Set<String>> groupIdentityLinks, boolean startProcessInstance) {

CommandContext commandContext = Context.getCommandContext();

Expand All @@ -191,7 +194,8 @@ public ProcessInstance createAndStartProcessInstanceWithInitialFlowElement(Proce
StartProcessInstanceBeforeContext startInstanceBeforeContext = new StartProcessInstanceBeforeContext(businessKey, businessStatus, processInstanceName,
callbackId, callbackType, referenceId, referenceType,
variables, transientVariables, tenantId, initiatorVariableName, initialFlowElement.getId(),
initialFlowElement, process, processDefinition, overrideDefinitionTenantId, predefinedProcessInstanceId);
initialFlowElement, process, processDefinition, overrideDefinitionTenantId, predefinedProcessInstanceId,
userIdentityLinks, groupIdentityLinks);

ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
if (processEngineConfiguration.getStartProcessInstanceInterceptor() != null) {
Expand Down Expand Up @@ -240,7 +244,35 @@ public ProcessInstance createAndStartProcessInstanceWithInitialFlowElement(Proce
processInstance.setTransientVariable(varName, startInstanceBeforeContext.getTransientVariables().get(varName));
}
}


if (startInstanceBeforeContext.getUserIdentityLinks() != null) {
for (Map.Entry<String, Set<String>> entry : startInstanceBeforeContext.getUserIdentityLinks().entrySet()) {
String identityLinkType = entry.getKey();
Set<String> users = entry.getValue();
if (identityLinkType != null && users != null) {
for (String user : users) {
if (user != null) {
IdentityLinkUtil.createProcessInstanceIdentityLink(processInstance, user, null, identityLinkType);
}
}
}
}
}

if (startInstanceBeforeContext.getGroupIdentityLinks() != null) {
for (Map.Entry<String, Set<String>> entry : startInstanceBeforeContext.getGroupIdentityLinks().entrySet()) {
String identityLinkType = entry.getKey();
Set<String> groups = entry.getValue();
if (identityLinkType != null && groups != null) {
for (String group : groups) {
if (group != null) {
IdentityLinkUtil.createProcessInstanceIdentityLink(processInstance, null, group, identityLinkType);
}
}
}
}
}

// Fire events
if (eventDispatcherEnabled) {
eventDispatcher.dispatchEvent(FlowableEventBuilder.createEntityWithVariablesEvent(FlowableEngineEventType.ENTITY_INITIALIZED,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
package org.flowable.engine.interceptor;

import java.util.Map;
import java.util.Set;

import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.Process;
Expand All @@ -28,7 +29,9 @@ public class StartProcessInstanceBeforeContext extends AbstractStartProcessInsta
protected String initiatorVariableName;
protected String overrideDefinitionTenantId;
protected String predefinedProcessInstanceId;

protected Map<String, Set<String>> userIdentityLinks;
protected Map<String, Set<String>> groupIdentityLinks;

public StartProcessInstanceBeforeContext() {

}
Expand All @@ -37,7 +40,8 @@ public StartProcessInstanceBeforeContext(String businessKey, String businessStat
String callbackId, String callbackType, String referenceId, String referenceType,
Map<String, Object> variables, Map<String, Object> transientVariables, String tenantId,
String initiatorVariableName, String initialActivityId, FlowElement initialFlowElement, Process process,
ProcessDefinition processDefinition, String overrideDefinitionTenantId, String predefinedProcessInstanceId) {
ProcessDefinition processDefinition, String overrideDefinitionTenantId, String predefinedProcessInstanceId,
Map<String, Set<String>> userIdentityLinks, Map<String, Set<String>> groupIdentityLinks) {

super(businessKey, businessStatus, processInstanceName, variables, transientVariables, initialActivityId, initialFlowElement, process,
processDefinition);
Expand All @@ -50,6 +54,8 @@ public StartProcessInstanceBeforeContext(String businessKey, String businessStat
this.initiatorVariableName = initiatorVariableName;
this.overrideDefinitionTenantId = overrideDefinitionTenantId;
this.predefinedProcessInstanceId = predefinedProcessInstanceId;
this.userIdentityLinks = userIdentityLinks;
this.groupIdentityLinks = groupIdentityLinks;
}

public String getCallbackId() {
Expand Down Expand Up @@ -115,4 +121,20 @@ public String getPredefinedProcessInstanceId() {
public void setPredefinedProcessInstanceId(String predefinedProcessInstanceId) {
this.predefinedProcessInstanceId = predefinedProcessInstanceId;
}

public Map<String, Set<String>> getUserIdentityLinks() {
return userIdentityLinks;
}

public void setUserIdentityLinks(Map<String, Set<String>> userIdentityLinks) {
this.userIdentityLinks = userIdentityLinks;
}

public Map<String, Set<String>> getGroupIdentityLinks() {
return groupIdentityLinks;
}

public void setGroupIdentityLinks(Map<String, Set<String>> groupIdentityLinks) {
this.groupIdentityLinks = groupIdentityLinks;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@
package org.flowable.engine.runtime;

import java.util.Map;
import java.util.Set;

import org.flowable.common.engine.api.FlowableIllegalArgumentException;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.form.api.FormInfo;
import org.flowable.identitylink.api.IdentityLinkType;

/**
* Helper for starting new ProcessInstance.
Expand Down Expand Up @@ -160,6 +162,32 @@ public interface ProcessInstanceBuilder {
*/
ProcessInstanceBuilder fallbackToDefaultTenant();

/**
* Adds user identity links to the process instance.
*
* @param userIdentityLinks
* for each identity link type (@see {@link IdentityLinkType}), a set of users can be provided; null values will be ignored
*/
ProcessInstanceBuilder userIdentityLinks(Map<String, Set<String>> userIdentityLinks);

/**
* Adds a user identity link to the process instance
*/
ProcessInstanceBuilder userIdentityLink(String identityLinkType, String user);

/**
* Adds group identity links to the process instance.
*
* @param groupIdentityLinks
* for each identity link type (@see {@link IdentityLinkType}), a set of groups can be provided; null values will be ignored
*/
ProcessInstanceBuilder groupIdentityLinks(Map<String, Set<String>> groupIdentityLinks);

/**
* Adds a group identity link to the process instance
*/
ProcessInstanceBuilder groupIdentityLink(String identityLinkType, String group);

/**
* Start the process instance
*
Expand Down
Loading