Skip to content

Commit

Permalink
fix UserTaskIT
Browse files Browse the repository at this point in the history
  • Loading branch information
elguardian committed Sep 2, 2024
1 parent aaa01e4 commit c91d26e
Show file tree
Hide file tree
Showing 19 changed files with 638 additions and 452 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

Expand Down Expand Up @@ -62,15 +61,21 @@ protected SecurityPolicy(IdentityProvider identity) {

@Override
public void enforce(KogitoWorkItem workItem) {
String actualOwner = (String) workItem.getParameter("ActorId");
String actualOwners = (String) workItem.getParameter("ActorId");
String actualRoles = (String) workItem.getParameter("GroupId");
if (actualOwner != null || actualRoles != null) {
List<String> owners = actualOwner != null ? List.of(actualOwner.split(",")) : Collections.emptyList();
List<String> roles = actualRoles != null ? List.of(actualRoles.split(",")) : Collections.emptyList();
String excludedOwner = (String) workItem.getParameter("ExcludedOwnerId");
if (actualOwners != null || actualRoles != null) {
List<String> owners = actualOwners != null ? new ArrayList<>(List.of(actualOwners.split(","))) : new ArrayList<>();
List<String> excluded = excludedOwner != null ? new ArrayList<>(List.of(excludedOwner.split(","))) : new ArrayList<>();
owners.removeAll(excluded);
List<String> roles = actualRoles != null ? List.of(actualRoles.split(",")) : new ArrayList<>();
List<String> userRoles = new ArrayList<>(identity.getRoles());
userRoles.retainAll(roles);
if (!owners.contains(identity.getName()) && userRoles.isEmpty()) {
String actualOwner = workItem.getActualOwner();
if (actualOwner != null && !identity.getName().equals(actualOwner)) {
throw new NotAuthorizedException("this work item " + workItem.getStringId() + " is not allows by this owner" + actualOwner);
} else if (!owners.contains(identity.getName()) && userRoles.isEmpty()) {
throw new NotAuthorizedException("this work item " + workItem.getStringId() + " is not allows by this owner" + actualOwners + " or " + actualRoles);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ public interface KogitoWorkItem extends WorkItem {

String getExternalReferenceId();

String getActualOwner();

String getStringId();

/**
Expand Down Expand Up @@ -87,6 +89,8 @@ public interface KogitoWorkItem extends WorkItem {
*/
KogitoProcessInstance getProcessInstance();

void removeOutput(String name);

void setOutput(String name, Object value);

default void setOutputs(Map<String, Object> outputs) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public interface UserTask {
*
* @return task priority if present
*/
String getTaskPriority();
Integer getTaskPriority();

/**
* Returns reference name of the task
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ public interface UserTaskInstance {

String status();

boolean hasActualOwner();

void setActuaOwner(String string);

String getActualOwner();

UserTaskTransitionToken createTransitionToken(String transitionId, Map<String, Object> data);
Expand All @@ -42,4 +46,5 @@ public interface UserTaskInstance {
void complete();

void abort();

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public class UserTaskModel {

private String taskName;
private String taskDescription;
private String taskPriority;
private Integer taskPriority;
private Set<String> potentialUsers;
private Set<String> potentialGroups;
private Set<String> adminUsers;
Expand Down Expand Up @@ -86,11 +86,11 @@ public void setTaskDescription(String taskDescription) {
*
* @return task priority if present
*/
public String getTaskPriority() {
public Integer getTaskPriority() {
return this.taskPriority;
}

public void setTaskPriority(String taskPriority) {
public void setTaskPriority(Integer taskPriority) {
this.taskPriority = taskPriority;
}

Expand Down Expand Up @@ -142,6 +142,10 @@ public Set<String> getAdminGroups() {
return this.adminGroups;
}

public void setAdminGroups(Set<String> adminGroups) {
this.adminGroups = adminGroups;
}

/**
* Returns excluded users that cannot work on this task
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ public boolean accept(Object payload) {
public DataEvent<?> adapt(Object payload) {
UserTaskStateEvent event = (UserTaskStateEvent) payload;
Map<String, Object> metadata = AdapterHelper.buildUserTaskMetadata(event.getUserTaskInstance());
Integer priority = event.getUserTaskModel().getTaskPriority();
String priorityStr = priority != null ? priority.toString() : null;

UserTaskInstanceStateEventBody.Builder builder = UserTaskInstanceStateEventBody.create()
.eventDate(new Date())
Expand All @@ -50,7 +52,7 @@ public DataEvent<?> adapt(Object payload) {
.userTaskInstanceId(event.getUserTaskInstance().id())
.userTaskName(event.getUserTaskModel().getTaskName())
.userTaskDescription(event.getUserTaskModel().getTaskDescription())
.userTaskPriority(event.getUserTaskModel().getTaskPriority())
.userTaskPriority(priorityStr)
.userTaskReferenceName(event.getUserTask().getReferenceName())
.state(event.getNewStatus())
.actualOwner(event.getUserTaskInstance().getActualOwner())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,20 @@ public Class<HumanTaskNode> generateNodeFor() {
}

private static final Set<String> taskParameters = Set.of(
"NotStartedNotify", "NotCompletedNotify", "NotCompletedReassign", "NotStartedReassign", "Description", "Comment", "ActorId", "GroupId", "Priority", "Skippable", "Content");
"NotStartedNotify",
"NotCompletedNotify",
"NotCompletedReassign",
"NotStartedReassign",
"Description",
"Comment",
"ActorId",
"GroupId",
"Priority",
"Skippable",
"Content",
"ExcludedOwnerId",
"BusinessAdministratorId",
"BusinessAdministratorGroupId");

@Override
protected Node handleNode(final Node node, final Element element, final String uri,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ public void internalTrigger(final KogitoNodeInstance from, String type) {
Map<String, Object> parameters = workItemNode.getWork().getWorkParametersFactory().apply(workItem);
parameters.forEach(workItem::setParameter);
}

processWorkItemHandler(() -> ((InternalKogitoWorkItemManager) InternalProcessRuntime.asKogitoProcessRuntime(getProcessInstance().getKnowledgeRuntime()).getKogitoWorkItemManager())
.internalExecuteWorkItem(workItem));
if (!workItemNode.isWaitForCompletion()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,66 +18,178 @@
*/
package org.kie.kogito.jbpm.usertask.handler;

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

import org.kie.kogito.internal.process.workitem.InvalidTransitionException;
import org.kie.kogito.internal.process.workitem.KogitoWorkItem;
import org.kie.kogito.internal.process.workitem.KogitoWorkItemHandler;
import org.kie.kogito.internal.process.workitem.KogitoWorkItemManager;
import org.kie.kogito.internal.process.workitem.Policy;
import org.kie.kogito.internal.process.workitem.WorkItemLifeCycle;
import org.kie.kogito.internal.process.workitem.WorkItemLifeCyclePhase;
import org.kie.kogito.internal.process.workitem.WorkItemPhaseState;
import org.kie.kogito.internal.process.workitem.WorkItemTerminationType;
import org.kie.kogito.internal.process.workitem.WorkItemTransition;
import org.kie.kogito.process.workitems.InternalKogitoWorkItem;
import org.kie.kogito.process.workitems.impl.DefaultKogitoWorkItemHandler;
import org.kie.kogito.process.workitems.impl.DefaultWorkItemLifeCycle;
import org.kie.kogito.process.workitems.impl.DefaultWorkItemLifeCyclePhase;
import org.kie.kogito.usertask.UserTask;
import org.kie.kogito.usertask.UserTaskInstance;
import org.kie.kogito.usertask.UserTasks;
import org.kie.kogito.usertask.model.UserTaskModel;

import static java.util.Collections.emptyMap;
import static java.util.Optional.ofNullable;

/**
* Default Work Item handler based on the standard life cycle
*/
public class UserTaskKogitoWorkItemHandler extends DefaultKogitoWorkItemHandler {
private static String UT_SEPARATOR = System.getProperty("org.jbpm.ht.user.separator", ",");

public static final WorkItemPhaseState INACTIVE = WorkItemPhaseState.initialized();
public static final WorkItemPhaseState COMPLETED = WorkItemPhaseState.of("Completed", WorkItemTerminationType.COMPLETE);
public static final WorkItemPhaseState ABORTED = WorkItemPhaseState.of("Aborted", WorkItemTerminationType.ABORT);
public static final WorkItemPhaseState ACTIVATED = WorkItemPhaseState.of("Activated");
public static final WorkItemPhaseState RESERVED = WorkItemPhaseState.of("Reserved");

public static final WorkItemLifeCyclePhase TRANSITION_RESERVED_COMPLETE =
new DefaultWorkItemLifeCyclePhase("complete", RESERVED, COMPLETED, UserTaskKogitoWorkItemHandler::userTaskCompleteWorkItemHandler);
public static final WorkItemLifeCyclePhase TRANSITION_RESERVED_ABORT =
new DefaultWorkItemLifeCyclePhase("abort", RESERVED, ABORTED, UserTaskKogitoWorkItemHandler::userTaskAbortWorkItemHandler);
public static final WorkItemLifeCyclePhase TRANSITION_ACTIVATED_CLAIM =
new DefaultWorkItemLifeCyclePhase("claim", ACTIVATED, RESERVED, UserTaskKogitoWorkItemHandler::userTaskClaimWorkItemHandler);
public static final WorkItemLifeCyclePhase TRANSITION_CREATED_ACTIVE =
new DefaultWorkItemLifeCyclePhase("activate", INACTIVE, ACTIVATED, UserTaskKogitoWorkItemHandler::userTaskActivateWorkItemHandler);
public static final WorkItemLifeCyclePhase TRANSITION_RESERVED_RELEASE =
new DefaultWorkItemLifeCyclePhase("release", RESERVED, ACTIVATED, UserTaskKogitoWorkItemHandler::userTaskReleaseWorkItemHandler);

private static final String DESCRIPTION = "Description";
private static final String PRIORITY = "Priority";
private static final String TASK_NAME = "TaskName";
private static final String ACTOR_ID = "ActorId";
private static final String GROUP_ID = "GroupId";
private static final String BUSINESSADMINISTRATOR_ID = "BusinessAdministratorId";
private static final String BUSINESSADMINISTRATOR_GROUP_ID = "BusinessAdministratorGroupId";
private static final String EXCLUDED_OWNER_ID = "ExcludedOwnerId";

@Override
public String getName() {
return "Human Task";
}

@Override
public Optional<WorkItemTransition> activateWorkItemHandler(KogitoWorkItemManager manager, KogitoWorkItemHandler handler, KogitoWorkItem workItem, WorkItemTransition transition) {
public WorkItemLifeCycle initialize() {
return new DefaultWorkItemLifeCycle(TRANSITION_CREATED_ACTIVE, TRANSITION_ACTIVATED_CLAIM, TRANSITION_RESERVED_RELEASE, TRANSITION_RESERVED_ABORT,
TRANSITION_RESERVED_COMPLETE);
}

@Override
public WorkItemTransition startingTransition(Map<String, Object> data, Policy... policies) {
return workItemLifeCycle.newTransition(TRANSITION_CREATED_ACTIVE.id(), null, data, policies);
}

@Override
public WorkItemTransition abortTransition(String phaseStatus, Policy... policies) {
return workItemLifeCycle.newTransition(TRANSITION_RESERVED_ABORT.id(), phaseStatus, emptyMap(), policies);
}

@Override
public WorkItemTransition completeTransition(String phaseStatus, Map<String, Object> data, Policy... policies) {
return workItemLifeCycle.newTransition(TRANSITION_RESERVED_COMPLETE.id(), phaseStatus, data, policies);
}

static public Optional<WorkItemTransition> userTaskActivateWorkItemHandler(KogitoWorkItemManager manager, KogitoWorkItemHandler handler, KogitoWorkItem workItem, WorkItemTransition transition) {
UserTasks userTasks = handler.getApplication().get(UserTasks.class);

Object priority = workItem.getParameter(PRIORITY);
Integer priorityInteger = null;
if (priority instanceof String priorityString) {
priorityInteger = Integer.parseInt((String) priorityString);
} else {
priority = (Integer) priority;
}

UserTask userTask = userTasks.userTaskById((String) workItem.getParameter("id"));
UserTaskModel model = userTask.createModel();
model.setTaskName((String) workItem.getParameter(TASK_NAME));
model.setTaskDescription((String) workItem.getParameter(DESCRIPTION));
model.setTaskPriority((String) workItem.getParameter(PRIORITY));
model.setTaskPriority(priorityInteger);
model.setExternalReferenceId(workItem.getStringId());
UserTaskInstance instance = userTask.createInstance(model);

ofNullable(workItem.getParameters().get(ACTOR_ID)).map(String.class::cast).map(UserTaskKogitoWorkItemHandler::toSet).ifPresent(model::setPotentialUsers);
ofNullable(workItem.getParameters().get(GROUP_ID)).map(String.class::cast).map(UserTaskKogitoWorkItemHandler::toSet).ifPresent(model::setPotentialGroups);
ofNullable(workItem.getParameters().get(BUSINESSADMINISTRATOR_ID)).map(String.class::cast).map(UserTaskKogitoWorkItemHandler::toSet).ifPresent(model::setAdminUsers);
ofNullable(workItem.getParameters().get(BUSINESSADMINISTRATOR_GROUP_ID)).map(String.class::cast).map(UserTaskKogitoWorkItemHandler::toSet).ifPresent(model::setAdminGroups);
ofNullable(workItem.getParameters().get(EXCLUDED_OWNER_ID)).map(String.class::cast).map(UserTaskKogitoWorkItemHandler::toSet).ifPresent(model::setExcludedUsers);

UserTaskInstance instance = userTask.createInstance(model);
if (workItem instanceof InternalKogitoWorkItem ikw) {
ikw.setExternalReferenceId(instance.id());
ikw.setActualOwner(instance.getActualOwner());
}

if (instance.getActualOwner() == null) {
return Optional.empty();
} else {
return Optional.of(handler.newTransition(TRANSITION_ACTIVATED_CLAIM.id(), workItem.getPhaseStatus(), emptyMap()));
}
}

static protected Set<String> toSet(String value) {
if (value == null) {
return null;
}
return Set.of(value.split(UT_SEPARATOR));
}

static public Optional<WorkItemTransition> userTaskClaimWorkItemHandler(KogitoWorkItemManager manager, KogitoWorkItemHandler handler, KogitoWorkItem workItem, WorkItemTransition transition) {
workItem.removeOutput("ACTUAL_OWNER");

UserTasks userTasks = handler.getApplication().get(UserTasks.class);
UserTask userTask = userTasks.userTaskById((String) workItem.getParameter("id"));
userTask.instances().findById(workItem.getExternalReferenceId()).ifPresent(ut -> {
Map<String, Object> data = transition.data();
if (!data.containsKey("ACTUAL_OWNER")) {
throw new InvalidTransitionException("transition claim does not contain ACTUAL_OWNER");
}
ut.setActuaOwner((String) data.get("ACTUAL_OWNER"));
if (workItem instanceof InternalKogitoWorkItem ikw) {
ikw.setActualOwner(ut.getActualOwner());
}
});
return Optional.empty();
}

static public Optional<WorkItemTransition> userTaskReleaseWorkItemHandler(KogitoWorkItemManager manager, KogitoWorkItemHandler handler, KogitoWorkItem workItem, WorkItemTransition transition) {
UserTasks userTasks = handler.getApplication().get(UserTasks.class);
UserTask userTask = userTasks.userTaskById((String) workItem.getParameter("id"));
userTask.instances().findById(workItem.getExternalReferenceId()).ifPresent(ut -> {
ut.setActuaOwner(null);
});

return Optional.empty();
}

@Override
public Optional<WorkItemTransition> completeWorkItemHandler(KogitoWorkItemManager manager, KogitoWorkItemHandler handler, KogitoWorkItem workItem, WorkItemTransition transition) {
static public Optional<WorkItemTransition> userTaskCompleteWorkItemHandler(KogitoWorkItemManager manager, KogitoWorkItemHandler handler, KogitoWorkItem workItem, WorkItemTransition transition) {
UserTasks userTasks = handler.getApplication().get(UserTasks.class);
UserTask userTask = userTasks.userTaskById((String) workItem.getParameter("id"));
userTask.instances().findById(workItem.getExternalReferenceId()).ifPresent(UserTaskInstance::complete);
userTask.instances().findById(workItem.getExternalReferenceId()).ifPresent(ut -> {
ut.complete();
});

return Optional.empty();
}

@Override
public Optional<WorkItemTransition> abortWorkItemHandler(KogitoWorkItemManager manager, KogitoWorkItemHandler handler, KogitoWorkItem workItem, WorkItemTransition transition) {
static public Optional<WorkItemTransition> userTaskAbortWorkItemHandler(KogitoWorkItemManager manager, KogitoWorkItemHandler handler, KogitoWorkItem workItem, WorkItemTransition transition) {
UserTasks userTasks = handler.getApplication().get(UserTasks.class);
UserTask userTask = userTasks.userTaskById((String) workItem.getParameter("id"));
userTask.instances().findById(workItem.getExternalReferenceId()).ifPresent(UserTaskInstance::abort);
userTask.instances().findById(workItem.getExternalReferenceId()).ifPresent(ut -> {
ut.abort();
});
return Optional.empty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public class DefaultUserTask implements UserTask {
private String taskName;
private String taskDescription;
private String referenceName;
private String taskPriority;
private Integer taskPriority;
private Boolean skippable;
private Set<String> potentialUsers;
private Set<String> potentialGroups;
Expand Down Expand Up @@ -129,11 +129,11 @@ public void setTaskDescription(String taskDescription) {
}

@Override
public String getTaskPriority() {
public Integer getTaskPriority() {
return this.taskPriority;
}

public void setTaskPriority(String taskPriority) {
public void setTaskPriority(Integer taskPriority) {
this.taskPriority = taskPriority;
}

Expand Down
Loading

0 comments on commit c91d26e

Please sign in to comment.