diff --git a/api/kogito-api/src/main/java/org/kie/kogito/internal/usertask/event/KogitoUserTaskEventSupport.java b/api/kogito-api/src/main/java/org/kie/kogito/internal/usertask/event/KogitoUserTaskEventSupport.java index 4adc903a217..5ec63f27040 100644 --- a/api/kogito-api/src/main/java/org/kie/kogito/internal/usertask/event/KogitoUserTaskEventSupport.java +++ b/api/kogito-api/src/main/java/org/kie/kogito/internal/usertask/event/KogitoUserTaskEventSupport.java @@ -23,6 +23,7 @@ import org.kie.kogito.usertask.UserTaskEventListener; import org.kie.kogito.usertask.UserTaskInstance; +import org.kie.kogito.usertask.lifecycle.UserTaskState; import org.kie.kogito.usertask.model.Attachment; import org.kie.kogito.usertask.model.Comment; @@ -37,7 +38,7 @@ enum AssignmentType { void fireOneUserTaskStateChange( UserTaskInstance instance, - String oldPhaseStatus, String newPhaseStatus); + UserTaskState oldPhaseStatus, UserTaskState newPhaseStatus); void fireOnUserTaskNotStartedDeadline( UserTaskInstance instance, diff --git a/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTask.java b/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTask.java index 8910256e68d..11d762195a5 100644 --- a/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTask.java +++ b/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTask.java @@ -109,4 +109,6 @@ public interface UserTask { Collection> getNotCompletedReassigments(); + UserTaskAssignmentStrategy getAssignmentStrategy(); + } diff --git a/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTaskAssignmentStrategy.java b/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTaskAssignmentStrategy.java new file mode 100644 index 00000000000..985a5d25572 --- /dev/null +++ b/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTaskAssignmentStrategy.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.kie.kogito.usertask; + +import java.util.Optional; + +import org.kie.kogito.auth.IdentityProvider; + +public interface UserTaskAssignmentStrategy { + + static final String DEFAULT_NAME = "default"; + + default String getName() { + return getClass().getName(); + } + + Optional computeAssigment(UserTaskInstance userTaskInstance, IdentityProvider identityProvider); + +} diff --git a/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTaskAssignmentStrategyConfig.java b/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTaskAssignmentStrategyConfig.java new file mode 100644 index 00000000000..b9bc180a529 --- /dev/null +++ b/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTaskAssignmentStrategyConfig.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.kie.kogito.usertask; + +import java.util.List; + +public interface UserTaskAssignmentStrategyConfig { + + List userTaskAssignmentStrategies(); + + UserTaskAssignmentStrategy forName(String name); + + default UserTaskAssignmentStrategy defaultUserTaskAssignmentStrategy() { + return forName(UserTaskAssignmentStrategy.DEFAULT_NAME); + } +} diff --git a/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTaskConfig.java b/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTaskConfig.java index ee031f84188..2bebb31ef62 100644 --- a/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTaskConfig.java +++ b/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTaskConfig.java @@ -28,6 +28,8 @@ public interface UserTaskConfig extends KogitoConfig { UserTaskEventListenerConfig userTaskEventListeners(); + UserTaskAssignmentStrategyConfig userTaskAssignmentStrategies(); + UserTaskLifeCycle userTaskLifeCycle(); UnitOfWorkManager unitOfWorkManager(); @@ -36,4 +38,6 @@ public interface UserTaskConfig extends KogitoConfig { IdentityProvider identityProvider(); + UserTaskInstances userTaskInstances(); + } diff --git a/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTaskInstance.java b/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTaskInstance.java index 9e73f146240..4a0023a0c41 100644 --- a/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTaskInstance.java +++ b/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTaskInstance.java @@ -22,8 +22,8 @@ import java.util.Map; import java.util.Set; +import org.kie.kogito.auth.IdentityProvider; import org.kie.kogito.usertask.lifecycle.UserTaskState; -import org.kie.kogito.usertask.lifecycle.UserTaskTransitionToken; import org.kie.kogito.usertask.model.Attachment; import org.kie.kogito.usertask.model.Comment; @@ -35,19 +35,15 @@ public interface UserTaskInstance { UserTaskState getStatus(); + String getUserTaskId(); + boolean hasActualOwner(); - void setActuaOwner(String string); + void setActuaOwner(String actualOwner); String getActualOwner(); - UserTaskTransitionToken createTransitionToken(String transitionId, Map data); - - void transition(UserTaskTransitionToken token); - - void complete(); - - void abort(); + void transition(String transitionId, Map data, IdentityProvider identityProvider); String getExternalReferenceId(); @@ -59,6 +55,14 @@ public interface UserTaskInstance { Map getMetadata(); + Map getOutputs(); + + Map getInputs(); + + void setInput(String key, Object value); + + void setOutput(String key, Object value); + /** * Returns potential users that can work on this task * @@ -94,23 +98,24 @@ public interface UserTaskInstance { */ Set getExcludedUsers(); - void addAttachment(Attachment attachment); + Attachment findAttachmentById(String attachmentId); - void updateAttachment(Attachment newAttachment); + Attachment addAttachment(Attachment attachment); - void removeAttachment(Attachment oldAttachment); + Attachment updateAttachment(Attachment newAttachment); - void addComment(Comment comment); + Attachment removeAttachment(Attachment oldAttachment); - void updateComment(Comment newComment); + Collection getAttachments(); - void removeComment(Comment comment); + Comment findCommentById(String commentId); - Collection getComments(); + Comment addComment(Comment comment); - Collection getAttachments(); + Comment updateComment(Comment newComment); - Attachment findAttachmentById(String attachmentId); + Comment removeComment(Comment comment); + + Collection getComments(); - Comment findCommentById(String commentId); } diff --git a/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTaskInstances.java b/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTaskInstances.java index 57e956bd87f..7f294b15fed 100644 --- a/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTaskInstances.java +++ b/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTaskInstances.java @@ -18,11 +18,20 @@ */ package org.kie.kogito.usertask; +import java.util.List; import java.util.Optional; import java.util.function.Function; +import org.kie.kogito.auth.IdentityProvider; + public interface UserTaskInstances { + void setReconnectUserTaskInstance(Function reconnectUserTaskInstance); + + void setDisconnectUserTaskInstance(Function disconnectUserTaskInstance); + + List findByIdentity(IdentityProvider identityProvider); + Optional findById(String userTaskInstanceId); boolean exists(String userTaskInstanceId); @@ -33,8 +42,4 @@ public interface UserTaskInstances { UserTaskInstance remove(String userTaskInstanceId); - void setReconnectUserTaskInstance(Function reconnectUserTaskInstance); - - void setDisconnectUserTaskInstance(Function disconnectUserTaskInstance); - } diff --git a/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTaskService.java b/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTaskService.java new file mode 100644 index 00000000000..8db68e19537 --- /dev/null +++ b/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTaskService.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.kie.kogito.usertask; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.kie.kogito.auth.IdentityProvider; +import org.kie.kogito.usertask.model.Attachment; +import org.kie.kogito.usertask.model.Comment; +import org.kie.kogito.usertask.view.UserTaskTransitionView; +import org.kie.kogito.usertask.view.UserTaskView; + +public interface UserTaskService { + + List list(IdentityProvider identity); + + Optional transition(String taskId, String transitionId, Map data, IdentityProvider identity); + + List allowedTransitions(String taskId, IdentityProvider identity); + + Optional setOutputs(String taskId, Map data, IdentityProvider identity); + + Optional setInputs(String taskId, Map data, IdentityProvider identity); + + List getComments(String taskId, IdentityProvider identity); + + Optional getComment(String taskId, String commentId, IdentityProvider identity); + + Optional addComment(String taskId, Comment comment, IdentityProvider identity); + + Optional updateComment(String taskId, Comment comment, IdentityProvider identity); + + Optional removeComment(String taskId, String commentId, IdentityProvider identity); + + List getAttachments(String taskId, IdentityProvider identity); + + Optional getAttachment(String taskId, String commentId, IdentityProvider identity); + + Optional addAttachment(String taskId, Attachment comment, IdentityProvider identity); + + Optional updateAttachment(String taskId, Attachment comment, IdentityProvider identity); + + Optional removeAttachment(String taskId, String commentId, IdentityProvider identity); +} diff --git a/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTasks.java b/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTasks.java index 3d31047cad2..0253644c0f2 100644 --- a/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTasks.java +++ b/api/kogito-api/src/main/java/org/kie/kogito/usertask/UserTasks.java @@ -24,6 +24,8 @@ public interface UserTasks extends KogitoEngine { + UserTaskInstances instances(); + UserTask userTaskById(String userTaskId); Collection userTaskIds(); diff --git a/api/kogito-api/src/main/java/org/kie/kogito/usertask/events/UserTaskStateEvent.java b/api/kogito-api/src/main/java/org/kie/kogito/usertask/events/UserTaskStateEvent.java index 196b76aafbb..e5471cc96f3 100644 --- a/api/kogito-api/src/main/java/org/kie/kogito/usertask/events/UserTaskStateEvent.java +++ b/api/kogito-api/src/main/java/org/kie/kogito/usertask/events/UserTaskStateEvent.java @@ -19,10 +19,12 @@ package org.kie.kogito.usertask.events; +import org.kie.kogito.usertask.lifecycle.UserTaskState; + public interface UserTaskStateEvent extends UserTaskEvent { - String getNewStatus(); + UserTaskState getNewStatus(); - String getOldStatus(); + UserTaskState getOldStatus(); } diff --git a/api/kogito-api/src/main/java/org/kie/kogito/usertask/lifecycle/UserTaskLifeCycle.java b/api/kogito-api/src/main/java/org/kie/kogito/usertask/lifecycle/UserTaskLifeCycle.java index eb8290884a9..09170f4d1cc 100644 --- a/api/kogito-api/src/main/java/org/kie/kogito/usertask/lifecycle/UserTaskLifeCycle.java +++ b/api/kogito-api/src/main/java/org/kie/kogito/usertask/lifecycle/UserTaskLifeCycle.java @@ -19,14 +19,16 @@ package org.kie.kogito.usertask.lifecycle; +import java.util.List; import java.util.Map; import java.util.Optional; +import org.kie.kogito.auth.IdentityProvider; import org.kie.kogito.usertask.UserTaskInstance; public interface UserTaskLifeCycle { - Optional transition(UserTaskInstance userTaskInstance, UserTaskTransitionToken transition); + Optional transition(UserTaskInstance userTaskInstance, UserTaskTransitionToken transition, IdentityProvider identity); UserTaskTransitionToken newTransitionToken(String transitionId, UserTaskInstance userTaskInstance, Map data); @@ -34,4 +36,6 @@ public interface UserTaskLifeCycle { UserTaskTransitionToken newAbortTransitionToken(UserTaskInstance userTaskInstance, Map emptyMap); + List allowedTransitions(UserTaskInstance ut); + } diff --git a/api/kogito-api/src/main/java/org/kie/kogito/usertask/lifecycle/UserTaskState.java b/api/kogito-api/src/main/java/org/kie/kogito/usertask/lifecycle/UserTaskState.java index 71ba89b94d0..63a3242ede4 100644 --- a/api/kogito-api/src/main/java/org/kie/kogito/usertask/lifecycle/UserTaskState.java +++ b/api/kogito-api/src/main/java/org/kie/kogito/usertask/lifecycle/UserTaskState.java @@ -19,7 +19,6 @@ package org.kie.kogito.usertask.lifecycle; import java.util.Objects; -import java.util.Optional; public class UserTaskState { @@ -69,8 +68,8 @@ public String getName() { return name; } - public Optional isTerminate() { - return Optional.ofNullable(terminate); + public boolean isTerminate() { + return terminate != null; } @Override @@ -91,7 +90,7 @@ public boolean equals(Object obj) { } public static UserTaskState initalized() { - return of(null); + return of("Created"); } @Override diff --git a/api/kogito-api/src/main/java/org/kie/kogito/usertask/lifecycle/UserTaskTransitionException.java b/api/kogito-api/src/main/java/org/kie/kogito/usertask/lifecycle/UserTaskTransitionException.java new file mode 100644 index 00000000000..3e98d19bdc4 --- /dev/null +++ b/api/kogito-api/src/main/java/org/kie/kogito/usertask/lifecycle/UserTaskTransitionException.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.kie.kogito.usertask.lifecycle; + +public class UserTaskTransitionException extends RuntimeException { + + private static final long serialVersionUID = -9127576077607542859L; + + public UserTaskTransitionException(String msg) { + super(msg); + } + +} diff --git a/api/kogito-api/src/main/java/org/kie/kogito/usertask/lifecycle/UserTaskTransitionExecutor.java b/api/kogito-api/src/main/java/org/kie/kogito/usertask/lifecycle/UserTaskTransitionExecutor.java index 6124901d28f..79eb36a5a0f 100644 --- a/api/kogito-api/src/main/java/org/kie/kogito/usertask/lifecycle/UserTaskTransitionExecutor.java +++ b/api/kogito-api/src/main/java/org/kie/kogito/usertask/lifecycle/UserTaskTransitionExecutor.java @@ -20,10 +20,11 @@ import java.util.Optional; +import org.kie.kogito.auth.IdentityProvider; import org.kie.kogito.usertask.UserTaskInstance; public interface UserTaskTransitionExecutor { - Optional execute(UserTaskInstance transition, UserTaskTransitionToken token); + Optional execute(UserTaskInstance transition, UserTaskTransitionToken token, IdentityProvider identity); } \ No newline at end of file diff --git a/api/kogito-api/src/main/java/org/kie/kogito/usertask/lifecycle/UserTaskTransitionToken.java b/api/kogito-api/src/main/java/org/kie/kogito/usertask/lifecycle/UserTaskTransitionToken.java index db1dd9e8d72..8277f4d6d56 100644 --- a/api/kogito-api/src/main/java/org/kie/kogito/usertask/lifecycle/UserTaskTransitionToken.java +++ b/api/kogito-api/src/main/java/org/kie/kogito/usertask/lifecycle/UserTaskTransitionToken.java @@ -22,7 +22,11 @@ public interface UserTaskTransitionToken { - UserTaskTransition transition(); + String transitionId(); + + UserTaskState source(); + + UserTaskState target(); Map data(); } diff --git a/api/kogito-api/src/main/java/org/kie/kogito/usertask/view/UserTaskTransitionView.java b/api/kogito-api/src/main/java/org/kie/kogito/usertask/view/UserTaskTransitionView.java new file mode 100644 index 00000000000..3f22f091e7c --- /dev/null +++ b/api/kogito-api/src/main/java/org/kie/kogito/usertask/view/UserTaskTransitionView.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.kie.kogito.usertask.view; + +import org.kie.kogito.usertask.lifecycle.UserTaskState; + +public class UserTaskTransitionView { + + private String transitionId; + private UserTaskState source; + private UserTaskState target; + + public String getTransitionId() { + return transitionId; + } + + public void setTransitionId(String transitionId) { + this.transitionId = transitionId; + } + + public UserTaskState getSource() { + return source; + } + + public void setSource(UserTaskState source) { + this.source = source; + } + + public UserTaskState getTarget() { + return target; + } + + public void setTarget(UserTaskState target) { + this.target = target; + } + +} diff --git a/api/kogito-api/src/main/java/org/kie/kogito/usertask/view/UserTaskView.java b/api/kogito-api/src/main/java/org/kie/kogito/usertask/view/UserTaskView.java new file mode 100644 index 00000000000..becac940dd6 --- /dev/null +++ b/api/kogito-api/src/main/java/org/kie/kogito/usertask/view/UserTaskView.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.kie.kogito.usertask.view; + +import java.util.Map; +import java.util.Set; + +import org.kie.kogito.usertask.lifecycle.UserTaskState; + +public class UserTaskView { + + private String id; + + private String userTaskId; + + private UserTaskState status; + + private String taskName; + private String taskDescription; + private Integer taskPriority; + private Set potentialUsers; + private Set potentialGroups; + private Set adminUsers; + private Set adminGroups; + private Set excludedUsers; + private String externalReferenceId; + private String actualOwner; + + private Map inputs; + private Map outputs; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getUserTaskId() { + return userTaskId; + } + + public void setUserTaskId(String userTaskId) { + this.userTaskId = userTaskId; + } + + public UserTaskState getStatus() { + return status; + } + + public void setStatus(UserTaskState status) { + this.status = status; + } + + public String getTaskName() { + return taskName; + } + + public void setTaskName(String taskName) { + this.taskName = taskName; + } + + public String getTaskDescription() { + return taskDescription; + } + + public void setTaskDescription(String taskDescription) { + this.taskDescription = taskDescription; + } + + public Integer getTaskPriority() { + return taskPriority; + } + + public void setTaskPriority(Integer taskPriority) { + this.taskPriority = taskPriority; + } + + public Set getPotentialUsers() { + return potentialUsers; + } + + public void setPotentialUsers(Set potentialUsers) { + this.potentialUsers = potentialUsers; + } + + public Set getPotentialGroups() { + return potentialGroups; + } + + public void setPotentialGroups(Set potentialGroups) { + this.potentialGroups = potentialGroups; + } + + public Set getAdminUsers() { + return adminUsers; + } + + public void setAdminUsers(Set adminUsers) { + this.adminUsers = adminUsers; + } + + public Set getAdminGroups() { + return adminGroups; + } + + public void setAdminGroups(Set adminGroups) { + this.adminGroups = adminGroups; + } + + public Set getExcludedUsers() { + return excludedUsers; + } + + public void setExcludedUsers(Set excludedUsers) { + this.excludedUsers = excludedUsers; + } + + public String getExternalReferenceId() { + return externalReferenceId; + } + + public void setExternalReferenceId(String externalReferenceId) { + this.externalReferenceId = externalReferenceId; + } + + public String getActualOwner() { + return actualOwner; + } + + public void setActualOwner(String actualOwner) { + this.actualOwner = actualOwner; + } + + public Map getInputs() { + return inputs; + } + + public void setInputs(Map inputs) { + this.inputs = inputs; + } + + public Map getOutputs() { + return outputs; + } + + public void setOutputs(Map outputs) { + this.outputs = outputs; + } + +} diff --git a/api/kogito-events-core/src/main/java/org/kie/kogito/event/impl/DefaultInstanceEventBatch.java b/api/kogito-events-core/src/main/java/org/kie/kogito/event/impl/DefaultInstanceEventBatch.java index c4f6cd76b2f..df94b1183ae 100644 --- a/api/kogito-events-core/src/main/java/org/kie/kogito/event/impl/DefaultInstanceEventBatch.java +++ b/api/kogito-events-core/src/main/java/org/kie/kogito/event/impl/DefaultInstanceEventBatch.java @@ -68,7 +68,7 @@ public int compare(DataEvent event1, DataEvent event2) { @Override public void append(Object event) { - LOG.info("event generated {}", event); + LOG.trace("event generated {}", event); this.dataEventAdapters.stream().filter(a -> a.accept(event)).map(a -> a.adapt(event)).forEach(this.processedEvents::add); } diff --git a/api/kogito-events-core/src/main/java/org/kie/kogito/event/impl/adapter/UserTaskStateEventDataEventAdapter.java b/api/kogito-events-core/src/main/java/org/kie/kogito/event/impl/adapter/UserTaskStateEventDataEventAdapter.java index fe577ac0c29..af70b37ae35 100644 --- a/api/kogito-events-core/src/main/java/org/kie/kogito/event/impl/adapter/UserTaskStateEventDataEventAdapter.java +++ b/api/kogito-events-core/src/main/java/org/kie/kogito/event/impl/adapter/UserTaskStateEventDataEventAdapter.java @@ -54,9 +54,9 @@ public DataEvent adapt(Object payload) { .userTaskDescription(event.getUserTaskInstance().getTaskDescription()) .userTaskPriority(priorityStr) .userTaskReferenceName(event.getUserTask().getReferenceName()) - .state(event.getNewStatus()) + .state(event.getNewStatus().getName()) .actualOwner(event.getUserTaskInstance().getActualOwner()) - .eventType(isTransition(event) ? event.getNewStatus() : "Modify") + .eventType(isTransition(event) ? event.getNewStatus().getName() : "Modify") .processInstanceId((String) event.getUserTaskInstance().getMetadata().get("ProcessInstanceId")); UserTaskInstanceStateEventBody body = builder.build(); diff --git a/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/ProcessToExecModelGenerator.java b/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/ProcessToExecModelGenerator.java index 253d24a7f0a..06f6186061b 100644 --- a/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/ProcessToExecModelGenerator.java +++ b/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/ProcessToExecModelGenerator.java @@ -29,7 +29,7 @@ import org.jbpm.process.core.context.variable.Variable; import org.jbpm.process.core.context.variable.VariableScope; import org.jbpm.workflow.core.impl.WorkflowProcessImpl; -import org.jbpm.workflow.core.node.HumanTaskNode; +import org.jbpm.workflow.core.node.WorkItemNode; import org.kie.api.definition.process.Node; import org.kie.api.definition.process.WorkflowProcess; import org.kie.kogito.ProcessInput; @@ -243,23 +243,22 @@ public static String extractModelClassName(String processId) { return sanitizeClassName(extractProcessId(processId) + MODEL_CLASS_SUFFIX); } - public List generateUserTaskModel(WorkflowProcess process) { + public List generateWorkItemModel(WorkflowProcess process) { String packageName = process.getPackageName(); - List userTaskModels = new ArrayList<>(); + List userTaskModels = new ArrayList<>(); VariableScope variableScope = (VariableScope) ((org.jbpm.process.core.Process) process).getDefaultContext( VariableScope.VARIABLE_SCOPE); for (Node node : ((WorkflowProcessImpl) process).getNodesRecursively()) { - if (node instanceof HumanTaskNode) { - HumanTaskNode humanTaskNode = (HumanTaskNode) node; - VariableScope nodeVariableScope = (VariableScope) ((ContextContainer) humanTaskNode + if (node instanceof WorkItemNode workItemNode) { + VariableScope nodeVariableScope = (VariableScope) ((ContextContainer) workItemNode .getParentContainer()).getDefaultContext(VariableScope.VARIABLE_SCOPE); if (nodeVariableScope == null) { nodeVariableScope = variableScope; } - userTaskModels.add(new UserTaskModelMetaData(packageName, variableScope, nodeVariableScope, - humanTaskNode, process.getId())); + userTaskModels.add(new WorkItemModelMetaData(packageName, variableScope, nodeVariableScope, + workItemNode, process.getId())); } } diff --git a/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/UserTaskModelMetaData.java b/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/WorkItemModelMetaData.java similarity index 90% rename from jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/UserTaskModelMetaData.java rename to jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/WorkItemModelMetaData.java index 3d648ad2e2f..5ae74187907 100644 --- a/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/UserTaskModelMetaData.java +++ b/jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/WorkItemModelMetaData.java @@ -30,7 +30,7 @@ import org.jbpm.process.core.datatype.DataType; import org.jbpm.process.core.datatype.DataTypeResolver; import org.jbpm.util.PatternConstants; -import org.jbpm.workflow.core.node.HumanTaskNode; +import org.jbpm.workflow.core.node.WorkItemNode; import org.kie.api.definition.process.WorkflowElementIdentifier; import org.kie.kogito.UserTask; import org.kie.kogito.UserTaskParam; @@ -70,7 +70,7 @@ import static org.jbpm.ruleflow.core.Metadata.CUSTOM_AUTO_START; import static org.kie.kogito.internal.utils.ConversionUtils.sanitizeClassName; -public class UserTaskModelMetaData { +public class WorkItemModelMetaData { private static final String TASK_INTPUT_CLASS_SUFFIX = "TaskInput"; private static final String TASK_OUTTPUT_CLASS_SUFFIX = "TaskOutput"; @@ -86,7 +86,7 @@ public class UserTaskModelMetaData { private final VariableScope processVariableScope; private final VariableScope variableScope; - private final HumanTaskNode humanTaskNode; + private final WorkItemNode workItemNode; private final String processId; private String inputModelClassName; @@ -98,20 +98,20 @@ public class UserTaskModelMetaData { private String taskModelClassName; private String taskModelClassSimpleName; - public UserTaskModelMetaData(String packageName, VariableScope processVariableScope, VariableScope variableScope, HumanTaskNode humanTaskNode, String processId) { + public WorkItemModelMetaData(String packageName, VariableScope processVariableScope, VariableScope variableScope, WorkItemNode workItemNode, String processId) { this.packageName = packageName; this.processVariableScope = processVariableScope; this.variableScope = variableScope; - this.humanTaskNode = humanTaskNode; + this.workItemNode = workItemNode; this.processId = processId; - this.inputModelClassSimpleName = sanitizeClassName(ProcessToExecModelGenerator.extractProcessId(processId) + "_" + humanTaskNode.getId().toExternalFormat() + "_" + TASK_INTPUT_CLASS_SUFFIX); + this.inputModelClassSimpleName = sanitizeClassName(ProcessToExecModelGenerator.extractProcessId(processId) + "_" + workItemNode.getId().toExternalFormat() + "_" + TASK_INTPUT_CLASS_SUFFIX); this.inputModelClassName = packageName + '.' + inputModelClassSimpleName; - this.outputModelClassSimpleName = sanitizeClassName(ProcessToExecModelGenerator.extractProcessId(processId) + "_" + humanTaskNode.getId().toExternalFormat() + "_" + TASK_OUTTPUT_CLASS_SUFFIX); + this.outputModelClassSimpleName = sanitizeClassName(ProcessToExecModelGenerator.extractProcessId(processId) + "_" + workItemNode.getId().toExternalFormat() + "_" + TASK_OUTTPUT_CLASS_SUFFIX); this.outputModelClassName = packageName + '.' + outputModelClassSimpleName; - this.taskModelClassSimpleName = sanitizeClassName(ProcessToExecModelGenerator.extractProcessId(processId) + "_" + humanTaskNode.getId().toExternalFormat() + "_" + TASK_MODEL_CLASS_SUFFIX); + this.taskModelClassSimpleName = sanitizeClassName(ProcessToExecModelGenerator.extractProcessId(processId) + "_" + workItemNode.getId().toExternalFormat() + "_" + TASK_MODEL_CLASS_SUFFIX); this.taskModelClassName = packageName + '.' + taskModelClassSimpleName; } @@ -144,21 +144,21 @@ public String getTaskModelClassName() { } public String getName() { - return (String) humanTaskNode.getWork().getParameters().getOrDefault(TASK_NAME, humanTaskNode.getName()); + return (String) workItemNode.getWork().getParameters().getOrDefault(TASK_NAME, workItemNode.getName()); } public String getNodeName() { - return humanTaskNode.getName(); + return workItemNode.getName(); } public WorkflowElementIdentifier getId() { - return humanTaskNode.getId(); + return workItemNode.getId(); } private void addUserTaskAnnotation(ClassOrInterfaceDeclaration modelClass) { - String taskName = (String) humanTaskNode.getWork().getParameter(TASK_NAME); + String taskName = (String) workItemNode.getWork().getParameter(TASK_NAME); if (taskName == null) - taskName = humanTaskNode.getName(); + taskName = workItemNode.getName(); modelClass.addAndGetAnnotation(UserTask.class).addPair("taskName", new StringLiteralExpr(taskName)).addPair("processName", new StringLiteralExpr(processId)); } @@ -190,8 +190,8 @@ private CompilationUnit compilationUnitInput() { NameExpr item = new NameExpr("item"); // map is task input -> context variable / process variable - Map inputTypes = humanTaskNode.getIoSpecification().getInputTypes(); - for (Entry entry : humanTaskNode.getIoSpecification().getInputMapping().entrySet()) { + Map inputTypes = workItemNode.getIoSpecification().getInputTypes(); + for (Entry entry : workItemNode.getIoSpecification().getInputMapping().entrySet()) { if (INTERNAL_FIELDS.contains(entry.getKey())) { continue; } @@ -235,7 +235,7 @@ private CompilationUnit compilationUnitInput() { AssignExpr.Operator.ASSIGN)); } - for (Entry entry : humanTaskNode.getWork().getParameters().entrySet()) { + for (Entry entry : workItemNode.getWork().getParameters().entrySet()) { if (entry.getValue() == null || INTERNAL_FIELDS.contains(entry.getKey())) { continue; @@ -275,7 +275,6 @@ private CompilationUnit compilationUnitInput() { return compilationUnit; } - @SuppressWarnings({ "unchecked" }) private CompilationUnit compilationUnitOutput() { CompilationUnit compilationUnit = parse(this.getClass().getResourceAsStream("/class-templates/TaskOutputTemplate.java")); compilationUnit.setPackageDeclaration(packageName); @@ -303,8 +302,8 @@ private CompilationUnit compilationUnitOutput() { HashMap.class.getSimpleName() + "<>"), NodeList.nodeList()), AssignExpr.Operator.ASSIGN)); // map is task output -> context variable / process variable - Map outputTypes = humanTaskNode.getIoSpecification().getOutputTypes(); - for (Entry entry : humanTaskNode.getIoSpecification().getOutputMappingBySources().entrySet()) { + Map outputTypes = workItemNode.getIoSpecification().getOutputTypes(); + for (Entry entry : workItemNode.getIoSpecification().getOutputMappingBySources().entrySet()) { if (entry.getValue() == null || INTERNAL_FIELDS.contains(entry.getKey())) { continue; } @@ -382,7 +381,7 @@ private CompilationUnit compilationUnitModel() { } private void addComment(CompilationUnit unit, String prefix) { - unit.addOrphanComment(new LineComment(prefix + " for user task '" + humanTaskNode.getName() + "' in process '" + processId + "'")); + unit.addOrphanComment(new LineComment(prefix + " for user task '" + workItemNode.getName() + "' in process '" + processId + "'")); } private void templateReplacement(NameExpr name) { @@ -407,13 +406,13 @@ public String templateReplacement(String template) { } public boolean isAdHoc() { - return !Boolean.parseBoolean((String) humanTaskNode.getMetaData(CUSTOM_AUTO_START)) - && (humanTaskNode.getIncomingConnections() == null || humanTaskNode.getIncomingConnections().isEmpty()); + return !Boolean.parseBoolean((String) workItemNode.getMetaData(CUSTOM_AUTO_START)) + && (workItemNode.getIncomingConnections() == null || workItemNode.getIncomingConnections().isEmpty()); } public SwitchEntry getModelSwitchEntry() { SwitchEntry entry = new SwitchEntry(); - entry.setLabels(NodeList.nodeList(new StringLiteralExpr(humanTaskNode.getId().toExternalFormat()))); + entry.setLabels(NodeList.nodeList(new StringLiteralExpr(workItemNode.getId().toExternalFormat()))); entry.addStatement(new ReturnStmt(new MethodCallExpr(new NameExpr( taskModelClassSimpleName), new SimpleName("from")).addArgument(WORK_ITEM))); return entry; diff --git a/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/LightWorkItemManager.java b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/LightWorkItemManager.java index 16a1519fe48..2be53fd155a 100755 --- a/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/LightWorkItemManager.java +++ b/jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/LightWorkItemManager.java @@ -137,11 +137,11 @@ public void retryWorkItem(String workItemId, Map params) { @Override public void completeWorkItem(String workItemId, Map data, Policy... policies) { InternalKogitoWorkItem workItem = getWorkItem(workItemId); + workItem.setState(KogitoWorkItem.COMPLETED); KogitoWorkItemHandler handler = getWorkItemHandler(workItem); transitionWorkItem(workItem, handler.completeTransition(workItem.getPhaseStatus(), data, policies), false); // process instance may have finished already KogitoProcessInstance processInstance = processInstanceManager.getProcessInstance(workItem.getProcessInstanceStringId()); - workItem.setState(KogitoWorkItem.COMPLETED); processInstance.signalEvent("workItemCompleted", workItem); } @@ -149,11 +149,11 @@ public void completeWorkItem(String workItemId, Map data, Policy @Override public void abortWorkItem(String workItemId, Policy... policies) { InternalKogitoWorkItem workItem = getWorkItem(workItemId); + workItem.setState(KogitoWorkItem.ABORTED); KogitoWorkItemHandler handler = getWorkItemHandler(workItemId); transitionWorkItem(workItem, handler.abortTransition(workItem.getPhaseStatus(), policies), false); // process instance may have finished already KogitoProcessInstance processInstance = processInstanceManager.getProcessInstance(workItem.getProcessInstanceStringId()); - workItem.setState(KogitoWorkItem.ABORTED); processInstance.signalEvent("workItemAborted", workItem); } diff --git a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/jbpm/usertask/handler/UserTaskKogitoWorkItemHandler.java b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/jbpm/usertask/handler/UserTaskKogitoWorkItemHandler.java index 4fb06024d3e..e3289e91e8e 100644 --- a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/jbpm/usertask/handler/UserTaskKogitoWorkItemHandler.java +++ b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/jbpm/usertask/handler/UserTaskKogitoWorkItemHandler.java @@ -18,28 +18,21 @@ */ package org.kie.kogito.jbpm.usertask.handler; -import java.util.Map; import java.util.Optional; import java.util.Set; -import org.kie.kogito.auth.SecurityPolicy; -import org.kie.kogito.internal.process.workitem.InvalidTransitionException; +import org.kie.kogito.Application; +import org.kie.kogito.auth.IdentityProviders; 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.UserTasks; import org.kie.kogito.usertask.impl.DefaultUserTaskInstance; +import org.kie.kogito.usertask.impl.lifecycle.DefaultUserTaskLifeCycle; import static java.util.Collections.emptyMap; import static java.util.Optional.ofNullable; @@ -51,29 +44,6 @@ 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_ACTIVATED_COMPLETE = - new DefaultWorkItemLifeCyclePhase("complete", ACTIVATED, COMPLETED, UserTaskKogitoWorkItemHandler::userTaskCompleteFromActiveWorkItemHandler); - public static final WorkItemLifeCyclePhase TRANSITION_RESERVED_ABORT = - new DefaultWorkItemLifeCyclePhase("abort", RESERVED, ABORTED, UserTaskKogitoWorkItemHandler::userTaskAbortWorkItemHandler); - public static final WorkItemLifeCyclePhase TRANSITION_ACTIVATED_ABORT = - new DefaultWorkItemLifeCyclePhase("abort", ACTIVATED, 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); - public static final WorkItemLifeCyclePhase TRANSITION_ACTIVATED_COMPLETED = - new DefaultWorkItemLifeCyclePhase("skip", ACTIVATED, COMPLETED, UserTaskKogitoWorkItemHandler::userTaskCompleteWorkItemHandler); - private static final String DESCRIPTION = "Description"; private static final String PRIORITY = "Priority"; private static final String TASK_NAME = "TaskName"; @@ -83,40 +53,16 @@ public class UserTaskKogitoWorkItemHandler extends DefaultKogitoWorkItemHandler 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 WorkItemLifeCycle initialize() { - return new DefaultWorkItemLifeCycle( - TRANSITION_CREATED_ACTIVE, - TRANSITION_ACTIVATED_CLAIM, - TRANSITION_ACTIVATED_ABORT, - TRANSITION_ACTIVATED_COMPLETE, - TRANSITION_RESERVED_RELEASE, - TRANSITION_RESERVED_ABORT, - TRANSITION_RESERVED_COMPLETE, - TRANSITION_ACTIVATED_COMPLETED); + public UserTaskKogitoWorkItemHandler() { + super(); } - @Override - public WorkItemTransition startingTransition(Map data, Policy... policies) { - return workItemLifeCycle.newTransition("activate", null, data, policies); - } - - @Override - public WorkItemTransition abortTransition(String phaseStatus, Policy... policies) { - return workItemLifeCycle.newTransition("abort", phaseStatus, emptyMap(), policies); - } - - @Override - public WorkItemTransition completeTransition(String phaseStatus, Map data, Policy... policies) { - return workItemLifeCycle.newTransition("complete", phaseStatus, data, policies); + public UserTaskKogitoWorkItemHandler(Application application) { + super(); + this.setApplication(application); } - static public Optional userTaskActivateWorkItemHandler(KogitoWorkItemManager manager, KogitoWorkItemHandler handler, KogitoWorkItem workItem, WorkItemTransition transition) { + public Optional activateWorkItemHandler(KogitoWorkItemManager manager, KogitoWorkItemHandler handler, KogitoWorkItem workItem, WorkItemTransition transition) { UserTasks userTasks = handler.getApplication().get(UserTasks.class); Object priority = workItem.getParameter(PRIORITY); @@ -130,11 +76,11 @@ static public Optional userTaskActivateWorkItemHandler(Kogit UserTask userTask = userTasks.userTaskById((String) workItem.getParameter(KogitoWorkItem.PARAMETER_UNIQUE_TASK_ID)); DefaultUserTaskInstance instance = (DefaultUserTaskInstance) userTask.createInstance(); - instance.setId(workItem.getStringId()); instance.setTaskName((String) workItem.getParameter(TASK_NAME)); instance.setTaskDescription((String) workItem.getParameter(DESCRIPTION)); instance.setTaskPriority(priorityInteger); instance.setExternalReferenceId(workItem.getStringId()); + instance.setMetadata("ProcessId", workItem.getProcessInstance().getProcessId()); instance.setMetadata("ProcessType", workItem.getProcessInstance().getProcess().getType()); instance.setMetadata("ProcessVersion", workItem.getProcessInstance().getProcessVersion()); @@ -144,114 +90,54 @@ static public Optional userTaskActivateWorkItemHandler(Kogit instance.setMetadata("RootProcessInstanceId", workItem.getProcessInstance().getRootProcessInstanceId()); instance.setMetadata("ParentProcessInstanceId", workItem.getProcessInstance().getParentProcessInstanceId()); - ofNullable(workItem.getParameters().get(ACTOR_ID)).map(String.class::cast).map(UserTaskKogitoWorkItemHandler::toSet).ifPresent(instance::setPotentialUsers); - ofNullable(workItem.getParameters().get(GROUP_ID)).map(String.class::cast).map(UserTaskKogitoWorkItemHandler::toSet).ifPresent(instance::setPotentialGroups); - ofNullable(workItem.getParameters().get(BUSINESSADMINISTRATOR_ID)).map(String.class::cast).map(UserTaskKogitoWorkItemHandler::toSet).ifPresent(instance::setAdminUsers); - ofNullable(workItem.getParameters().get(BUSINESSADMINISTRATOR_GROUP_ID)).map(String.class::cast).map(UserTaskKogitoWorkItemHandler::toSet).ifPresent(instance::setAdminGroups); - ofNullable(workItem.getParameters().get(EXCLUDED_OWNER_ID)).map(String.class::cast).map(UserTaskKogitoWorkItemHandler::toSet).ifPresent(instance::setExcludedUsers); - - instance.assign(); - instance.transition(instance.createTransitionToken("activate", emptyMap())); - - if (workItem instanceof InternalKogitoWorkItem ikw) { - ikw.setExternalReferenceId(instance.getId()); - ikw.setActualOwner(instance.getActualOwner()); - } userTask.instances().create(instance); - return Optional.empty(); - } + instance.fireInitialStateChange(); + workItem.getParameters().forEach(instance::setInput); - static protected Set toSet(String value) { - if (value == null) { - return null; - } - return Set.of(value.split(UT_SEPARATOR)); - } + ofNullable(workItem.getParameters().get(ACTOR_ID)).map(String.class::cast).map(this::toSet).ifPresent(instance::setPotentialUsers); + ofNullable(workItem.getParameters().get(GROUP_ID)).map(String.class::cast).map(this::toSet).ifPresent(instance::setPotentialGroups); + ofNullable(workItem.getParameters().get(BUSINESSADMINISTRATOR_ID)).map(String.class::cast).map(this::toSet).ifPresent(instance::setAdminUsers); + ofNullable(workItem.getParameters().get(BUSINESSADMINISTRATOR_GROUP_ID)).map(String.class::cast).map(this::toSet).ifPresent(instance::setAdminGroups); + ofNullable(workItem.getParameters().get(EXCLUDED_OWNER_ID)).map(String.class::cast).map(this::toSet).ifPresent(instance::setExcludedUsers); - static public Optional userTaskClaimWorkItemHandler(KogitoWorkItemManager manager, KogitoWorkItemHandler handler, KogitoWorkItem workItem, WorkItemTransition transition) { - workItem.removeOutput("ACTUAL_OWNER"); + instance.transition(DefaultUserTaskLifeCycle.ACTIVATE, emptyMap(), IdentityProviders.of(DefaultUserTaskLifeCycle.WORKFLOW_ENGINE_USER)); - UserTasks userTasks = handler.getApplication().get(UserTasks.class); - UserTask userTask = userTasks.userTaskById((String) workItem.getParameter(KogitoWorkItem.PARAMETER_UNIQUE_TASK_ID)); - userTask.instances().findById(workItem.getExternalReferenceId()).ifPresent(ut -> { - Map data = transition.data(); - if (workItem instanceof InternalKogitoWorkItem ikw) { - getUserFromTransition(transition).ifPresent(ikw::setActualOwner); - if (data.containsKey("ACTUAL_OWNER") && ikw.getActualOwner() == null) { - ut.setActuaOwner((String) data.get("ACTUAL_OWNER")); - } - if (ikw.getActualOwner() == null) { - throw new InvalidTransitionException("transition claim does not contain user id"); - } - } - ut.setActuaOwner(workItem.getActualOwner()); - ut.transition(ut.createTransitionToken("claim", emptyMap())); - userTask.instances().update(ut); - }); - return Optional.empty(); - } - - static public Optional userTaskReleaseWorkItemHandler(KogitoWorkItemManager manager, KogitoWorkItemHandler handler, KogitoWorkItem workItem, WorkItemTransition transition) { if (workItem instanceof InternalKogitoWorkItem ikw) { - ikw.setActualOwner(null); + ikw.setExternalReferenceId(instance.getId()); } - UserTasks userTasks = handler.getApplication().get(UserTasks.class); - UserTask userTask = userTasks.userTaskById((String) workItem.getParameter(KogitoWorkItem.PARAMETER_UNIQUE_TASK_ID)); - userTask.instances().findById(workItem.getExternalReferenceId()).ifPresent(ut -> { - ut.setActuaOwner(null); - ut.transition(ut.createTransitionToken("release", emptyMap())); - userTask.instances().update(ut); - }); return Optional.empty(); } - static public Optional userTaskCompleteWorkItemHandler(KogitoWorkItemManager manager, KogitoWorkItemHandler handler, KogitoWorkItem workItem, WorkItemTransition transition) { + @Override + public Optional completeWorkItemHandler(KogitoWorkItemManager manager, KogitoWorkItemHandler handler, KogitoWorkItem workItem, WorkItemTransition transition) { UserTasks userTasks = handler.getApplication().get(UserTasks.class); UserTask userTask = userTasks.userTaskById((String) workItem.getParameter(KogitoWorkItem.PARAMETER_UNIQUE_TASK_ID)); userTask.instances().findById(workItem.getExternalReferenceId()).ifPresent(ut -> { - ut.transition(ut.createTransitionToken("complete", emptyMap())); - userTask.instances().update(ut); + ut.transition(DefaultUserTaskLifeCycle.SKIP, emptyMap(), IdentityProviders.of(DefaultUserTaskLifeCycle.WORKFLOW_ENGINE_USER)); }); - if (workItem instanceof InternalKogitoWorkItem ikw && ikw.getActualOwner() == null) { - getUserFromTransition(transition).ifPresent(user -> { - ikw.setActualOwner(user); - }); - } return Optional.empty(); } - static public Optional userTaskCompleteFromActiveWorkItemHandler(KogitoWorkItemManager manager, KogitoWorkItemHandler handler, KogitoWorkItem workItem, - WorkItemTransition transition) { + @Override + public Optional abortWorkItemHandler(KogitoWorkItemManager manager, KogitoWorkItemHandler handler, KogitoWorkItem workItem, WorkItemTransition transition) { UserTasks userTasks = handler.getApplication().get(UserTasks.class); UserTask userTask = userTasks.userTaskById((String) workItem.getParameter(KogitoWorkItem.PARAMETER_UNIQUE_TASK_ID)); userTask.instances().findById(workItem.getExternalReferenceId()).ifPresent(ut -> { - ut.transition(ut.createTransitionToken("complete", emptyMap())); - userTask.instances().update(ut); + ut.transition(DefaultUserTaskLifeCycle.FAIL, emptyMap(), IdentityProviders.of(DefaultUserTaskLifeCycle.WORKFLOW_ENGINE_USER)); }); - if (workItem instanceof InternalKogitoWorkItem ikw) { - getUserFromTransition(transition).ifPresent(user -> { - ikw.setActualOwner(user); - }); - } return Optional.empty(); } - static public Optional userTaskAbortWorkItemHandler(KogitoWorkItemManager manager, KogitoWorkItemHandler handler, KogitoWorkItem workItem, WorkItemTransition transition) { - UserTasks userTasks = handler.getApplication().get(UserTasks.class); - UserTask userTask = userTasks.userTaskById((String) workItem.getParameter(KogitoWorkItem.PARAMETER_UNIQUE_TASK_ID)); - userTask.instances().findById(workItem.getExternalReferenceId()).ifPresent(ut -> { - ut.transition(ut.createTransitionToken("skip", emptyMap())); - userTask.instances().update(ut); - }); - return Optional.empty(); + protected Set toSet(String value) { + if (value == null) { + return null; + } + return Set.of(value.split(UT_SEPARATOR)); } - private static Optional getUserFromTransition(WorkItemTransition transition) { - Optional securityPolicy = transition.policies().stream().filter(SecurityPolicy.class::isInstance).map(SecurityPolicy.class::cast).findAny(); - if (securityPolicy.isPresent()) { - return Optional.ofNullable(securityPolicy.get().getUser()); - } - return Optional.empty(); + @Override + public String getName() { + return "Human Task"; } } diff --git a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/jbpm/usertask/handler/UserTaskKogitoWorkItemHandlerProcessListener.java b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/jbpm/usertask/handler/UserTaskKogitoWorkItemHandlerProcessListener.java new file mode 100644 index 00000000000..5c878bf91fc --- /dev/null +++ b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/jbpm/usertask/handler/UserTaskKogitoWorkItemHandlerProcessListener.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.kie.kogito.jbpm.usertask.handler; + +import java.util.List; + +import org.kie.kogito.Model; +import org.kie.kogito.process.ProcessInstance; +import org.kie.kogito.process.Processes; +import org.kie.kogito.process.WorkItem; +import org.kie.kogito.usertask.UserTaskEventListener; +import org.kie.kogito.usertask.events.UserTaskStateEvent; +import org.kie.kogito.usertask.lifecycle.UserTaskState; + +public class UserTaskKogitoWorkItemHandlerProcessListener implements UserTaskEventListener { + + private Processes processes; + + public UserTaskKogitoWorkItemHandlerProcessListener(Processes processes) { + this.processes = processes; + } + + @Override + public void onUserTaskState(UserTaskStateEvent event) { + UserTaskState userTaskState = event.getNewStatus(); + + if (!userTaskState.isTerminate()) { + return; + } + + String processId = (String) event.getUserTaskInstance().getMetadata().get("ProcessId"); + String processInstanceId = (String) event.getUserTaskInstance().getMetadata().get("ProcessInstanceId"); + ProcessInstance processInstance = processes.processById(processId).instances().findById(processInstanceId).orElse(null); + WorkItem workItem = processInstance.workItem(event.getUserTaskInstance().getExternalReferenceId()); + // we check first that the work item is not fished to convey the signal + List terminationPhases = List.of(UserTaskKogitoWorkItemHandler.completed.getName(), UserTaskKogitoWorkItemHandler.aborted.getName()); + if (!terminationPhases.contains(workItem.getPhaseStatus())) { + processInstance.completeWorkItem(event.getUserTaskInstance().getExternalReferenceId(), event.getUserTaskInstance().getOutputs()); + } + } +} diff --git a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/BasicUserTaskAssignmentStrategy.java b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/BasicUserTaskAssignmentStrategy.java new file mode 100644 index 00000000000..076a62c313e --- /dev/null +++ b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/BasicUserTaskAssignmentStrategy.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.kie.kogito.usertask.impl; + +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; + +import org.kie.kogito.auth.IdentityProvider; +import org.kie.kogito.usertask.UserTaskAssignmentStrategy; +import org.kie.kogito.usertask.UserTaskInstance; + +public class BasicUserTaskAssignmentStrategy implements UserTaskAssignmentStrategy { + + @Override + public String getName() { + return DEFAULT_NAME; + } + + @Override + public Optional computeAssigment(UserTaskInstance userTaskInstance, IdentityProvider identityProvider) { + Set users = new HashSet<>(userTaskInstance.getPotentialUsers()); + users.removeAll(userTaskInstance.getExcludedUsers()); + if (users.size() == 1) { + return Optional.of(users.iterator().next()); + } + return Optional.empty(); + } + +} diff --git a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTask.java b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTask.java index 92e2fa9c745..e994c3c53c6 100644 --- a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTask.java +++ b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTask.java @@ -24,8 +24,8 @@ import java.util.Set; import org.kie.kogito.Application; -import org.kie.kogito.uow.events.UnitOfWorkUserTaskEventListener; import org.kie.kogito.usertask.UserTask; +import org.kie.kogito.usertask.UserTaskAssignmentStrategy; import org.kie.kogito.usertask.UserTaskConfig; import org.kie.kogito.usertask.UserTaskInstance; import org.kie.kogito.usertask.UserTaskInstances; @@ -65,7 +65,7 @@ public DefaultUserTask(String id, String name) { } public DefaultUserTask(Application application, String id, String name) { - this(application, id, name, new InMemoryUserTaskInstances()); + this(application, id, name, application.config().get(UserTaskConfig.class).userTaskInstances()); } public DefaultUserTask(Application application, String id, String name, UserTaskInstances userTaskInstances) { @@ -73,8 +73,6 @@ public DefaultUserTask(Application application, String id, String name, UserTask this.id = id; this.name = name; this.userTaskInstances = userTaskInstances; - this.userTaskInstances.setReconnectUserTaskInstance(this::connect); - this.userTaskInstances.setDisconnectUserTaskInstance(this::disconnect); this.skippable = Boolean.FALSE; this.potentialUsers = new HashSet<>(); this.potentialGroups = new HashSet<>(); @@ -95,6 +93,7 @@ public void setApplication(Application application) { @Override public UserTaskInstance createInstance() { DefaultUserTaskInstance instance = new DefaultUserTaskInstance(this); + instance.setUserTaskId(id()); instance.setTaskName(getTaskName()); instance.setTaskDescription(getTaskDescription()); instance.setTaskPriority(getTaskPriority()); @@ -103,30 +102,13 @@ public UserTaskInstance createInstance() { instance.setAdminUsers(getAdminUsers()); instance.setPotentialGroups(getPotentialGroups()); instance.setExcludedUsers(getExcludedUsers()); - connect(instance); return instance; } - private UserTaskInstance disconnect(UserTaskInstance userTaskInstance) { - DefaultUserTaskInstance instance = (DefaultUserTaskInstance) userTaskInstance; - instance.setUserTask(null); - instance.setUserTaskEventSupport(null); - instance.setUserTaskLifeCycle(null); - instance.setInstances(null); - return instance; - } - - public UserTaskInstance connect(UserTaskInstance userTaskInstance) { - DefaultUserTaskInstance instance = (DefaultUserTaskInstance) userTaskInstance; + @Override + public UserTaskAssignmentStrategy getAssignmentStrategy() { UserTaskConfig userTaskConfig = application.config().get(UserTaskConfig.class); - KogitoUserTaskEventSupportImpl impl = new KogitoUserTaskEventSupportImpl(userTaskConfig.identityProvider()); - userTaskConfig.userTaskEventListeners().listeners().forEach(impl::addEventListener); - impl.addEventListener(new UnitOfWorkUserTaskEventListener(application.unitOfWorkManager())); - instance.setUserTask(this); - instance.setUserTaskEventSupport(impl); - instance.setUserTaskLifeCycle(userTaskConfig.userTaskLifeCycle()); - instance.setInstances(userTaskInstances); - return instance; + return userTaskConfig.userTaskAssignmentStrategies().defaultUserTaskAssignmentStrategy(); } @Override diff --git a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTaskAssignmentStrategyConfig.java b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTaskAssignmentStrategyConfig.java new file mode 100644 index 00000000000..27ee3ed89ea --- /dev/null +++ b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTaskAssignmentStrategyConfig.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.kie.kogito.usertask.impl; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.kie.kogito.usertask.UserTaskAssignmentStrategy; +import org.kie.kogito.usertask.UserTaskAssignmentStrategyConfig; + +public class DefaultUserTaskAssignmentStrategyConfig implements UserTaskAssignmentStrategyConfig { + + private Map userTaskAssignmentStrategies; + + public DefaultUserTaskAssignmentStrategyConfig() { + this.userTaskAssignmentStrategies = new HashMap<>(); + BasicUserTaskAssignmentStrategy strategy = new BasicUserTaskAssignmentStrategy(); + this.userTaskAssignmentStrategies.put(strategy.getName(), strategy); + } + + @Override + public List userTaskAssignmentStrategies() { + return new ArrayList<>(userTaskAssignmentStrategies.values()); + } + + @Override + public UserTaskAssignmentStrategy forName(String name) { + return userTaskAssignmentStrategies.get(name); + } + +} diff --git a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTaskConfig.java b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTaskConfig.java index 93d012f0116..7282ce72532 100644 --- a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTaskConfig.java +++ b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTaskConfig.java @@ -27,8 +27,10 @@ import org.kie.kogito.services.uow.CollectingUnitOfWorkFactory; import org.kie.kogito.services.uow.DefaultUnitOfWorkManager; import org.kie.kogito.uow.UnitOfWorkManager; +import org.kie.kogito.usertask.UserTaskAssignmentStrategyConfig; import org.kie.kogito.usertask.UserTaskConfig; import org.kie.kogito.usertask.UserTaskEventListenerConfig; +import org.kie.kogito.usertask.UserTaskInstances; import org.kie.kogito.usertask.impl.lifecycle.DefaultUserTaskLifeCycle; import org.kie.kogito.usertask.lifecycle.UserTaskLifeCycle; @@ -39,13 +41,17 @@ public class DefaultUserTaskConfig implements UserTaskConfig { private JobsService jobService; private IdentityProvider identityProvider; private UserTaskLifeCycle userTaskLifeCycle; + private UserTaskAssignmentStrategyConfig userTaskAssignmentStrategyConfig; + private UserTaskInstances userTaskInstances; public DefaultUserTaskConfig() { this(new DefaultUserTaskEventListenerConfig(), new DefaultUnitOfWorkManager(new CollectingUnitOfWorkFactory()), null, new NoOpIdentityProvider(), - new DefaultUserTaskLifeCycle()); + new DefaultUserTaskLifeCycle(), + new DefaultUserTaskAssignmentStrategyConfig(), + new InMemoryUserTaskInstances()); } public DefaultUserTaskConfig( @@ -53,13 +59,17 @@ public DefaultUserTaskConfig( Iterable unitOfWorkManager, Iterable jobService, Iterable identityProvider, - Iterable userTaskLifeCycle) { + Iterable userTaskLifeCycle, + Iterable userTaskAssignmentStrategyConfig, + Iterable userTaskInstances) { this.userTaskEventListeners = singleton(userTaskEventListenerConfig, DefaultUserTaskEventListenerConfig::new); this.unitOfWorkManager = singleton(unitOfWorkManager, () -> new DefaultUnitOfWorkManager(new CollectingUnitOfWorkFactory())); this.jobService = singleton(jobService, () -> null); this.identityProvider = singleton(identityProvider, NoOpIdentityProvider::new); this.userTaskLifeCycle = singleton(userTaskLifeCycle, DefaultUserTaskLifeCycle::new); + this.userTaskAssignmentStrategyConfig = singleton(userTaskAssignmentStrategyConfig, DefaultUserTaskAssignmentStrategyConfig::new); + this.userTaskInstances = singleton(userTaskInstances, InMemoryUserTaskInstances::new); } @@ -76,12 +86,16 @@ public DefaultUserTaskConfig( UnitOfWorkManager unitOfWorkManager, JobsService jobService, IdentityProvider identityProvider, - UserTaskLifeCycle userTaskLifeCycle) { + UserTaskLifeCycle userTaskLifeCycle, + DefaultUserTaskAssignmentStrategyConfig userTaskAssignmentStrategyConfig, + UserTaskInstances userTaskInstances) { this.userTaskEventListeners = userTaskEventListenerConfig; this.unitOfWorkManager = unitOfWorkManager; this.jobService = jobService; this.identityProvider = identityProvider; this.userTaskLifeCycle = userTaskLifeCycle; + this.userTaskAssignmentStrategyConfig = userTaskAssignmentStrategyConfig; + this.userTaskInstances = userTaskInstances; } @Override @@ -109,4 +123,14 @@ public UserTaskLifeCycle userTaskLifeCycle() { return userTaskLifeCycle; } + @Override + public UserTaskAssignmentStrategyConfig userTaskAssignmentStrategies() { + return userTaskAssignmentStrategyConfig; + } + + @Override + public UserTaskInstances userTaskInstances() { + return userTaskInstances; + } + } diff --git a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTaskInstance.java b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTaskInstance.java index 8adb9f07d02..c0fe460e818 100644 --- a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTaskInstance.java +++ b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTaskInstance.java @@ -19,7 +19,6 @@ package org.kie.kogito.usertask.impl; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -28,6 +27,7 @@ import java.util.Set; import java.util.UUID; +import org.kie.kogito.auth.IdentityProvider; import org.kie.kogito.internal.usertask.event.KogitoUserTaskEventSupport; import org.kie.kogito.internal.usertask.event.KogitoUserTaskEventSupport.AssignmentType; import org.kie.kogito.usertask.UserTask; @@ -35,7 +35,6 @@ import org.kie.kogito.usertask.UserTaskInstances; import org.kie.kogito.usertask.lifecycle.UserTaskLifeCycle; import org.kie.kogito.usertask.lifecycle.UserTaskState; -import org.kie.kogito.usertask.lifecycle.UserTaskTransition; import org.kie.kogito.usertask.lifecycle.UserTaskTransitionToken; import org.kie.kogito.usertask.model.Attachment; import org.kie.kogito.usertask.model.Comment; @@ -46,6 +45,8 @@ public class DefaultUserTaskInstance implements UserTaskInstance { private String id; + private String userTaskId; + private UserTaskState status; private String actualOwner; private String taskName; @@ -75,6 +76,9 @@ public class DefaultUserTaskInstance implements UserTaskInstance { private UserTaskLifeCycle setUserTaskLifeCycle; public DefaultUserTaskInstance() { + this.inputs = new HashMap<>(); + this.outputs = new HashMap<>(); + this.status = UserTaskState.initalized(); this.metadata = new HashMap<>(); this.attachments = new ArrayList<>(); this.comments = new ArrayList<>(); @@ -86,27 +90,10 @@ public DefaultUserTaskInstance() { } public DefaultUserTaskInstance(UserTask userTask) { + this(); this.id = UUID.randomUUID().toString(); this.userTask = userTask; this.instances = userTask.instances(); - this.status = UserTaskState.initalized(); - this.metadata = new HashMap<>(); - this.attachments = new ArrayList<>(); - this.comments = new ArrayList<>(); - this.potentialUsers = new HashSet<>(); - this.potentialGroups = new HashSet<>(); - this.adminUsers = new HashSet<>(); - this.adminGroups = new HashSet<>(); - this.excludedUsers = new HashSet<>(); - } - - public void assign() { - Set potentialUsers = new HashSet<>(this.getPotentialUsers()); - potentialUsers.removeAll(getExcludedUsers()); - - if (potentialUsers.size() == 1) { - this.actualOwner = potentialUsers.iterator().next(); - } } public void setUserTaskEventSupport(KogitoUserTaskEventSupport userTaskEventSupport) { @@ -121,22 +108,17 @@ public void setInstances(UserTaskInstances instances) { this.instances = instances; } - @Override - public void complete() { - UserTaskTransitionToken transition = this.setUserTaskLifeCycle.newCompleteTransitionToken(this, Collections.emptyMap()); - transition(transition); - instances.remove(id); + public void setId(String id) { + this.id = id; } @Override - public void abort() { - UserTaskTransitionToken transition = this.setUserTaskLifeCycle.newAbortTransitionToken(this, Collections.emptyMap()); - transition(transition); - instances.remove(id); + public String getUserTaskId() { + return userTaskId; } - public void setId(String id) { - this.id = id; + public void setUserTaskId(String userTaskId) { + this.userTaskId = userTaskId; } @Override @@ -162,7 +144,7 @@ public boolean hasActualOwner() { public void setActuaOwner(String actualOwner) { this.actualOwner = actualOwner; if (this.userTaskEventSupport != null) { - this.userTaskEventSupport.fireOneUserTaskStateChange(this, this.status.getName(), this.status.getName()); + this.userTaskEventSupport.fireOneUserTaskStateChange(this, this.status, this.status); } } @@ -181,21 +163,23 @@ public void setExternalReferenceId(String externalReferenceId) { } @Override - public UserTaskTransitionToken createTransitionToken(String transitionId, Map data) { - return this.setUserTaskLifeCycle.newTransitionToken(transitionId, this, data); - } - - @Override - public void transition(UserTaskTransitionToken token) { - Optional next = Optional.of(token); + public void transition(String transitionId, Map data, IdentityProvider identity) { + Optional next = Optional.of(this.setUserTaskLifeCycle.newTransitionToken(transitionId, this, data)); while (next.isPresent()) { - UserTaskTransition transition = next.get().transition(); - next = this.setUserTaskLifeCycle.transition(this, token); + UserTaskTransitionToken transition = next.get(); + next = this.setUserTaskLifeCycle.transition(this, transition, identity); this.status = transition.target(); - this.userTaskEventSupport.fireOneUserTaskStateChange(this, transition.source().getName(), transition.target().getName()); - if (this.status.isTerminate().isPresent()) { - this.instances.remove(this.id); - } + this.updatePersistence(); + this.userTaskEventSupport.fireOneUserTaskStateChange(this, transition.source(), transition.target()); + } + + } + + private void updatePersistence() { + if (this.status.isTerminate()) { + this.instances.remove(this.id); + } else { + this.instances.update(this); } } @@ -204,7 +188,7 @@ public UserTask getUserTask() { return userTask; } - public void setUserTask(DefaultUserTask userTask) { + public void setUserTask(UserTask userTask) { this.userTask = userTask; } @@ -213,7 +197,7 @@ public Map getInputs() { } public void setInputs(Map inputs) { - this.inputs = inputs; + inputs.forEach(this::setOutput); } public Map getOutputs() { @@ -221,7 +205,23 @@ public Map getOutputs() { } public void setOutputs(Map outputs) { - this.outputs = outputs; + outputs.forEach(this::setOutput); + } + + @Override + public void setInput(String key, Object newValue) { + Object oldValue = this.inputs.put(key, newValue); + if (this.userTaskEventSupport != null) { + this.userTaskEventSupport.fireOnUserTaskInputVariableChange(this, key, oldValue, newValue); + } + } + + @Override + public void setOutput(String key, Object newValue) { + Object oldValue = this.outputs.put(key, newValue); + if (this.userTaskEventSupport != null) { + this.userTaskEventSupport.fireOnUserTaskOutputVariableChange(this, key, oldValue, newValue); + } } /** @@ -237,7 +237,13 @@ public String getTaskName() { public void setTaskName(String taskName) { this.taskName = taskName; if (this.userTaskEventSupport != null) { - this.userTaskEventSupport.fireOneUserTaskStateChange(this, this.status.getName(), this.status.getName()); + this.userTaskEventSupport.fireOneUserTaskStateChange(this, this.status, this.status); + } + } + + public void fireInitialStateChange() { + if (this.userTaskEventSupport != null) { + this.userTaskEventSupport.fireOneUserTaskStateChange(this, this.status, this.status); } } @@ -254,7 +260,7 @@ public String getTaskDescription() { public void setTaskDescription(String taskDescription) { this.taskDescription = taskDescription; if (this.userTaskEventSupport != null) { - this.userTaskEventSupport.fireOneUserTaskStateChange(this, this.status.getName(), this.status.getName()); + this.userTaskEventSupport.fireOneUserTaskStateChange(this, this.status, this.status); } } @@ -271,7 +277,7 @@ public Integer getTaskPriority() { public void setTaskPriority(Integer taskPriority) { this.taskPriority = taskPriority; if (this.userTaskEventSupport != null) { - this.userTaskEventSupport.fireOneUserTaskStateChange(this, this.status.getName(), this.status.getName()); + this.userTaskEventSupport.fireOneUserTaskStateChange(this, this.status, this.status); } } @@ -370,37 +376,41 @@ public void setExcludedUsers(Set excludedUsers) { * * @return A map which key is the attachment id and value the attachment object */ + public List getAttachments() { return attachments; } @Override - public void addAttachment(Attachment attachment) { + public Attachment addAttachment(Attachment attachment) { this.attachments.add(attachment); if (this.userTaskEventSupport != null) { this.userTaskEventSupport.fireOnUserTaskAttachmentAdded(this, attachment); } + return attachment; } @Override - public void updateAttachment(Attachment newAttachment) { + public Attachment updateAttachment(Attachment newAttachment) { Optional oldAttachment = this.attachments.stream().filter(e -> e.getId().equals(newAttachment.getId())).findFirst(); if (oldAttachment.isEmpty()) { - return; + return null; } this.attachments.remove(oldAttachment.get()); this.attachments.add(newAttachment); if (this.userTaskEventSupport != null) { this.userTaskEventSupport.fireOnUserTaskAttachmentChange(this, oldAttachment.get(), newAttachment); } + return newAttachment; } @Override - public void removeAttachment(Attachment oldAttachment) { + public Attachment removeAttachment(Attachment oldAttachment) { this.attachments.remove(oldAttachment); if (this.userTaskEventSupport != null) { this.userTaskEventSupport.fireOnUserTaskAttachmentDeleted(this, oldAttachment); } + return oldAttachment; } public void setAttachments(List attachments) { @@ -413,37 +423,41 @@ public void setAttachments(List attachments) { * * @return A map which key is the comment id and value the comment object */ + public List getComments() { return comments; } @Override - public void addComment(Comment comment) { + public Comment addComment(Comment comment) { this.comments.add(comment); if (this.userTaskEventSupport != null) { this.userTaskEventSupport.fireOnUserTaskCommentAdded(this, comment); } + return comment; } @Override - public void updateComment(Comment newComment) { + public Comment updateComment(Comment newComment) { Optional oldComment = this.comments.stream().filter(e -> e.getId().equals(newComment.getId())).findFirst(); if (oldComment.isEmpty()) { - return; + return null; } this.comments.remove(oldComment.get()); this.comments.add(newComment); if (this.userTaskEventSupport != null) { this.userTaskEventSupport.fireOnUserTaskCommentChange(this, oldComment.get(), newComment); } + return newComment; } @Override - public void removeComment(Comment comment) { + public Comment removeComment(Comment comment) { this.comments.remove(comment); if (this.userTaskEventSupport != null) { this.userTaskEventSupport.fireOnUserTaskCommentDeleted(this, comment); } + return comment; } public void setComments(List comments) { diff --git a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTasks.java b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTasks.java index a2a664e82d8..1ef710a3cc8 100644 --- a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTasks.java +++ b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/DefaultUserTasks.java @@ -25,13 +25,18 @@ import java.util.Map; import org.kie.kogito.Application; +import org.kie.kogito.uow.events.UnitOfWorkUserTaskEventListener; import org.kie.kogito.usertask.UserTask; +import org.kie.kogito.usertask.UserTaskConfig; +import org.kie.kogito.usertask.UserTaskInstance; +import org.kie.kogito.usertask.UserTaskInstances; import org.kie.kogito.usertask.UserTasks; public class DefaultUserTasks implements UserTasks { private Map userTasks; private Application application; + private UserTaskInstances userTaskInstances; public DefaultUserTasks() { this.userTasks = new HashMap<>(); @@ -49,6 +54,9 @@ public DefaultUserTasks(Application application, Iterable userTasks) { UserTask userTask = userTaskIterator.next(); this.userTasks.put(userTask.id(), userTask); } + userTaskInstances = application.config().get(UserTaskConfig.class).userTaskInstances(); + userTaskInstances.setDisconnectUserTaskInstance(this::disconnect); + userTaskInstances.setReconnectUserTaskInstance(this::connect); } @Override @@ -61,4 +69,30 @@ public Collection userTaskIds() { return userTasks.keySet(); } + @Override + public UserTaskInstances instances() { + return application.config().get(UserTaskConfig.class).userTaskInstances(); + } + + private UserTaskInstance disconnect(UserTaskInstance userTaskInstance) { + DefaultUserTaskInstance instance = (DefaultUserTaskInstance) userTaskInstance; + instance.setUserTask(null); + instance.setUserTaskEventSupport(null); + instance.setUserTaskLifeCycle(null); + instance.setInstances(null); + return instance; + } + + public UserTaskInstance connect(UserTaskInstance userTaskInstance) { + DefaultUserTaskInstance instance = (DefaultUserTaskInstance) userTaskInstance; + UserTaskConfig userTaskConfig = application.config().get(UserTaskConfig.class); + KogitoUserTaskEventSupportImpl impl = new KogitoUserTaskEventSupportImpl(userTaskConfig.identityProvider()); + userTaskConfig.userTaskEventListeners().listeners().forEach(impl::addEventListener); + impl.addEventListener(new UnitOfWorkUserTaskEventListener(application.unitOfWorkManager())); + instance.setUserTask(application.get(UserTasks.class).userTaskById(instance.getUserTaskId())); + instance.setUserTaskEventSupport(impl); + instance.setUserTaskLifeCycle(userTaskConfig.userTaskLifeCycle()); + instance.setInstances(userTaskInstances); + return instance; + } } diff --git a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/InMemoryUserTaskInstances.java b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/InMemoryUserTaskInstances.java index 6704fbd83a8..5c23a308399 100644 --- a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/InMemoryUserTaskInstances.java +++ b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/InMemoryUserTaskInstances.java @@ -18,11 +18,18 @@ */ package org.kie.kogito.usertask.impl; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.function.Function; +import org.kie.kogito.auth.IdentityProvider; import org.kie.kogito.usertask.UserTaskInstance; import org.kie.kogito.usertask.UserTaskInstances; @@ -62,6 +69,56 @@ public Optional findById(String userTaskInstanceId) { } } + @Override + public List findByIdentity(IdentityProvider identity) { + try { + String user = identity.getName(); + Collection roles = identity.getRoles(); + List users = new ArrayList<>(); + for (String id : userTaskInstances.keySet()) { + UserTaskInstance userTaskInstance = mapper.readValue(userTaskInstances.get(id), DefaultUserTaskInstance.class); + if (checkVisibility(userTaskInstance, user, roles)) { + users.add(reconnectUserTaskInstance.apply(userTaskInstance)); + } + } + return users; + } catch (Exception e) { + return Collections.emptyList(); + } + } + + private boolean checkVisibility(UserTaskInstance userTaskInstance, String user, Collection roles) { + Set adminUsers = userTaskInstance.getAdminUsers(); + if (adminUsers.contains(user)) { + return true; + } + + Set userAdminGroups = new HashSet<>(userTaskInstance.getAdminGroups()); + userAdminGroups.retainAll(roles); + if (!userAdminGroups.isEmpty()) { + return true; + } + + if (userTaskInstance.getActualOwner() != null && userTaskInstance.getActualOwner().equals(user)) { + return true; + } + + // there is no user + Set users = new HashSet<>(userTaskInstance.getPotentialUsers()); + users.removeAll(userTaskInstance.getExcludedUsers()); + if (users.contains(user)) { + return true; + } + + Set userPotGroups = new HashSet<>(userTaskInstance.getPotentialGroups()); + userPotGroups.retainAll(roles); + if (!userPotGroups.isEmpty()) { + return true; + } + + return false; + } + @Override public boolean exists(String userTaskInstanceId) { return userTaskInstances.containsKey(userTaskInstanceId); diff --git a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/KogitoUserTaskEventSupportImpl.java b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/KogitoUserTaskEventSupportImpl.java index 1a2c3be5d6e..b337d159d4e 100644 --- a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/KogitoUserTaskEventSupportImpl.java +++ b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/KogitoUserTaskEventSupportImpl.java @@ -37,6 +37,7 @@ import org.kie.kogito.usertask.impl.events.UserTaskDeadlineEventImpl; import org.kie.kogito.usertask.impl.events.UserTaskStateEventImpl; import org.kie.kogito.usertask.impl.events.UserTaskVariableEventImpl; +import org.kie.kogito.usertask.lifecycle.UserTaskState; import org.kie.kogito.usertask.model.Attachment; import org.kie.kogito.usertask.model.Comment; @@ -84,7 +85,7 @@ private void fireUserTaskNotification( @Override public void fireOneUserTaskStateChange( UserTaskInstance userTaskInstance, - String oldStatus, String newStatus) { + UserTaskState oldStatus, UserTaskState newStatus) { UserTaskStateEventImpl event = new UserTaskStateEventImpl(userTaskInstance, oldStatus, newStatus, identityProvider.getName()); event.setOldStatus(oldStatus); event.setNewStatus(newStatus); diff --git a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/UserTaskServiceImpl.java b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/UserTaskServiceImpl.java new file mode 100644 index 00000000000..b7dfbb83fc7 --- /dev/null +++ b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/UserTaskServiceImpl.java @@ -0,0 +1,208 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.kie.kogito.usertask.impl; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.kie.kogito.Application; +import org.kie.kogito.auth.IdentityProvider; +import org.kie.kogito.usertask.UserTaskConfig; +import org.kie.kogito.usertask.UserTaskInstance; +import org.kie.kogito.usertask.UserTaskService; +import org.kie.kogito.usertask.UserTasks; +import org.kie.kogito.usertask.lifecycle.UserTaskLifeCycle; +import org.kie.kogito.usertask.lifecycle.UserTaskTransition; +import org.kie.kogito.usertask.model.Attachment; +import org.kie.kogito.usertask.model.Comment; +import org.kie.kogito.usertask.view.UserTaskTransitionView; +import org.kie.kogito.usertask.view.UserTaskView; + +public class UserTaskServiceImpl implements UserTaskService { + + private Application application; + + public UserTaskServiceImpl(Application application) { + this.application = application; + } + + @Override + public List list(IdentityProvider identity) { + return application.get(UserTasks.class).instances().findByIdentity(identity).stream().map(this::toUserTaskView).toList(); + } + + private UserTaskView toUserTaskView(UserTaskInstance instance) { + UserTaskView view = new UserTaskView(); + return view; + } + + @Override + public Optional transition(String taskId, String transitionId, Map data, IdentityProvider identity) { + Optional userTaskInstance = application.get(UserTasks.class).instances().findById(taskId); + if (userTaskInstance.isEmpty()) { + return Optional.empty(); + } + UserTaskInstance ut = userTaskInstance.get(); + ut.transition(transitionId, data, identity); + return Optional.of(toUserTaskView(ut)); + } + + @Override + public List allowedTransitions(String taskId, IdentityProvider identity) { + Optional userTaskInstance = application.get(UserTasks.class).instances().findById(taskId); + if (userTaskInstance.isEmpty()) { + return Collections.emptyList(); + } + UserTaskInstance ut = userTaskInstance.get(); + UserTaskLifeCycle userTaskLifeCycle = application.config().get(UserTaskConfig.class).userTaskLifeCycle(); + List transitions = userTaskLifeCycle.allowedTransitions(ut); + return toUserTaskTransitionView(transitions); + } + + private List toUserTaskTransitionView(List transitions) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Optional setOutputs(String taskId, Map data, IdentityProvider identity) { + Optional userTaskInstance = application.get(UserTasks.class).instances().findById(taskId); + if (userTaskInstance.isEmpty()) { + return Optional.empty(); + } + UserTaskInstance ut = userTaskInstance.get(); + data.forEach(ut::setOutput); + return Optional.of(toUserTaskView(ut)); + } + + @Override + public Optional setInputs(String taskId, Map data, IdentityProvider identity) { + Optional userTaskInstance = application.get(UserTasks.class).instances().findById(taskId); + if (userTaskInstance.isEmpty()) { + return Optional.empty(); + } + UserTaskInstance ut = userTaskInstance.get(); + data.forEach(ut::setInput); + return Optional.of(toUserTaskView(ut)); + } + + @Override + public List getComments(String taskId, IdentityProvider identity) { + Optional userTaskInstance = application.get(UserTasks.class).instances().findById(taskId); + if (userTaskInstance.isEmpty()) { + return Collections.emptyList(); + } + UserTaskInstance ut = userTaskInstance.get(); + return new ArrayList<>(ut.getComments()); + } + + @Override + public Optional getComment(String taskId, String commentId, IdentityProvider identity) { + Optional userTaskInstance = application.get(UserTasks.class).instances().findById(taskId); + if (userTaskInstance.isEmpty()) { + return Optional.empty(); + } + UserTaskInstance ut = userTaskInstance.get(); + return Optional.ofNullable(ut.findCommentById(commentId)); + } + + @Override + public Optional addComment(String taskId, Comment comment, IdentityProvider identity) { + Optional userTaskInstance = application.get(UserTasks.class).instances().findById(taskId); + if (userTaskInstance.isEmpty()) { + return Optional.empty(); + } + UserTaskInstance ut = userTaskInstance.get(); + return Optional.ofNullable(ut.addComment(comment)); + } + + @Override + public Optional updateComment(String taskId, Comment comment, IdentityProvider identity) { + Optional userTaskInstance = application.get(UserTasks.class).instances().findById(taskId); + if (userTaskInstance.isEmpty()) { + return Optional.empty(); + } + UserTaskInstance ut = userTaskInstance.get(); + return Optional.ofNullable(ut.updateComment(comment)); + } + + @Override + public Optional removeComment(String taskId, String commentId, IdentityProvider identity) { + Optional userTaskInstance = application.get(UserTasks.class).instances().findById(taskId); + if (userTaskInstance.isEmpty()) { + return Optional.empty(); + } + UserTaskInstance ut = userTaskInstance.get(); + return Optional.ofNullable(ut.removeComment(new Comment(commentId, identity.getName()))); + } + + @Override + public List getAttachments(String taskId, IdentityProvider identity) { + Optional userTaskInstance = application.get(UserTasks.class).instances().findById(taskId); + if (userTaskInstance.isEmpty()) { + return Collections.emptyList(); + } + UserTaskInstance ut = userTaskInstance.get(); + return new ArrayList<>(ut.getAttachments()); + } + + @Override + public Optional getAttachment(String taskId, String attachmentId, IdentityProvider identity) { + Optional userTaskInstance = application.get(UserTasks.class).instances().findById(taskId); + if (userTaskInstance.isEmpty()) { + return Optional.empty(); + } + UserTaskInstance ut = userTaskInstance.get(); + return Optional.ofNullable(ut.findAttachmentById(attachmentId)); + } + + @Override + public Optional addAttachment(String taskId, Attachment attachment, IdentityProvider identity) { + Optional userTaskInstance = application.get(UserTasks.class).instances().findById(taskId); + if (userTaskInstance.isEmpty()) { + return Optional.empty(); + } + UserTaskInstance ut = userTaskInstance.get(); + return Optional.ofNullable(ut.addAttachment(attachment)); + } + + @Override + public Optional updateAttachment(String taskId, Attachment attachment, IdentityProvider identity) { + Optional userTaskInstance = application.get(UserTasks.class).instances().findById(taskId); + if (userTaskInstance.isEmpty()) { + return Optional.empty(); + } + UserTaskInstance ut = userTaskInstance.get(); + return Optional.ofNullable(ut.updateAttachment(attachment)); + } + + @Override + public Optional removeAttachment(String taskId, String attachmentId, IdentityProvider identity) { + Optional userTaskInstance = application.get(UserTasks.class).instances().findById(taskId); + if (userTaskInstance.isEmpty()) { + return Optional.empty(); + } + UserTaskInstance ut = userTaskInstance.get(); + return Optional.ofNullable(ut.removeAttachment(new Attachment(taskId, identity.getName()))); + } + +} diff --git a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/events/UserTaskStateEventImpl.java b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/events/UserTaskStateEventImpl.java index 1bc9005e1f3..e7f734ed089 100644 --- a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/events/UserTaskStateEventImpl.java +++ b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/events/UserTaskStateEventImpl.java @@ -20,34 +20,37 @@ import org.kie.kogito.usertask.UserTaskInstance; import org.kie.kogito.usertask.events.UserTaskStateEvent; +import org.kie.kogito.usertask.lifecycle.UserTaskState; public class UserTaskStateEventImpl extends UserTaskEventImpl implements UserTaskStateEvent { private static final long serialVersionUID = 4556236095420836309L; - private String oldStatus; - private String newStatus; + private UserTaskState oldStatus; + private UserTaskState newStatus; - public UserTaskStateEventImpl(UserTaskInstance userTaskInstance, String oldStatus, String newStatus, String user) { + public UserTaskStateEventImpl(UserTaskInstance userTaskInstance, UserTaskState oldStatus, UserTaskState newStatus, String user) { super(userTaskInstance, user); + this.oldStatus = oldStatus; + this.newStatus = newStatus; } - public void setOldStatus(String oldStatus) { + public void setOldStatus(UserTaskState oldStatus) { this.oldStatus = oldStatus; } - public void setNewStatus(String newStatus) { + public void setNewStatus(UserTaskState newStatus) { this.newStatus = newStatus; } @Override - public String getNewStatus() { + public UserTaskState getNewStatus() { return newStatus; } @Override - public String getOldStatus() { + public UserTaskState getOldStatus() { return oldStatus; } diff --git a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/lifecycle/DefaultUserTaskLifeCycle.java b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/lifecycle/DefaultUserTaskLifeCycle.java index da681ed8f4d..05f7af3520e 100644 --- a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/lifecycle/DefaultUserTaskLifeCycle.java +++ b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/lifecycle/DefaultUserTaskLifeCycle.java @@ -18,34 +18,52 @@ */ package org.kie.kogito.usertask.impl.lifecycle; +import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; +import org.kie.kogito.auth.IdentityProvider; +import org.kie.kogito.internal.process.workitem.NotAuthorizedException; +import org.kie.kogito.usertask.UserTaskAssignmentStrategy; import org.kie.kogito.usertask.UserTaskInstance; +import org.kie.kogito.usertask.impl.DefaultUserTaskInstance; import org.kie.kogito.usertask.lifecycle.UserTaskLifeCycle; import org.kie.kogito.usertask.lifecycle.UserTaskState; import org.kie.kogito.usertask.lifecycle.UserTaskState.TerminationType; import org.kie.kogito.usertask.lifecycle.UserTaskTransition; +import org.kie.kogito.usertask.lifecycle.UserTaskTransitionException; import org.kie.kogito.usertask.lifecycle.UserTaskTransitionToken; public class DefaultUserTaskLifeCycle implements UserTaskLifeCycle { + public static final String WORKFLOW_ENGINE_USER = "WORKFLOW_ENGINE_USER"; - public static final UserTaskState INACTIVE = UserTaskState.of(null); + public static final String PARAMETER_USER = "USER"; + + public static final String ACTIVATE = "activate"; + public static final String CLAIM = "claim"; + public static final String RELEASE = "release"; + public static final String COMPLETE = "complete"; + public static final String SKIP = "skip"; + public static final String FAIL = "fail"; + + public static final UserTaskState INACTIVE = UserTaskState.initalized(); public static final UserTaskState ACTIVE = UserTaskState.of("Ready"); public static final UserTaskState RESERVED = UserTaskState.of("Reserved"); public static final UserTaskState COMPLETED = UserTaskState.of("Completed", TerminationType.COMPLETED); public static final UserTaskState ERROR = UserTaskState.of("Error", TerminationType.ERROR); public static final UserTaskState OBSOLETE = UserTaskState.of("Obsolete", TerminationType.OBSOLETE); - private static final UserTaskTransition T_NEW_ACTIVE = new DefaultUserTransition("activate", INACTIVE, ACTIVE, DefaultUserTaskLifeCycle::activate); - private static final UserTaskTransition T_ACTIVE_RESERVED = new DefaultUserTransition("claim", ACTIVE, RESERVED, DefaultUserTaskLifeCycle::claim); - private static final UserTaskTransition T_RESERVED_ACTIVE = new DefaultUserTransition("release", RESERVED, ACTIVE, DefaultUserTaskLifeCycle::release); - private static final UserTaskTransition T_ACTIVE_COMPLETED = new DefaultUserTransition("complete", ACTIVE, COMPLETED, DefaultUserTaskLifeCycle::complete); - private static final UserTaskTransition T_RESERVED_COMPLETED = new DefaultUserTransition("complete", RESERVED, COMPLETED, DefaultUserTaskLifeCycle::complete); - private static final UserTaskTransition T_RESERVED_SKIPPED = new DefaultUserTransition("skip", RESERVED, OBSOLETE, DefaultUserTaskLifeCycle::skip); - private static final UserTaskTransition T_ACTIVE_SKIPPED = new DefaultUserTransition("skip", ACTIVE, OBSOLETE, DefaultUserTaskLifeCycle::complete); - private static final UserTaskTransition T_RESERVED_ERROR = new DefaultUserTransition("fail", RESERVED, ERROR, DefaultUserTaskLifeCycle::fail); + private final UserTaskTransition T_NEW_ACTIVE = new DefaultUserTransition(ACTIVATE, INACTIVE, ACTIVE, this::activate); + private final UserTaskTransition T_ACTIVE_RESERVED = new DefaultUserTransition(CLAIM, ACTIVE, RESERVED, this::claim); + private final UserTaskTransition T_ACTIVE_SKIPPED = new DefaultUserTransition(SKIP, ACTIVE, OBSOLETE, this::skip); + private final UserTaskTransition T_ACTIVE_ERROR = new DefaultUserTransition(FAIL, ACTIVE, ERROR, this::fail); + private final UserTaskTransition T_RESERVED_ACTIVE = new DefaultUserTransition(RELEASE, RESERVED, ACTIVE, this::release); + private final UserTaskTransition T_RESERVED_COMPLETED = new DefaultUserTransition(COMPLETE, RESERVED, COMPLETED, this::complete); + private final UserTaskTransition T_RESERVED_SKIPPED = new DefaultUserTransition(SKIP, RESERVED, OBSOLETE, this::skip); + private final UserTaskTransition T_RESERVED_ERROR = new DefaultUserTransition(FAIL, RESERVED, ERROR, this::fail); private List transitions; @@ -53,58 +71,133 @@ public DefaultUserTaskLifeCycle() { transitions = List.of( T_NEW_ACTIVE, T_ACTIVE_RESERVED, + T_ACTIVE_SKIPPED, + T_ACTIVE_ERROR, T_RESERVED_ACTIVE, - T_ACTIVE_COMPLETED, T_RESERVED_COMPLETED, - T_ACTIVE_SKIPPED, T_RESERVED_SKIPPED, T_RESERVED_ERROR); } @Override - public Optional transition(UserTaskInstance userTaskInstance, UserTaskTransitionToken transition) { - return Optional.empty(); + public List allowedTransitions(UserTaskInstance userTaskInstance) { + return transitions.stream().filter(t -> t.source().equals(userTaskInstance.getStatus())).toList(); } @Override - public UserTaskTransitionToken newTransitionToken(String transitionId, UserTaskInstance userTaskInstance, Map data) { - UserTaskState state = userTaskInstance.getStatus(); - UserTaskTransition transition = transitions.stream().filter(e -> e.source().equals(state) && e.id().equals(transitionId)).findAny() - .orElseThrow(() -> new RuntimeException("Invalid transition " + transitionId + " from " + state)); - return new DefaultUserTaskTransitionToken(transition, data); + public Optional transition(UserTaskInstance userTaskInstance, UserTaskTransitionToken userTaskTransitionToken, IdentityProvider identityProvider) { + checkPermission(userTaskInstance, identityProvider); + UserTaskTransition transition = transitions.stream() + .filter(t -> t.source().equals(userTaskInstance.getStatus()) && t.id().equals(userTaskTransitionToken.transitionId())) + .findFirst() + .orElseThrow(() -> new UserTaskTransitionException("Invalid transition from " + userTaskInstance.getStatus())); + return transition.executor().execute(userTaskInstance, userTaskTransitionToken, identityProvider); } @Override public UserTaskTransitionToken newCompleteTransitionToken(UserTaskInstance userTaskInstance, Map data) { - return newTransitionToken("complete", userTaskInstance, data); + return newTransitionToken(COMPLETE, userTaskInstance.getStatus(), data); } @Override public UserTaskTransitionToken newAbortTransitionToken(UserTaskInstance userTaskInstance, Map data) { - return newTransitionToken("fail", userTaskInstance, data); + return newTransitionToken(FAIL, userTaskInstance.getStatus(), data); } - public static Optional activate(UserTaskInstance userTaskInstance, UserTaskTransitionToken token) { + @Override + public UserTaskTransitionToken newTransitionToken(String transitionId, UserTaskInstance userTaskInstance, Map data) { + return newTransitionToken(transitionId, userTaskInstance.getStatus(), data); + } + + public UserTaskTransitionToken newTransitionToken(String transitionId, UserTaskState state, Map data) { + UserTaskTransition transition = transitions.stream().filter(e -> e.source().equals(state) && e.id().equals(transitionId)).findAny() + .orElseThrow(() -> new RuntimeException("Invalid transition " + transitionId + " from " + state)); + return new DefaultUserTaskTransitionToken(transition.id(), transition.source(), transition.target(), data); + } + + public Optional activate(UserTaskInstance userTaskInstance, UserTaskTransitionToken token, IdentityProvider identityProvider) { + String user = assignStrategy(userTaskInstance, identityProvider); + if (user != null) { + return Optional.of(newTransitionToken(CLAIM, ACTIVE, Map.of(PARAMETER_USER, user))); + } return Optional.empty(); } - public static Optional claim(UserTaskInstance userTaskInstance, UserTaskTransitionToken token) { + public Optional claim(UserTaskInstance userTaskInstance, UserTaskTransitionToken token, IdentityProvider identityProvider) { + if (userTaskInstance instanceof DefaultUserTaskInstance defaultUserTaskInstance) { + if (token.data().containsKey(PARAMETER_USER)) { + defaultUserTaskInstance.setActuaOwner((String) token.data().get(PARAMETER_USER)); + } else { + defaultUserTaskInstance.setActuaOwner(identityProvider.getName()); + } + } return Optional.empty(); } - public static Optional release(UserTaskInstance userTaskInstance, UserTaskTransitionToken token) { + public Optional release(UserTaskInstance userTaskInstance, UserTaskTransitionToken token, IdentityProvider identityProvider) { + if (userTaskInstance instanceof DefaultUserTaskInstance defaultUserTaskInstance) { + defaultUserTaskInstance.setActuaOwner(null); + } return Optional.empty(); } - public static Optional complete(UserTaskInstance userTaskInstance, UserTaskTransitionToken token) { + public Optional complete(UserTaskInstance userTaskInstance, UserTaskTransitionToken token, IdentityProvider identityProvider) { return Optional.empty(); } - public static Optional skip(UserTaskInstance userTaskInstance, UserTaskTransitionToken token) { + public Optional skip(UserTaskInstance userTaskInstance, UserTaskTransitionToken token, IdentityProvider identityProvider) { return Optional.empty(); } - public static Optional fail(UserTaskInstance userTaskInstance, UserTaskTransitionToken token) { + public Optional fail(UserTaskInstance userTaskInstance, UserTaskTransitionToken token, IdentityProvider identityProvider) { return Optional.empty(); } + + private String assignStrategy(UserTaskInstance userTaskInstance, IdentityProvider identityProvider) { + UserTaskAssignmentStrategy assignmentStrategy = userTaskInstance.getUserTask().getAssignmentStrategy(); + return assignmentStrategy.computeAssigment(userTaskInstance, identityProvider).orElse(null); + } + + private void checkPermission(UserTaskInstance userTaskInstance, IdentityProvider identityProvider) { + String user = identityProvider.getName(); + Collection roles = identityProvider.getRoles(); + + if (WORKFLOW_ENGINE_USER.equals(user)) { + return; + } + + // first we check admins + Set adminUsers = userTaskInstance.getAdminUsers(); + if (adminUsers.contains(user)) { + return; + } + + Set userAdminGroups = new HashSet<>(userTaskInstance.getAdminGroups()); + userAdminGroups.retainAll(roles); + if (!userAdminGroups.isEmpty()) { + return; + } + + if (userTaskInstance.getActualOwner() != null && userTaskInstance.getActualOwner().equals(user)) { + return; + } + + if (List.of(INACTIVE, ACTIVE).contains(userTaskInstance.getStatus())) { + // there is no user + Set users = new HashSet<>(userTaskInstance.getPotentialUsers()); + users.removeAll(userTaskInstance.getExcludedUsers()); + if (users.contains(identityProvider.getName())) { + return; + } + + Set userPotGroups = new HashSet<>(userTaskInstance.getPotentialGroups()); + userPotGroups.retainAll(roles); + if (!userPotGroups.isEmpty()) { + return; + } + } + + throw new NotAuthorizedException("user " + user + " with roles " + roles + " not autorized to perform an operation on user task " + userTaskInstance.getId()); + } + } diff --git a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/lifecycle/DefaultUserTaskTransitionToken.java b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/lifecycle/DefaultUserTaskTransitionToken.java index 303d9d8e80a..f8cfd7733fc 100644 --- a/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/lifecycle/DefaultUserTaskTransitionToken.java +++ b/jbpm/jbpm-usertask/src/main/java/org/kie/kogito/usertask/impl/lifecycle/DefaultUserTaskTransitionToken.java @@ -20,21 +20,25 @@ import java.util.Map; -import org.kie.kogito.usertask.lifecycle.UserTaskTransition; +import org.kie.kogito.usertask.lifecycle.UserTaskState; import org.kie.kogito.usertask.lifecycle.UserTaskTransitionToken; public class DefaultUserTaskTransitionToken implements UserTaskTransitionToken { - private UserTaskTransition transition; + private String transition; private Map data; + private UserTaskState source; + private UserTaskState target; - public DefaultUserTaskTransitionToken(UserTaskTransition transition, Map data) { + public DefaultUserTaskTransitionToken(String transition, UserTaskState source, UserTaskState target, Map data) { this.transition = transition; + this.source = source; + this.target = target; this.data = data; } @Override - public UserTaskTransition transition() { + public String transitionId() { return transition; } @@ -43,4 +47,14 @@ public Map data() { return data; } + @Override + public UserTaskState source() { + return source; + } + + @Override + public UserTaskState target() { + return target; + } + } diff --git a/jbpm/process-workitems/src/main/java/org/kie/kogito/process/workitems/impl/DefaultKogitoWorkItemHandler.java b/jbpm/process-workitems/src/main/java/org/kie/kogito/process/workitems/impl/DefaultKogitoWorkItemHandler.java index 993fb8f8ade..460f5edb62d 100644 --- a/jbpm/process-workitems/src/main/java/org/kie/kogito/process/workitems/impl/DefaultKogitoWorkItemHandler.java +++ b/jbpm/process-workitems/src/main/java/org/kie/kogito/process/workitems/impl/DefaultKogitoWorkItemHandler.java @@ -45,6 +45,11 @@ public class DefaultKogitoWorkItemHandler implements KogitoWorkItemHandler { public static final String TRANSITION_ACTIVATE = "activate"; public static final String TRANSITION_SKIP = "skip"; + public static final WorkItemPhaseState initialized = 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"); + private static Logger LOG = LoggerFactory.getLogger(DefaultKogitoWorkItemHandler.class); protected Application application; @@ -60,11 +65,6 @@ public DefaultKogitoWorkItemHandler() { } public WorkItemLifeCycle initialize() { - WorkItemPhaseState initialized = WorkItemPhaseState.initialized(); - WorkItemPhaseState completed = WorkItemPhaseState.of("Completed", WorkItemTerminationType.COMPLETE); - WorkItemPhaseState aborted = WorkItemPhaseState.of("Aborted", WorkItemTerminationType.ABORT); - WorkItemPhaseState activated = WorkItemPhaseState.of("Activated"); - DefaultWorkItemLifeCyclePhase complete = new DefaultWorkItemLifeCyclePhase(TRANSITION_COMPLETE, activated, completed, this::completeWorkItemHandler); DefaultWorkItemLifeCyclePhase abort = new DefaultWorkItemLifeCyclePhase(TRANSITION_ABORT, activated, aborted, this::abortWorkItemHandler); DefaultWorkItemLifeCyclePhase active = new DefaultWorkItemLifeCyclePhase(TRANSITION_ACTIVATE, initialized, activated, this::activateWorkItemHandler); diff --git a/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/java/org/kie/kogito/codegen/tests/CallActivityTaskIT.java b/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/java/org/kie/kogito/codegen/tests/CallActivityTaskIT.java index 372f770c88e..9ac5584099e 100644 --- a/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/java/org/kie/kogito/codegen/tests/CallActivityTaskIT.java +++ b/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/java/org/kie/kogito/codegen/tests/CallActivityTaskIT.java @@ -151,7 +151,7 @@ public void testCallActivityTaskWithExpressionsForIO() throws Exception { assertThat(workItems).hasSize(1); WorkItem wi = workItems.get(0); assertThat(wi.getName()).isEqualTo("MyTask"); - assertThat(wi.getPhaseStatus()).isEqualTo(UserTaskKogitoWorkItemHandler.ACTIVATED.getName()); + assertThat(wi.getPhaseStatus()).isEqualTo(UserTaskKogitoWorkItemHandler.activated.getName()); KogitoWorkItemHandler handler = getWorkItemHandler(p, wi); WorkItemTransition transition = handler.completeTransition(workItems.get(0).getPhaseStatus(), parameters, securityPolicy); diff --git a/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/java/org/kie/kogito/codegen/tests/PublishEventIT.java b/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/java/org/kie/kogito/codegen/tests/PublishEventIT.java index 39efe723397..5284672e0a0 100644 --- a/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/java/org/kie/kogito/codegen/tests/PublishEventIT.java +++ b/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/java/org/kie/kogito/codegen/tests/PublishEventIT.java @@ -149,8 +149,11 @@ public void testCompensationProcess() throws Exception { } - private Optional findUserTaskInstanceEvent(List> events, String status) { - return events.stream().filter(UserTaskInstanceStateDataEvent.class::isInstance).map(e -> (UserTaskInstanceStateDataEvent) e).filter(e -> status.equals(e.getData().getState())).findAny(); + private Optional findUserTaskInstanceEvent(List> events, String taskName, String status) { + return events.stream().filter(UserTaskInstanceStateDataEvent.class::isInstance) + .map(e -> (UserTaskInstanceStateDataEvent) e) + .filter(e -> status.equals(e.getData().getState()) && e.getData().getUserTaskName().equals(taskName)) + .findAny(); } private Optional> findProcessInstanceEvent(List> events, int state) { @@ -205,7 +208,7 @@ public void onUserTaskState(UserTaskStateEvent event) { List left = findNodeInstanceEvents(events, 2); assertThat(left).hasSize(1).extractingResultOf("getNodeType").containsOnly("StartNode"); - Optional userFirstTask = findUserTaskInstanceEvent(events, "Ready"); + Optional userFirstTask = findUserTaskInstanceEvent(events, "FirstTask", "Ready"); assertThat(userFirstTask).isPresent(); assertUserTaskInstanceEvent(userFirstTask.get(), "FirstTask", null, "1", "Ready", "UserTasksProcess", "First Task"); @@ -226,11 +229,12 @@ public void onUserTaskState(UserTaskStateEvent event) { left = findNodeInstanceEvents(events, 1); assertThat(left).hasSize(1).extractingResultOf("getNodeType").containsOnly("HumanTaskNode"); - Optional firstUserTaskInstance = findUserTaskInstanceEvent(events, "Ready"); - Optional secondUserTaskInstance = findUserTaskInstanceEvent(events, "Completed"); + Optional firstUserTaskInstance = findUserTaskInstanceEvent(events, "SecondTask", "Ready"); + // we completed through the work item handler so the user task is really obsolete + Optional secondUserTaskInstance = findUserTaskInstanceEvent(events, "FirstTask", "Obsolete"); assertUserTaskInstanceEvent(firstUserTaskInstance.get(), "SecondTask", null, "1", "Ready", "UserTasksProcess", "Second Task"); - assertUserTaskInstanceEvent(secondUserTaskInstance.get(), "FirstTask", null, "1", "Completed", "UserTasksProcess", "First Task"); + assertUserTaskInstanceEvent(secondUserTaskInstance.get(), "FirstTask", null, "1", "Obsolete", "UserTasksProcess", "First Task"); workItems = processInstance.workItems(securityPolicy); assertThat(workItems).hasSize(1); @@ -252,7 +256,7 @@ public void onUserTaskState(UserTaskStateEvent event) { left = findNodeInstanceEvents(events, 2); assertThat(left).hasSize(2).extractingResultOf("getNodeType").containsOnly("HumanTaskNode", "EndNode"); - assertUserTaskInstanceEvent(events.get(0), "SecondTask", null, "1", "Completed", "UserTasksProcess", "Second Task"); + assertUserTaskInstanceEvent(events.get(0), "SecondTask", null, "1", "Obsolete", "UserTasksProcess", "Second Task"); } @Test @@ -285,7 +289,7 @@ public void testBasicUserTaskProcessAbort() throws Exception { List triggered = findNodeInstanceEvents(events, 1); assertThat(triggered).hasSize(2).extractingResultOf("getNodeName").containsOnly("StartProcess", "First Task"); - Optional event = findUserTaskInstanceEvent(events, "Ready"); + Optional event = findUserTaskInstanceEvent(events, "FirstTask", "Ready"); assertThat(event).isPresent(); assertUserTaskInstanceEvent(event.get(), "FirstTask", null, "1", "Ready", "UserTasksProcess", "First Task"); @@ -343,7 +347,7 @@ public void testBasicUserTaskProcessWithSecurityRoles() throws Exception { List left = findNodeInstanceEvents(events, 2); assertThat(left).hasSize(1).extractingResultOf("getNodeType").containsOnly("StartNode"); - Optional userTask = findUserTaskInstanceEvent(events, "Ready"); + Optional userTask = findUserTaskInstanceEvent(events, "FirstTask", "Ready"); assertThat(userTask).isPresent(); assertUserTaskInstanceEvent(userTask.get(), "FirstTask", null, "1", "Ready", "UserTasksProcess", "First Task"); } diff --git a/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/java/org/kie/kogito/codegen/tests/UserTaskIT.java b/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/java/org/kie/kogito/codegen/tests/UserTaskIT.java index dc15d15ae9f..00b7f6526a2 100644 --- a/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/java/org/kie/kogito/codegen/tests/UserTaskIT.java +++ b/kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/java/org/kie/kogito/codegen/tests/UserTaskIT.java @@ -26,7 +26,6 @@ import java.util.Map; import java.util.Optional; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.kie.kogito.Application; import org.kie.kogito.Model; @@ -38,31 +37,32 @@ import org.kie.kogito.internal.process.event.DefaultKogitoProcessEventListener; import org.kie.kogito.internal.process.event.ProcessWorkItemTransitionEvent; import org.kie.kogito.internal.process.runtime.KogitoProcessInstance; -import org.kie.kogito.internal.process.workitem.KogitoWorkItemHandler; import org.kie.kogito.internal.process.workitem.NotAuthorizedException; import org.kie.kogito.internal.process.workitem.Policy; -import org.kie.kogito.internal.process.workitem.WorkItemNotFoundException; -import org.kie.kogito.internal.process.workitem.WorkItemTransition; +import org.kie.kogito.jbpm.usertask.handler.UserTaskKogitoWorkItemHandler; +import org.kie.kogito.jbpm.usertask.handler.UserTaskKogitoWorkItemHandlerProcessListener; import org.kie.kogito.process.Process; import org.kie.kogito.process.ProcessConfig; import org.kie.kogito.process.ProcessInstance; import org.kie.kogito.process.Processes; import org.kie.kogito.process.VariableViolationException; import org.kie.kogito.process.WorkItem; +import org.kie.kogito.usertask.UserTask; +import org.kie.kogito.usertask.UserTaskConfig; +import org.kie.kogito.usertask.UserTaskInstance; +import org.kie.kogito.usertask.UserTasks; +import org.kie.kogito.usertask.impl.lifecycle.DefaultUserTaskLifeCycle; import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType; -import static org.kie.kogito.jbpm.usertask.handler.UserTaskKogitoWorkItemHandler.ACTIVATED; -import static org.kie.kogito.jbpm.usertask.handler.UserTaskKogitoWorkItemHandler.RESERVED; -import static org.kie.kogito.jbpm.usertask.handler.UserTaskKogitoWorkItemHandler.TRANSITION_ACTIVATED_CLAIM; -import static org.kie.kogito.jbpm.usertask.handler.UserTaskKogitoWorkItemHandler.TRANSITION_RESERVED_COMPLETE; -import static org.kie.kogito.jbpm.usertask.handler.UserTaskKogitoWorkItemHandler.TRANSITION_RESERVED_RELEASE; - -@Disabled +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.kie.kogito.usertask.impl.lifecycle.DefaultUserTaskLifeCycle.CLAIM; +import static org.kie.kogito.usertask.impl.lifecycle.DefaultUserTaskLifeCycle.COMPLETE; +import static org.kie.kogito.usertask.impl.lifecycle.DefaultUserTaskLifeCycle.RELEASE; + public class UserTaskIT extends AbstractCodegenIT { private Policy securityPolicy = SecurityPolicy.of("john", emptyList()); @@ -86,6 +86,9 @@ public void afterWorkItemTransition(ProcessWorkItemTransitionEvent event) { } }); + // we wired user tasks and processes + app.config().get(UserTaskConfig.class).userTaskEventListeners().listeners().add(new UserTaskKogitoWorkItemHandlerProcessListener(app.get(Processes.class))); + Process p = app.get(Processes.class).processById("UserTasksProcess"); Model m = p.createModel(); @@ -100,28 +103,55 @@ public void afterWorkItemTransition(ProcessWorkItemTransitionEvent event) { List workItems = processInstance.workItems(securityPolicy); assertThat(workItems).hasSize(1); assertThat(workItems.get(0).getName()).isEqualTo("FirstTask"); - WorkItem wi = workItems.get(0); + WorkItem wi_1 = workItems.get(0); - KogitoWorkItemHandler handler = getWorkItemHandler(p, wi); - WorkItemTransition transition = handler.newTransition(TRANSITION_ACTIVATED_CLAIM.id(), wi.getPhaseStatus(), singletonMap("ACTUAL_OWNER", "john"), securityPolicy); - processInstance.transitionWorkItem(workItems.get(0).getId(), transition); + UserTasks userTasks = app.get(UserTasks.class); + + UserTask userTask_1 = userTasks.userTaskById(getUserTaskId(wi_1.getExternalReferenceId())); + UserTaskInstance userTaskInstance_1 = userTask_1.instances().findById(getUserTaskInstanceId(wi_1.getExternalReferenceId())).get(); + assertThat(userTaskInstance_1).isNotNull(); + + List userTaskList = userTasks.instances().findByIdentity(IdentityProviders.of("mary")); + assertThat(userTaskList).hasSize(1); + + userTaskList = userTask_1.instances().findByIdentity(IdentityProviders.of("invalid")); + assertThat(userTaskList).hasSize(0); + + userTaskList = userTask_1.instances().findByIdentity(IdentityProviders.of("john")); + assertThat(userTaskList).hasSize(1); + + userTaskInstance_1 = userTaskList.get(0); + userTaskInstance_1.transition(CLAIM, emptyMap(), IdentityProviders.of("john")); + userTaskInstance_1.transition(COMPLETE, emptyMap(), IdentityProviders.of("john")); - workItems = processInstance.workItems(securityPolicy); - assertThat(workItems).hasSize(1); - wi = workItems.get(0); - assertThat(wi.getName()).isEqualTo("FirstTask"); - transition = handler.newTransition(TRANSITION_RESERVED_COMPLETE.id(), wi.getPhaseStatus(), singletonMap("ACTUAL_OWNER", "john"), securityPolicy); - processInstance.transitionWorkItem(workItems.get(0).getId(), transition); assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_ACTIVE); workItems = processInstance.workItems(securityPolicy); assertThat(workItems).hasSize(1); assertThat(workItems.get(0).getName()).isEqualTo("SecondTask"); + WorkItem wi_2 = workItems.get(0); + + UserTask userTask_2 = userTasks.userTaskById(getUserTaskId(wi_2.getExternalReferenceId())); + UserTaskInstance userTaskInstance_2 = userTask_2.instances().findById(getUserTaskInstanceId(wi_2.getExternalReferenceId())).get(); + assertThat(userTaskInstance_2).isNotNull(); + + userTaskList = userTasks.instances().findByIdentity(IdentityProviders.of("john")); + assertThat(userTaskList).hasSize(1); + + userTaskInstance_1 = userTaskList.get(0); + userTaskInstance_2.transition(COMPLETE, emptyMap(), IdentityProviders.of("john")); - processInstance.completeWorkItem(workItems.get(0).getId(), null, securityPolicy); assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_COMPLETED); - assertThat(workItemTransitionEvents).hasSize(12); + assertThat(workItemTransitionEvents).hasSize(8); + } + + private String getUserTaskInstanceId(String externalReference) { + return externalReference.split(":")[1]; + } + + private String getUserTaskId(String externalReference) { + return externalReference.split(":")[0]; } @Test @@ -129,7 +159,7 @@ public void testBasicUserTaskProcessPhases() throws Exception { Application app = generateCodeProcessesOnly("usertask/UserTasksProcess.bpmn2"); assertThat(app).isNotNull(); - + app.config().get(UserTaskConfig.class).userTaskEventListeners().listeners().add(new UserTaskKogitoWorkItemHandlerProcessListener(app.get(Processes.class))); Process p = app.get(Processes.class).processById("UserTasksProcess"); Model m = p.createModel(); @@ -146,16 +176,12 @@ public void testBasicUserTaskProcessPhases() throws Exception { WorkItem wi = workItems.get(0); assertThat(wi.getName()).isEqualTo("FirstTask"); - KogitoWorkItemHandler handler = getWorkItemHandler(p, wi); - WorkItemTransition transition = handler.newTransition(TRANSITION_ACTIVATED_CLAIM.id(), wi.getPhaseStatus(), singletonMap("ACTUAL_OWNER", "john"), securityPolicy); - processInstance.transitionWorkItem(workItems.get(0).getId(), transition); - - workItems = processInstance.workItems(securityPolicy); - assertThat(workItems).hasSize(1); - wi = workItems.get(0); - assertThat(wi.getName()).isEqualTo("FirstTask"); - transition = handler.newTransition(TRANSITION_RESERVED_COMPLETE.id(), wi.getPhaseStatus(), singletonMap("ACTUAL_OWNER", "john"), securityPolicy); - processInstance.transitionWorkItem(workItems.get(0).getId(), transition); + UserTasks userTasks = app.get(UserTasks.class); + List userTaskInstances = userTasks.instances().findByIdentity(IdentityProviders.of("john")); + assertThat(userTaskInstances).isNotNull().hasSize(1); + UserTaskInstance ut_1 = userTaskInstances.get(0); + ut_1.transition(CLAIM, emptyMap(), IdentityProviders.of("john")); + ut_1.transition(COMPLETE, emptyMap(), IdentityProviders.of("john")); assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_ACTIVE); @@ -164,9 +190,11 @@ public void testBasicUserTaskProcessPhases() throws Exception { wi = workItems.get(0); assertThat(wi.getName()).isEqualTo("SecondTask"); - handler = getWorkItemHandler(p, wi); - transition = handler.completeTransition(workItems.get(0).getPhaseStatus(), parameters, securityPolicy); - processInstance.transitionWorkItem(workItems.get(0).getId(), transition); + userTaskInstances = userTasks.instances().findByIdentity(IdentityProviders.of("john")); + assertThat(userTaskInstances).isNotNull().hasSize(1); + UserTaskInstance ut_2 = userTaskInstances.get(0); + ut_2.transition(COMPLETE, emptyMap(), IdentityProviders.of("john")); + assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_COMPLETED); } @@ -175,7 +203,7 @@ public void testBasicUserTaskProcessClaimAndCompletePhases() throws Exception { Application app = generateCodeProcessesOnly("usertask/UserTasksProcess.bpmn2"); assertThat(app).isNotNull(); - + app.config().get(UserTaskConfig.class).userTaskEventListeners().listeners().add(new UserTaskKogitoWorkItemHandlerProcessListener(app.get(Processes.class))); Process p = app.get(Processes.class).processById("UserTasksProcess"); Model m = p.createModel(); @@ -193,32 +221,20 @@ public void testBasicUserTaskProcessClaimAndCompletePhases() throws Exception { assertThat(wi.getName()).isEqualTo("FirstTask"); assertThat(wi.getResults()).isEmpty(); - KogitoWorkItemHandler handler = getWorkItemHandler(p, wi); - WorkItemTransition transition = handler.newTransition(TRANSITION_ACTIVATED_CLAIM.id(), wi.getPhaseStatus(), Map.of("ACTUAL_OWNER", "john", "test", "value"), securityPolicy); - processInstance.transitionWorkItem(workItems.get(0).getId(), transition); - + UserTasks userTasks = app.get(UserTasks.class); + List userTaskInstances = userTasks.instances().findByIdentity(IdentityProviders.of("john")); + assertThat(userTaskInstances).isNotNull().hasSize(1); + UserTaskInstance ut_1 = userTaskInstances.get(0); + ut_1.transition(CLAIM, emptyMap(), IdentityProviders.of("john")); assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_ACTIVE); - workItems = processInstance.workItems(securityPolicy); - assertThat(workItems).hasSize(1); - wi = workItems.get(0); - assertThat(wi.getName()).isEqualTo("FirstTask"); - - assertThat(wi.getResults()).hasSize(1) - .containsEntry("test", "value"); - - handler = getWorkItemHandler(p, wi); - transition = handler.newTransition(TRANSITION_RESERVED_COMPLETE.id(), wi.getPhaseStatus(), emptyMap(), securityPolicy); - processInstance.transitionWorkItem(workItems.get(0).getId(), transition); - + ut_1.transition(COMPLETE, Map.of("test", "value"), IdentityProviders.of("john")); assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_ACTIVE); - workItems = processInstance.workItems(securityPolicy); - assertThat(workItems).hasSize(1); - wi = workItems.get(0); - assertThat(wi.getName()).isEqualTo("SecondTask"); - assertThat(wi.getPhaseStatus()).isEqualTo(RESERVED.getName()); - assertThat(wi.getResults()).isEmpty(); + userTaskInstances = userTasks.instances().findByIdentity(IdentityProviders.of("john")); + assertThat(userTaskInstances).isNotNull().hasSize(1); + UserTaskInstance ut_2 = userTaskInstances.get(0); + assertThat(ut_2.getStatus()).isEqualTo(DefaultUserTaskLifeCycle.RESERVED); processInstance.abort(); assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_ABORTED); @@ -229,7 +245,7 @@ public void testBasicUserTaskProcessReleaseAndCompletePhases() throws Exception Application app = generateCodeProcessesOnly("usertask/UserTasksProcess.bpmn2"); assertThat(app).isNotNull(); - + app.config().get(UserTaskConfig.class).userTaskEventListeners().listeners().add(new UserTaskKogitoWorkItemHandlerProcessListener(app.get(Processes.class))); Process p = app.get(Processes.class).processById("UserTasksProcess"); Model m = p.createModel(); @@ -245,43 +261,26 @@ public void testBasicUserTaskProcessReleaseAndCompletePhases() throws Exception assertThat(workItems).hasSize(1); WorkItem wi = workItems.get(0); assertThat(wi.getName()).isEqualTo("FirstTask"); - assertThat(wi.getPhaseStatus()).isEqualTo(ACTIVATED.getName()); + assertThat(wi.getPhaseStatus()).isEqualTo(UserTaskKogitoWorkItemHandler.activated.getName()); assertThat(wi.getResults()).isEmpty(); - KogitoWorkItemHandler handler = getWorkItemHandler(p, wi); - assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_ACTIVE); - - WorkItemTransition claim = handler.newTransition(TRANSITION_ACTIVATED_CLAIM.id(), wi.getPhaseStatus(), singletonMap("ACTUAL_OWNER", "john"), securityPolicy); - processInstance.transitionWorkItem(wi.getId(), claim); + UserTasks userTasks = app.get(UserTasks.class); + List userTaskInstances = userTasks.instances().findByIdentity(IdentityProviders.of("john")); + assertThat(userTaskInstances).isNotNull().hasSize(1); + UserTaskInstance ut_1 = userTaskInstances.get(0); + ut_1.transition(CLAIM, emptyMap(), IdentityProviders.of("john")); + assertThat(ut_1.getStatus()).isEqualTo(DefaultUserTaskLifeCycle.RESERVED); + assertThat(ut_1.getActualOwner()).isEqualTo("john"); - workItems = processInstance.workItems(securityPolicy); - assertThat(workItems).hasSize(1); - wi = workItems.get(0); - assertThat(wi.getName()).isEqualTo("FirstTask"); - assertThat(wi.getPhaseStatus()).isEqualTo(RESERVED.getName()); - assertThat(wi.getResults()).isEmpty(); + ut_1.transition(RELEASE, emptyMap(), IdentityProviders.of("john")); + assertThat(ut_1.getStatus()).isEqualTo(DefaultUserTaskLifeCycle.ACTIVE); + assertThat(ut_1.getActualOwner()).isNull(); - WorkItemTransition release = handler.newTransition(TRANSITION_RESERVED_RELEASE.id(), wi.getPhaseStatus(), emptyMap(), securityPolicy); - processInstance.transitionWorkItem(wi.getId(), release); + ut_1.transition(DefaultUserTaskLifeCycle.CLAIM, emptyMap(), IdentityProviders.of("john")); + assertThat(ut_1.getStatus()).isEqualTo(DefaultUserTaskLifeCycle.RESERVED); + assertThat(ut_1.getActualOwner()).isEqualTo("john"); - workItems = processInstance.workItems(securityPolicy); - assertThat(workItems).hasSize(1); - wi = workItems.get(0); - assertThat(wi.getName()).isEqualTo("FirstTask"); - assertThat(wi.getPhaseStatus()).isEqualTo(ACTIVATED.getName()); - assertThat(wi.getResults()).isEmpty(); - - claim = handler.newTransition(TRANSITION_ACTIVATED_CLAIM.id(), wi.getPhaseStatus(), singletonMap("ACTUAL_OWNER", "john"), securityPolicy); - processInstance.transitionWorkItem(wi.getId(), claim); - - workItems = processInstance.workItems(securityPolicy); - assertThat(workItems).hasSize(1); - wi = workItems.get(0); - assertThat(wi.getName()).isEqualTo("FirstTask"); - assertThat(wi.getPhaseStatus()).isEqualTo(RESERVED.getName()); - assertThat(wi.getResults()).isEmpty(); - WorkItemTransition transition = handler.completeTransition(wi.getPhaseStatus(), emptyMap(), securityPolicy); - processInstance.transitionWorkItem(wi.getId(), transition); + ut_1.transition(DefaultUserTaskLifeCycle.COMPLETE, emptyMap(), IdentityProviders.of("john")); assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_ACTIVE); workItems = processInstance.workItems(securityPolicy); @@ -289,11 +288,14 @@ public void testBasicUserTaskProcessReleaseAndCompletePhases() throws Exception wi = workItems.get(0); assertThat(wi.getName()).isEqualTo("SecondTask"); - assertThat(wi.getPhaseStatus()).isEqualTo(RESERVED.getName()); - assertThat(wi.getResults()).isEmpty(); + userTaskInstances = userTasks.instances().findByIdentity(IdentityProviders.of("john")); + assertThat(userTaskInstances).isNotNull().hasSize(1); + UserTaskInstance ut_2 = userTaskInstances.get(0); + assertThat(ut_2.getStatus()).isEqualTo(DefaultUserTaskLifeCycle.RESERVED); processInstance.abort(); assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_ABORTED); + assertThat(userTasks.instances().findByIdentity(IdentityProviders.of("john"))).hasSize(0); } @Test @@ -301,6 +303,7 @@ public void testBasicUserTaskProcessClaimAndCompletePhasesWithIdentity() throws Application app = generateCodeProcessesOnly("usertask/UserTasksProcess.bpmn2"); assertThat(app).isNotNull(); + app.config().get(UserTaskConfig.class).userTaskEventListeners().listeners().add(new UserTaskKogitoWorkItemHandlerProcessListener(app.get(Processes.class))); final List workItemTransitionEvents = new ArrayList<>(); app.config().get(ProcessConfig.class).processEventListeners().listeners().add(new DefaultKogitoProcessEventListener() { @@ -330,37 +333,30 @@ public void afterWorkItemTransition(ProcessWorkItemTransitionEvent event) { assertThat(workItems).hasSize(1); WorkItem wi = workItems.get(0); assertThat(wi.getName()).isEqualTo("FirstTask"); - assertThat(wi.getPhaseStatus()).isEqualTo(ACTIVATED.getName()); + assertThat(wi.getPhaseStatus()).isEqualTo(UserTaskKogitoWorkItemHandler.activated.getName()); assertThat(wi.getResults()).isEmpty(); - KogitoWorkItemHandler handler = getWorkItemHandler(p, wi); - WorkItemTransition transition = handler.newTransition(TRANSITION_ACTIVATED_CLAIM.id(), wi.getPhaseStatus(), Map.of("ACTUAL_OWNER", "john", "test", "value"), securityPolicy); - processInstance.transitionWorkItem(workItems.get(0).getId(), transition); - - workItems = processInstance.workItems(securityPolicy); - assertThat(workItems).hasSize(1); - wi = workItems.get(0); - assertThat(wi.getName()).isEqualTo("FirstTask"); - assertThat(wi.getPhaseStatus()).isEqualTo(RESERVED.getName()); - assertThat(wi.getResults()).hasSize(1) - .containsEntry("test", "value"); - - transition = handler.completeTransition(wi.getPhaseStatus(), emptyMap(), securityPolicy); - processInstance.transitionWorkItem(workItems.get(0).getId(), transition); + UserTasks userTasks = app.get(UserTasks.class); + List userTaskInstances = userTasks.instances().findByIdentity(IdentityProviders.of("john")); + assertThat(userTaskInstances).isNotNull().hasSize(1); + UserTaskInstance ut_1 = userTaskInstances.get(0); + assertThat(ut_1.getStatus()).isEqualTo(DefaultUserTaskLifeCycle.ACTIVE); + ut_1.transition(CLAIM, emptyMap(), IdentityProviders.of("john")); + assertThat(ut_1.getStatus()).isEqualTo(DefaultUserTaskLifeCycle.RESERVED); + ut_1.transition(COMPLETE, emptyMap(), IdentityProviders.of("john")); + assertThat(ut_1.getStatus()).isEqualTo(DefaultUserTaskLifeCycle.COMPLETED); assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_ACTIVE); - workItems = processInstance.workItems(securityPolicy); - assertThat(workItems).hasSize(1); - wi = workItems.get(0); - assertThat(wi.getName()).isEqualTo("SecondTask"); - assertThat(wi.getPhaseStatus()).isEqualTo(RESERVED.getName()); - assertThat(wi.getResults()).isEmpty(); + userTaskInstances = userTasks.instances().findByIdentity(IdentityProviders.of("john")); + assertThat(userTaskInstances).isNotNull().hasSize(1); + UserTaskInstance ut_2 = userTaskInstances.get(0); + assertThat(ut_2.getStatus()).isEqualTo(DefaultUserTaskLifeCycle.RESERVED); processInstance.abort(); assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_ABORTED); - assertThat(workItemTransitionEvents).hasSize(12); + assertThat(workItemTransitionEvents).hasSize(8); } @Test @@ -368,7 +364,7 @@ public void testBasicUserTaskProcessClaimAndCompleteWrongUser() throws Exception Application app = generateCodeProcessesOnly("usertask/UserTasksProcess.bpmn2"); assertThat(app).isNotNull(); - + app.config().get(UserTaskConfig.class).userTaskEventListeners().listeners().add(new UserTaskKogitoWorkItemHandlerProcessListener(app.get(Processes.class))); Process p = app.get(Processes.class).processById("UserTasksProcess"); Model m = p.createModel(); @@ -384,7 +380,7 @@ public void testBasicUserTaskProcessClaimAndCompleteWrongUser() throws Exception assertThat(workItems).hasSize(1); WorkItem wi = workItems.get(0); assertThat(wi.getName()).isEqualTo("FirstTask"); - assertThat(wi.getPhaseStatus()).isEqualTo(ACTIVATED.getName()); + assertThat(wi.getPhaseStatus()).isEqualTo(UserTaskKogitoWorkItemHandler.activated.getName()); assertThat(wi.getResults()).isEmpty(); final String wiId = wi.getId(); @@ -394,11 +390,13 @@ public void testBasicUserTaskProcessClaimAndCompleteWrongUser() throws Exception List securedWorkItems = processInstance.workItems(SecurityPolicy.of(identity)); assertThat(securedWorkItems).isEmpty(); - assertThatExceptionOfType(WorkItemNotFoundException.class).isThrownBy(() -> processInstance.workItem(wiId, SecurityPolicy.of(identity))); + assertThatExceptionOfType(NotAuthorizedException.class).isThrownBy(() -> processInstance.workItem(wiId, SecurityPolicy.of(identity))); - KogitoWorkItemHandler handler = getWorkItemHandler(p, wi); - WorkItemTransition claimKelly = handler.newTransition(TRANSITION_ACTIVATED_CLAIM.id(), wi.getPhaseStatus(), singletonMap("ACTUAL_OWNER", "kelly"), SecurityPolicy.of(identity)); - assertThatExceptionOfType(NotAuthorizedException.class).isThrownBy(() -> processInstance.transitionWorkItem(wiId, claimKelly)); + UserTasks userTasks = app.get(UserTasks.class); + List userTaskInstances = userTasks.instances().findByIdentity(IdentityProviders.of("john")); + assertThat(userTaskInstances).isNotNull().hasSize(1); + UserTaskInstance utInvalid = userTaskInstances.get(0); + assertThatExceptionOfType(NotAuthorizedException.class).isThrownBy(() -> utInvalid.transition(CLAIM, emptyMap(), IdentityProviders.of("invalid"))); assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_ACTIVE); @@ -406,32 +404,32 @@ public void testBasicUserTaskProcessClaimAndCompleteWrongUser() throws Exception assertThat(workItems).hasSize(1); wi = workItems.get(0); assertThat(wi.getName()).isEqualTo("FirstTask"); - assertThat(wi.getPhaseStatus()).isEqualTo(ACTIVATED.getName()); + assertThat(wi.getPhaseStatus()).isEqualTo(UserTaskKogitoWorkItemHandler.activated.getName()); assertThat(wi.getResults()).isEmpty(); assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_ACTIVE); - WorkItemTransition claimJohn = handler.newTransition(TRANSITION_ACTIVATED_CLAIM.id(), wi.getPhaseStatus(), singletonMap("ACTUAL_OWNER", "john"), securityPolicy); - processInstance.transitionWorkItem(wiId, claimJohn); - - workItems = processInstance.workItems(securityPolicy); - assertThat(workItems).hasSize(1); - wi = workItems.get(0); - assertThat(wi.getName()).isEqualTo("FirstTask"); - assertThat(wi.getPhaseStatus()).isEqualTo(RESERVED.getName()); - assertThat(wi.getResults()).isEmpty(); - WorkItemTransition completeJohn = handler.newTransition(TRANSITION_RESERVED_COMPLETE.id(), wi.getPhaseStatus(), emptyMap(), securityPolicy); - processInstance.transitionWorkItem(wiId, completeJohn); + userTaskInstances = userTasks.instances().findByIdentity(IdentityProviders.of("john")); + assertThat(userTaskInstances).isNotNull().hasSize(1); + UserTaskInstance ut_1 = userTaskInstances.get(0); + ut_1.transition(CLAIM, emptyMap(), IdentityProviders.of("john")); + ut_1.transition(COMPLETE, emptyMap(), IdentityProviders.of("john")); workItems = processInstance.workItems(securityPolicy); assertThat(workItems).hasSize(1); wi = workItems.get(0); assertThat(wi.getName()).isEqualTo("SecondTask"); - assertThat(wi.getPhaseStatus()).isEqualTo(RESERVED.getName()); + assertThat(wi.getPhaseStatus()).isEqualTo(UserTaskKogitoWorkItemHandler.activated.getName()); assertThat(wi.getResults()).isEmpty(); + userTaskInstances = userTasks.instances().findByIdentity(IdentityProviders.of("john")); + assertThat(userTaskInstances).isNotNull().hasSize(1); + UserTaskInstance ut_2 = userTaskInstances.get(0); + assertThat(ut_2.getStatus()).isEqualTo(DefaultUserTaskLifeCycle.RESERVED); + processInstance.abort(); assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_ABORTED); + assertThat(userTasks.instances().findByIdentity(IdentityProviders.of("john"))).hasSize(0); } @Test @@ -439,7 +437,7 @@ public void testApprovalWithExcludedOwnerViaPhases() throws Exception { Application app = generateCodeProcessesOnly("usertask/approval.bpmn2"); assertThat(app).isNotNull(); - + app.config().get(UserTaskConfig.class).userTaskEventListeners().listeners().add(new UserTaskKogitoWorkItemHandlerProcessListener(app.get(Processes.class))); Process p = app.get(Processes.class).processById("approvals"); Model m = p.createModel(); @@ -458,10 +456,12 @@ public void testApprovalWithExcludedOwnerViaPhases() throws Exception { List workItems = processInstance.workItems(policy); assertThat(workItems).hasSize(1); - WorkItem wi = workItems.get(0); - KogitoWorkItemHandler handler = getWorkItemHandler(p, wi); - WorkItemTransition transition = handler.newTransition(TRANSITION_RESERVED_COMPLETE.id(), wi.getPhaseStatus(), singletonMap("ActorId", "manager"), policy); - processInstance.transitionWorkItem(workItems.get(0).getId(), transition); + UserTasks userTasks = app.get(UserTasks.class); + List userTaskInstances = userTasks.instances().findByIdentity(IdentityProviders.of("manager")); + assertThat(userTaskInstances).isNotNull().hasSize(1); + UserTaskInstance ut_1 = userTaskInstances.get(0); + ut_1.transition(COMPLETE, emptyMap(), IdentityProviders.of("manager")); + // actual owner of the first task is excluded owner on the second task so won't find it workItems = processInstance.workItems(policy); assertThat(workItems).isEmpty(); @@ -471,15 +471,12 @@ public void testApprovalWithExcludedOwnerViaPhases() throws Exception { workItems = processInstance.workItems(policy); assertThat(workItems).hasSize(1); - wi = workItems.get(0); - transition = handler.newTransition(TRANSITION_ACTIVATED_CLAIM.id(), wi.getPhaseStatus(), singletonMap("ACTUAL_OWNER", "john"), policy); - processInstance.transitionWorkItem(workItems.get(0).getId(), transition); - workItems = processInstance.workItems(policy); - assertThat(workItems).hasSize(1); - wi = workItems.get(0); - transition = handler.newTransition(TRANSITION_RESERVED_COMPLETE.id(), wi.getPhaseStatus(), emptyMap(), policy); - processInstance.transitionWorkItem(workItems.get(0).getId(), transition); + userTaskInstances = userTasks.instances().findByIdentity(IdentityProviders.of("admin", singletonList("managers"))); + assertThat(userTaskInstances).isNotNull().hasSize(1); + UserTaskInstance ut_2 = userTaskInstances.get(0); + ut_2.transition(CLAIM, emptyMap(), IdentityProviders.of("admin", singletonList("managers"))); + ut_2.transition(COMPLETE, emptyMap(), IdentityProviders.of("admin", singletonList("managers"))); assertThat(processInstance.status()).isEqualTo(KogitoProcessInstance.STATE_COMPLETED); } @@ -489,7 +486,7 @@ public void testApprovalWithExcludedOwner() throws Exception { Application app = generateCodeProcessesOnly("usertask/approval.bpmn2"); assertThat(app).isNotNull(); - + app.config().get(UserTaskConfig.class).userTaskEventListeners().listeners().add(new UserTaskKogitoWorkItemHandlerProcessListener(app.get(Processes.class))); Process p = app.get(Processes.class).processById("approvals"); Model m = p.createModel(); @@ -519,13 +516,13 @@ public void testApprovalWithExcludedOwner() throws Exception { assertThat(workItems).hasSize(1); assertThat(workItems).hasSize(1); - WorkItem wi = workItems.get(0); - - KogitoWorkItemHandler handler = getWorkItemHandler(p, wi); - WorkItemTransition transition = handler.newTransition(TRANSITION_ACTIVATED_CLAIM.id(), wi.getPhaseStatus(), singletonMap("ACTUAL_OWNER", "john"), policy); - processInstance.transitionWorkItem(workItems.get(0).getId(), transition); - processInstance.completeWorkItem(workItems.get(0).getId(), null, policy); + UserTasks userTasks = app.get(UserTasks.class); + List userTaskInstances = userTasks.instances().findByIdentity(identity); + assertThat(userTaskInstances).isNotNull().hasSize(1); + UserTaskInstance ut_1 = userTaskInstances.get(0); + ut_1.transition(CLAIM, emptyMap(), identity); + ut_1.transition(COMPLETE, emptyMap(), identity); assertThat(processInstance.status()).isEqualTo(KogitoProcessInstance.STATE_COMPLETED); } @@ -535,7 +532,7 @@ public void testBasicUserTaskProcessCancelAndTriggerNode() throws Exception { Application app = generateCodeProcessesOnly("usertask/UserTasksProcess.bpmn2"); assertThat(app).isNotNull(); - + app.config().get(UserTaskConfig.class).userTaskEventListeners().listeners().add(new UserTaskKogitoWorkItemHandlerProcessListener(app.get(Processes.class))); Process p = app.get(Processes.class).processById("UserTasksProcess"); Model m = p.createModel(); @@ -547,30 +544,20 @@ public void testBasicUserTaskProcessCancelAndTriggerNode() throws Exception { assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_ACTIVE); - List workItems = processInstance.workItems(securityPolicy); - assertThat(workItems).hasSize(1); - WorkItem wi = workItems.get(0); - assertThat(wi.getName()).isEqualTo("FirstTask"); - assertThat(wi.getPhaseStatus()).isEqualTo(ACTIVATED.getName()); - - KogitoWorkItemHandler handler = getWorkItemHandler(p, wi); - WorkItemTransition transition = handler.newTransition(TRANSITION_ACTIVATED_CLAIM.id(), wi.getPhaseStatus(), singletonMap("ACTUAL_OWNER", "john"), securityPolicy); - processInstance.transitionWorkItem(workItems.get(0).getId(), transition); - - workItems = processInstance.workItems(securityPolicy); - assertThat(workItems).hasSize(1); - wi = workItems.get(0); - assertThat(wi.getName()).isEqualTo("FirstTask"); - transition = handler.newTransition(TRANSITION_RESERVED_COMPLETE.id(), wi.getPhaseStatus(), singletonMap("ACTUAL_OWNER", "john"), securityPolicy); - processInstance.transitionWorkItem(workItems.get(0).getId(), transition); + UserTasks userTasks = app.get(UserTasks.class); + List userTaskInstances = userTasks.instances().findByIdentity(IdentityProviders.of("john")); + assertThat(userTaskInstances).isNotNull().hasSize(1); + UserTaskInstance ut_1 = userTaskInstances.get(0); + ut_1.transition(CLAIM, emptyMap(), IdentityProviders.of("john")); + ut_1.transition(COMPLETE, emptyMap(), IdentityProviders.of("john")); assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_ACTIVE); - workItems = processInstance.workItems(securityPolicy); + List workItems = processInstance.workItems(securityPolicy); assertThat(workItems).hasSize(1); - wi = workItems.get(0); + WorkItem wi = workItems.get(0); assertThat(wi.getName()).isEqualTo("SecondTask"); - assertThat(wi.getPhaseStatus()).isEqualTo(RESERVED.getName()); + assertThat(wi.getPhaseStatus()).isEqualTo(UserTaskKogitoWorkItemHandler.activated.getName()); String firstSecondTaskNodeInstanceId = wi.getNodeInstanceId(); @@ -582,11 +569,14 @@ public void testBasicUserTaskProcessCancelAndTriggerNode() throws Exception { assertThat(workItems).hasSize(1); wi = workItems.get(0); assertThat(wi.getName()).isEqualTo("SecondTask"); - assertThat(wi.getPhaseStatus()).isEqualTo(RESERVED.getName()); + assertThat(wi.getPhaseStatus()).isEqualTo(UserTaskKogitoWorkItemHandler.activated.getName()); // since it was triggered again it must have different node instance id assertThat(wi.getNodeInstanceId()).isNotEqualTo(firstSecondTaskNodeInstanceId); - transition = handler.completeTransition(workItems.get(0).getPhaseStatus(), parameters, securityPolicy); - processInstance.transitionWorkItem(workItems.get(0).getId(), transition); + + userTaskInstances = userTasks.instances().findByIdentity(IdentityProviders.of("john")); + assertThat(userTaskInstances).isNotNull().hasSize(1); + UserTaskInstance ut_2 = userTaskInstances.get(0); + ut_2.transition(COMPLETE, emptyMap(), IdentityProviders.of("john")); assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_COMPLETED); } @@ -596,7 +586,7 @@ public void testBasicUserTaskProcessCancelAndRetriggerNode() throws Exception { Application app = generateCodeProcessesOnly("usertask/UserTasksProcess.bpmn2"); assertThat(app).isNotNull(); - + app.config().get(UserTaskConfig.class).userTaskEventListeners().listeners().add(new UserTaskKogitoWorkItemHandlerProcessListener(app.get(Processes.class))); Process p = app.get(Processes.class).processById("UserTasksProcess"); Model m = p.createModel(); @@ -613,18 +603,14 @@ public void testBasicUserTaskProcessCancelAndRetriggerNode() throws Exception { WorkItem wi = workItems.get(0); assertThat(wi.getName()).isEqualTo("FirstTask"); - assertThat(wi.getPhaseStatus()).isEqualTo(ACTIVATED.getName()); + assertThat(wi.getPhaseStatus()).isEqualTo(UserTaskKogitoWorkItemHandler.activated.getName()); - KogitoWorkItemHandler handler = getWorkItemHandler(p, wi); - WorkItemTransition transition = handler.newTransition(TRANSITION_ACTIVATED_CLAIM.id(), wi.getPhaseStatus(), singletonMap("ACTUAL_OWNER", "john"), securityPolicy); - processInstance.transitionWorkItem(workItems.get(0).getId(), transition); - - workItems = processInstance.workItems(securityPolicy); - assertThat(workItems).hasSize(1); - wi = workItems.get(0); - assertThat(wi.getName()).isEqualTo("FirstTask"); - transition = handler.newTransition(TRANSITION_RESERVED_COMPLETE.id(), wi.getPhaseStatus(), singletonMap("ACTUAL_OWNER", "john"), securityPolicy); - processInstance.transitionWorkItem(workItems.get(0).getId(), transition); + UserTasks userTasks = app.get(UserTasks.class); + List userTaskInstances = userTasks.instances().findByIdentity(IdentityProviders.of("john")); + assertThat(userTaskInstances).isNotNull().hasSize(1); + UserTaskInstance ut_1 = userTaskInstances.get(0); + ut_1.transition(CLAIM, emptyMap(), IdentityProviders.of("john")); + ut_1.transition(COMPLETE, emptyMap(), IdentityProviders.of("john")); assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_ACTIVE); @@ -632,8 +618,7 @@ public void testBasicUserTaskProcessCancelAndRetriggerNode() throws Exception { assertThat(workItems).hasSize(1); wi = workItems.get(0); assertThat(wi.getName()).isEqualTo("SecondTask"); - - assertThat(wi.getPhaseStatus()).isEqualTo(RESERVED.getName()); + assertThat(wi.getPhaseStatus()).isEqualTo(UserTaskKogitoWorkItemHandler.activated.getName()); String firstSecondTaskNodeInstanceId = wi.getNodeInstanceId(); @@ -645,12 +630,14 @@ public void testBasicUserTaskProcessCancelAndRetriggerNode() throws Exception { wi = workItems.get(0); assertThat(wi.getName()).isEqualTo("SecondTask"); - assertThat(wi.getPhaseStatus()).isEqualTo(RESERVED.getName()); + assertThat(wi.getPhaseStatus()).isEqualTo(UserTaskKogitoWorkItemHandler.activated.getName()); // since it was retriggered it must have different node instance id assertThat(wi.getNodeInstanceId()).isNotEqualTo(firstSecondTaskNodeInstanceId); - transition = handler.completeTransition(workItems.get(0).getPhaseStatus(), parameters, securityPolicy); - processInstance.transitionWorkItem(wi.getId(), transition); + userTaskInstances = userTasks.instances().findByIdentity(IdentityProviders.of("john")); + assertThat(userTaskInstances).isNotNull().hasSize(1); + UserTaskInstance ut_2 = userTaskInstances.get(0); + ut_2.transition(COMPLETE, emptyMap(), IdentityProviders.of("john")); assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_COMPLETED); } @@ -660,7 +647,7 @@ public void testBasicUserTaskProcessClaimReleaseClaimAndCompletePhases() throws Application app = generateCodeProcessesOnly("usertask/UserTasksProcess.bpmn2"); assertThat(app).isNotNull(); - + app.config().get(UserTaskConfig.class).userTaskEventListeners().listeners().add(new UserTaskKogitoWorkItemHandlerProcessListener(app.get(Processes.class))); Process p = app.get(Processes.class).processById("UserTasksProcess"); Model m = p.createModel(); @@ -676,58 +663,36 @@ public void testBasicUserTaskProcessClaimReleaseClaimAndCompletePhases() throws assertThat(workItems).hasSize(1); WorkItem wi = workItems.get(0); assertThat(wi.getName()).isEqualTo("FirstTask"); - assertThat(wi.getPhaseStatus()).isEqualTo(ACTIVATED.getName()); + assertThat(wi.getPhaseStatus()).isEqualTo(UserTaskKogitoWorkItemHandler.activated.getName()); assertThat(wi.getResults()).isEmpty(); - KogitoWorkItemHandler handler = getWorkItemHandler(p, wi); - WorkItemTransition transition = handler.newTransition(TRANSITION_ACTIVATED_CLAIM.id(), wi.getPhaseStatus(), Map.of("ACTUAL_OWNER", "john", "test", "value"), securityPolicy); - processInstance.transitionWorkItem(workItems.get(0).getId(), transition); + UserTasks userTasks = app.get(UserTasks.class); + List userTaskInstances = userTasks.instances().findByIdentity(IdentityProviders.of("john")); + assertThat(userTaskInstances).isNotNull().hasSize(1); + UserTaskInstance ut_1 = userTaskInstances.get(0); + ut_1.transition(CLAIM, emptyMap(), IdentityProviders.of("john")); + assertThat(ut_1.getStatus()).isEqualTo(DefaultUserTaskLifeCycle.RESERVED); + ut_1.transition(RELEASE, emptyMap(), IdentityProviders.of("john")); + assertThat(ut_1.getStatus()).isEqualTo(DefaultUserTaskLifeCycle.ACTIVE); + assertThat(ut_1.getActualOwner()).isNull(); + ut_1.transition(CLAIM, emptyMap(), IdentityProviders.of("john")); + assertThat(ut_1.getActualOwner()).isEqualTo("john"); + ut_1.transition(COMPLETE, emptyMap(), IdentityProviders.of("john")); assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_ACTIVE); - workItems = processInstance.workItems(securityPolicy); - assertThat(workItems).hasSize(1); - wi = workItems.get(0); - assertThat(wi.getName()).isEqualTo("FirstTask"); - - assertThat(wi.getPhaseStatus()).isEqualTo(RESERVED.getName()); - assertThat(wi.getResults()).hasSize(1) - .containsEntry("test", "value"); - - transition = handler.newTransition(TRANSITION_RESERVED_RELEASE.id(), wi.getPhaseStatus(), emptyMap(), securityPolicy); - - processInstance.transitionWorkItem(workItems.get(0).getId(), transition); - - workItems = processInstance.workItems(securityPolicy); - assertThat(workItems).hasSize(1); - wi = workItems.get(0); - assertThat(wi.getName()).isEqualTo("FirstTask"); - assertThat(wi.getPhaseStatus()).isEqualTo(ACTIVATED.getName()); - assertThat(wi.getResults()).hasSize(0); - - transition = handler.newTransition(TRANSITION_ACTIVATED_CLAIM.id(), wi.getPhaseStatus(), Map.of("ACTUAL_OWNER", "john", "test", "value"), securityPolicy); - processInstance.transitionWorkItem(wi.getId(), transition); - assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_ACTIVE); - - workItems = processInstance.workItems(securityPolicy); - assertThat(workItems).hasSize(1); - wi = workItems.get(0); - assertThat(wi.getName()).isEqualTo("FirstTask"); - assertThat(wi.getPhaseStatus()).isEqualTo(RESERVED.getName()); - assertThat(wi.getResults()).hasSize(1) - .containsEntry("test", "value"); - - transition = handler.newTransition(TRANSITION_RESERVED_COMPLETE.id(), wi.getPhaseStatus(), emptyMap(), securityPolicy); - processInstance.transitionWorkItem(wi.getId(), transition); - assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_ACTIVE); - workItems = processInstance.workItems(securityPolicy); assertThat(workItems).hasSize(1); wi = workItems.get(0); assertThat(wi.getName()).isEqualTo("SecondTask"); - assertThat(wi.getPhaseStatus()).isEqualTo(RESERVED.getName()); + assertThat(wi.getPhaseStatus()).isEqualTo(UserTaskKogitoWorkItemHandler.activated.getName()); assertThat(wi.getResults()).isEmpty(); + userTaskInstances = userTasks.instances().findByIdentity(IdentityProviders.of("john")); + assertThat(userTaskInstances).isNotNull().hasSize(1); + UserTaskInstance ut_2 = userTaskInstances.get(0); + assertThat(ut_2.getStatus()).isEqualTo(DefaultUserTaskLifeCycle.RESERVED); + processInstance.abort(); assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_ABORTED); } @@ -738,7 +703,7 @@ public void testApprovalWithReadonlyVariableTags() throws Exception { Application app = generateCodeProcessesOnly("usertask/approval-with-readonly-variable-tags.bpmn2"); assertThat(app).isNotNull(); - + app.config().get(UserTaskConfig.class).userTaskEventListeners().listeners().add(new UserTaskKogitoWorkItemHandlerProcessListener(app.get(Processes.class))); Class resourceClazz = Class.forName("org.acme.travels.ApprovalsModel", true, testClassLoader()); assertThat(resourceClazz).isNotNull(); @@ -774,7 +739,7 @@ public void testApprovalWithInternalVariableTags() throws Exception { Application app = generateCodeProcessesOnly("usertask/approval-with-internal-variable-tags.bpmn2"); assertThat(app).isNotNull(); - + app.config().get(UserTaskConfig.class).userTaskEventListeners().listeners().add(new UserTaskKogitoWorkItemHandlerProcessListener(app.get(Processes.class))); Class resourceClazz = Class.forName("org.acme.travels.ApprovalsModel", true, testClassLoader()); assertThat(resourceClazz).isNotNull(); // internal variables are not exposed on the model @@ -800,7 +765,7 @@ public void testApprovalWithRequiredVariableTags() throws Exception { Application app = generateCodeProcessesOnly("usertask/approval-with-required-variable-tags.bpmn2"); assertThat(app).isNotNull(); - + app.config().get(UserTaskConfig.class).userTaskEventListeners().listeners().add(new UserTaskKogitoWorkItemHandlerProcessListener(app.get(Processes.class))); Process p = app.get(Processes.class).processById("approvals"); Model m = p.createModel(); @@ -818,7 +783,7 @@ public void testApprovalWithIOVariableTags() throws Exception { Application app = generateCodeProcessesOnly("usertask/approval-with-io-variable-tags.bpmn2"); assertThat(app).isNotNull(); - + app.config().get(UserTaskConfig.class).userTaskEventListeners().listeners().add(new UserTaskKogitoWorkItemHandlerProcessListener(app.get(Processes.class))); Class modelClazz = Class.forName("org.acme.travels.ApprovalsModel", true, testClassLoader()); assertThat(modelClazz).isNotNull(); assertThat(modelClazz.getDeclaredField("decision")).isNotNull(); @@ -857,7 +822,7 @@ public void testUserTaskWithIOexpressionProcess() throws Exception { Application app = generateCodeProcessesOnly("usertask/UserTaskWithIOexpression.bpmn2"); assertThat(app).isNotNull(); - + app.config().get(UserTaskConfig.class).userTaskEventListeners().listeners().add(new UserTaskKogitoWorkItemHandlerProcessListener(app.get(Processes.class))); Process p = app.get(Processes.class).processById("UserTask"); Model m = p.createModel(); @@ -888,7 +853,7 @@ public void testBasicUserTaskProcessWithBusinessKey() throws Exception { Application app = generateCodeProcessesOnly("usertask/UserTasksProcess.bpmn2"); assertThat(app).isNotNull(); - + app.config().get(UserTaskConfig.class).userTaskEventListeners().listeners().add(new UserTaskKogitoWorkItemHandlerProcessListener(app.get(Processes.class))); Process p = app.get(Processes.class).processById("UserTasksProcess"); Model m = p.createModel(); @@ -912,24 +877,23 @@ public void testBasicUserTaskProcessWithBusinessKey() throws Exception { List workItems = processInstance.workItems(securityPolicy); assertThat(workItems).hasSize(1); assertThat(workItems.get(0).getName()).isEqualTo("FirstTask"); - WorkItem wi = workItems.get(0); - KogitoWorkItemHandler handler = getWorkItemHandler(p, wi); - WorkItemTransition transition = handler.newTransition(TRANSITION_ACTIVATED_CLAIM.id(), wi.getPhaseStatus(), singletonMap("ACTUAL_OWNER", "john"), securityPolicy); - processInstance.transitionWorkItem(workItems.get(0).getId(), transition); - - workItems = processInstance.workItems(securityPolicy); - assertThat(workItems).hasSize(1); - wi = workItems.get(0); - assertThat(wi.getName()).isEqualTo("FirstTask"); - transition = handler.newTransition(TRANSITION_RESERVED_COMPLETE.id(), wi.getPhaseStatus(), singletonMap("ACTUAL_OWNER", "john"), securityPolicy); - processInstance.transitionWorkItem(workItems.get(0).getId(), transition); + UserTasks userTasks = app.get(UserTasks.class); + List userTaskInstances = userTasks.instances().findByIdentity(IdentityProviders.of("john")); + assertThat(userTaskInstances).isNotNull().hasSize(1); + UserTaskInstance ut_1 = userTaskInstances.get(0); + ut_1.transition(CLAIM, emptyMap(), IdentityProviders.of("john")); + ut_1.transition(COMPLETE, emptyMap(), IdentityProviders.of("john")); workItems = processInstance.workItems(securityPolicy); assertThat(workItems).hasSize(1); assertThat(workItems.get(0).getName()).isEqualTo("SecondTask"); - processInstance.completeWorkItem(workItems.get(0).getId(), null, securityPolicy); + userTaskInstances = userTasks.instances().findByIdentity(IdentityProviders.of("john")); + assertThat(userTaskInstances).isNotNull().hasSize(1); + UserTaskInstance ut_2 = userTaskInstances.get(0); + ut_2.transition(COMPLETE, emptyMap(), IdentityProviders.of("john")); + assertThat(processInstance.status()).isEqualTo(ProcessInstance.STATE_COMPLETED); } diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/ProcessCodegen.java b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/ProcessCodegen.java index 6f5ba05f7a5..ec800c185c5 100644 --- a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/ProcessCodegen.java +++ b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/ProcessCodegen.java @@ -42,7 +42,7 @@ import org.jbpm.compiler.canonical.ProcessMetaData; import org.jbpm.compiler.canonical.ProcessToExecModelGenerator; import org.jbpm.compiler.canonical.TriggerMetaData; -import org.jbpm.compiler.canonical.UserTaskModelMetaData; +import org.jbpm.compiler.canonical.WorkItemModelMetaData; import org.jbpm.compiler.xml.XmlProcessReader; import org.jbpm.compiler.xml.core.SemanticModules; import org.jbpm.process.core.impl.ProcessImpl; @@ -287,7 +287,7 @@ protected Collection internalGenerate() { Map processIdToInputModelGenerator = new HashMap<>(); Map processIdToOutputModelGenerator = new HashMap<>(); - Map> processIdToUserTaskModel = new HashMap<>(); + Map> processIdToWorkItemModel = new HashMap<>(); Map processIdToMetadata = new HashMap<>(); // first we generate all the data classes from variable declarations @@ -306,8 +306,8 @@ protected Collection internalGenerate() { // then we generate user task inputs and outputs if any for (WorkflowProcess workFlowProcess : processes.values()) { - UserTasksModelClassGenerator utcg = new UserTasksModelClassGenerator(workFlowProcess); - processIdToUserTaskModel.put(workFlowProcess.getId(), utcg.generate()); + WorkItemModelClassGenerator utcg = new WorkItemModelClassGenerator(workFlowProcess); + processIdToWorkItemModel.put(workFlowProcess.getId(), utcg.generate()); } // then we can instantiate the exec model generator @@ -361,7 +361,7 @@ protected Collection internalGenerate() { applicationCanonicalName()); processResourceGenerator - .withUserTasks(processIdToUserTaskModel.get(workFlowProcess.getId())) + .withWorkItems(processIdToWorkItemModel.get(workFlowProcess.getId())) .withSignals(metaData.getSignals()) .withTriggers(metaData.isStartable(), metaData.isDynamic(), metaData.getTriggers()); @@ -417,14 +417,14 @@ protected Collection internalGenerate() { mmd.generate()); } - for (List utmd : processIdToUserTaskModel.values()) { + for (List utmd : processIdToWorkItemModel.values()) { - for (UserTaskModelMetaData ut : utmd) { - storeFile(MODEL_TYPE, UserTasksModelClassGenerator.generatedFilePath(ut.getInputModelClassName()), ut.generateInput()); + for (WorkItemModelMetaData ut : utmd) { + storeFile(MODEL_TYPE, WorkItemModelClassGenerator.generatedFilePath(ut.getInputModelClassName()), ut.generateInput()); - storeFile(MODEL_TYPE, UserTasksModelClassGenerator.generatedFilePath(ut.getOutputModelClassName()), ut.generateOutput()); + storeFile(MODEL_TYPE, WorkItemModelClassGenerator.generatedFilePath(ut.getOutputModelClassName()), ut.generateOutput()); - storeFile(MODEL_TYPE, UserTasksModelClassGenerator.generatedFilePath(ut.getTaskModelClassName()), ut.generateModel()); + storeFile(MODEL_TYPE, WorkItemModelClassGenerator.generatedFilePath(ut.getTaskModelClassName()), ut.generateModel()); } } @@ -438,7 +438,7 @@ protected Collection internalGenerate() { for (ProcessResourceGenerator resourceGenerator : rgs) { storeFile(REST_TYPE, resourceGenerator.generatedFilePath(), resourceGenerator.generate()); - storeFile(MODEL_TYPE, UserTasksModelClassGenerator.generatedFilePath(resourceGenerator.getTaskModelFactoryClassName()), resourceGenerator.getTaskModelFactory()); + storeFile(MODEL_TYPE, WorkItemModelClassGenerator.generatedFilePath(resourceGenerator.getTaskModelFactoryClassName()), resourceGenerator.getTaskModelFactory()); } } diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/ProcessResourceGenerator.java b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/ProcessResourceGenerator.java index 90ab0277b06..8fd6d24d4cb 100644 --- a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/ProcessResourceGenerator.java +++ b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/ProcessResourceGenerator.java @@ -32,7 +32,7 @@ import org.drools.util.StringUtils; import org.jbpm.compiler.canonical.ProcessToExecModelGenerator; import org.jbpm.compiler.canonical.TriggerMetaData; -import org.jbpm.compiler.canonical.UserTaskModelMetaData; +import org.jbpm.compiler.canonical.WorkItemModelMetaData; import org.jbpm.ruleflow.core.Metadata; import org.jbpm.ruleflow.core.RuleFlowProcess; import org.jbpm.workflow.core.node.StartNode; @@ -75,7 +75,7 @@ public class ProcessResourceGenerator { private static final String REST_TEMPLATE_NAME = "RestResource"; private static final String REACTIVE_REST_TEMPLATE_NAME = "ReactiveRestResource"; - private static final String REST_USER_TASK_TEMPLATE_NAME = "RestResourceUserTask"; + private static final String REST_WORK_ITEM_TEMPLATE_NAME = "RestResourceWorkItem"; private static final String REST_SIGNAL_TEMPLATE_NAME = "RestResourceSignal"; private static final String SIGNAL_METHOD_PREFFIX = "signal_"; @@ -95,7 +95,7 @@ public class ProcessResourceGenerator { private boolean dynamic; private List triggers; - private List userTasks; + private List userTasks; private Map signals; private CompilationUnit taskModelFactoryUnit; private String taskModelFactoryClassName; @@ -117,7 +117,7 @@ public ProcessResourceGenerator( this.processClazzName = processfqcn; } - public ProcessResourceGenerator withUserTasks(List userTasks) { + public ProcessResourceGenerator withWorkItems(List userTasks) { this.userTasks = userTasks; return this; } @@ -281,7 +281,7 @@ public String generate() { if (userTasks != null && !userTasks.isEmpty()) { - CompilationUnit userTaskClazz = templateBuilder.build(context, REST_USER_TASK_TEMPLATE_NAME).compilationUnitOrThrow(); + CompilationUnit userTaskClazz = templateBuilder.build(context, REST_WORK_ITEM_TEMPLATE_NAME).compilationUnitOrThrow(); ClassOrInterfaceDeclaration userTaskTemplate = userTaskClazz .findFirst(ClassOrInterfaceDeclaration.class) @@ -292,7 +292,7 @@ public String generate() { .orElseThrow(IllegalStateException::new); SwitchStmt switchExpr = taskModelFactoryMethod.getBody().map(b -> b.findFirst(SwitchStmt.class).orElseThrow(IllegalStateException::new)).orElseThrow(IllegalStateException::new); - for (UserTaskModelMetaData userTask : userTasks) { + for (WorkItemModelMetaData userTask : userTasks) { String methodSuffix = sanitizeName(userTask.getName()) + "_" + index.getAndIncrement(); userTaskTemplate.findAll(MethodDeclaration.class).forEach(md -> { MethodDeclaration cloned = md.clone(); @@ -392,14 +392,14 @@ private void interpolateStrings(StringLiteralExpr vv) { vv.setString(interpolated); } - private void interpolateUserTaskStrings(StringLiteralExpr vv, UserTaskModelMetaData userTask) { + private void interpolateUserTaskStrings(StringLiteralExpr vv, WorkItemModelMetaData userTask) { String s = vv.getValue(); String interpolated = s.replace("$taskName$", sanitizeName(userTask.getName())); interpolated = interpolated.replace("$taskNodeName$", userTask.getNodeName()); vv.setString(interpolated); } - private void interpolateUserTaskNameExp(NameExpr name, UserTaskModelMetaData userTask) { + private void interpolateUserTaskNameExp(NameExpr name, WorkItemModelMetaData userTask) { name.setName(userTask.templateReplacement(name.getNameAsString())); } @@ -410,7 +410,7 @@ private void interpolateMethods(MethodDeclaration m) { m.setName(interpolated); } - private void interpolateUserTaskTypes(Type t, UserTaskModelMetaData userTask) { + private void interpolateUserTaskTypes(Type t, WorkItemModelMetaData userTask) { if (t.isArrayType()) { t = t.asArrayType().getElementType(); } @@ -421,11 +421,11 @@ private void interpolateUserTaskTypes(Type t, UserTaskModelMetaData userTask) { } } - private void interpolateUserTaskTypes(SimpleName returnType, UserTaskModelMetaData userTask) { + private void interpolateUserTaskTypes(SimpleName returnType, WorkItemModelMetaData userTask) { returnType.setIdentifier(userTask.templateReplacement(returnType.getIdentifier())); } - private void interpolateUserTaskTypeArguments(NodeList ta, UserTaskModelMetaData userTask) { + private void interpolateUserTaskTypeArguments(NodeList ta, WorkItemModelMetaData userTask) { ta.stream().forEach(t -> interpolateUserTaskTypes(t, userTask)); } diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/UserTasksModelClassGenerator.java b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/WorkItemModelClassGenerator.java similarity index 81% rename from kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/UserTasksModelClassGenerator.java rename to kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/WorkItemModelClassGenerator.java index a3850d60f1a..e66be467caf 100644 --- a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/UserTasksModelClassGenerator.java +++ b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/WorkItemModelClassGenerator.java @@ -21,21 +21,21 @@ import java.util.List; import org.jbpm.compiler.canonical.ProcessToExecModelGenerator; -import org.jbpm.compiler.canonical.UserTaskModelMetaData; +import org.jbpm.compiler.canonical.WorkItemModelMetaData; import org.kie.api.definition.process.WorkflowProcess; -public class UserTasksModelClassGenerator { +public class WorkItemModelClassGenerator { private final WorkflowProcess workFlowProcess; - private List modelMetaData; + private List modelMetaData; - public UserTasksModelClassGenerator(WorkflowProcess workFlowProcess) { + public WorkItemModelClassGenerator(WorkflowProcess workFlowProcess) { this.workFlowProcess = workFlowProcess; } - public List generate() { + public List generate() { // create model class for all variables - modelMetaData = ProcessToExecModelGenerator.INSTANCE.generateUserTaskModel(workFlowProcess); + modelMetaData = ProcessToExecModelGenerator.INSTANCE.generateWorkItemModel(workFlowProcess); return modelMetaData; } diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/usertask/UserTaskCodegen.java b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/usertask/UserTaskCodegen.java index 58bd4a54b61..ff07de053c1 100644 --- a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/usertask/UserTaskCodegen.java +++ b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/usertask/UserTaskCodegen.java @@ -18,10 +18,13 @@ */ package org.kie.kogito.codegen.usertask; +import java.io.File; import java.io.IOException; import java.io.Reader; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -97,6 +100,8 @@ public class UserTaskCodegen extends AbstractGenerator { TemplatedGenerator templateGenerator; private List descriptors; + private TemplatedGenerator producerTemplateGenerator; + private TemplatedGenerator restTemplateGenerator; public UserTaskCodegen(KogitoBuildContext context, List collectedResources) { super(context, "usertasks"); @@ -106,6 +111,16 @@ public UserTaskCodegen(KogitoBuildContext context, List collectedResources .withTemplateBasePath("/class-templates/usertask") .withTargetTypeName(SECTION_CLASS_NAME) .build(context, "UserTask"); + + producerTemplateGenerator = TemplatedGenerator.builder() + .withTemplateBasePath("/class-templates/usertask") + .withTargetTypeName(SECTION_CLASS_NAME) + .build(context, "UserTasksServiceProducer"); + + restTemplateGenerator = TemplatedGenerator.builder() + .withTemplateBasePath("/class-templates/usertask") + .withTargetTypeName(SECTION_CLASS_NAME) + .build(context, "RestResourceUserTask"); } @Override @@ -125,6 +140,42 @@ public boolean isEmpty() { @Override protected Collection internalGenerate() { + if (descriptors.isEmpty()) { + return Collections.emptyList(); + } + + List generatedFiles = new ArrayList<>(); + generatedFiles.addAll(generateUserTask()); + if (context().hasDI()) { + generatedFiles.add(generateProducer()); + } + + if (context().hasRESTForGenerator(this)) { + generatedFiles.add(generateRestEndpiont()); + } + + return generatedFiles; + } + + private GeneratedFile generateRestEndpiont() { + String packageName = context().getPackageName(); + CompilationUnit compilationUnit = producerTemplateGenerator.compilationUnitOrThrow("Not rest endpoints template found for user tasks"); + compilationUnit.setPackageDeclaration(packageName); + String className = compilationUnit.findFirst(ClassOrInterfaceDeclaration.class).get().getNameAsString(); + String urlBase = packageName.replaceAll("\\.", File.separator); + return new GeneratedFile(GeneratedFileType.REST, Path.of(urlBase).resolve(className + ".java"), compilationUnit.toString()); + } + + private GeneratedFile generateProducer() { + String packageName = context().getPackageName(); + CompilationUnit compilationUnit = restTemplateGenerator.compilationUnitOrThrow("No producer template found for user tasks"); + compilationUnit.setPackageDeclaration(packageName); + String className = compilationUnit.findFirst(ClassOrInterfaceDeclaration.class).get().getNameAsString(); + String urlBase = packageName.replaceAll("\\.", File.separator); + return new GeneratedFile(GeneratedFileType.INTERNAL_RESOURCE, Path.of(urlBase).resolve(className + ".java"), compilationUnit.toString()); + } + + private List generateUserTask() { List generatedFiles = new ArrayList<>(); for (Work info : descriptors) { CompilationUnit unit = templateGenerator.compilationUnit().get(); @@ -171,7 +222,6 @@ protected Collection internalGenerate() { generatedFiles.add(new GeneratedFile(GeneratedFileType.SOURCE, UserTaskCodegenHelper.path(info).resolve(className + ".java"), unit.toString())); } - return generatedFiles; } diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/usertask/UserTaskConfigGenerator.java b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/usertask/UserTaskConfigGenerator.java index 641f3b2d1a5..d6483f92b28 100644 --- a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/usertask/UserTaskConfigGenerator.java +++ b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/usertask/UserTaskConfigGenerator.java @@ -35,11 +35,9 @@ public class UserTaskConfigGenerator implements ConfigGenerator { - private List collectedResources; private TemplatedGenerator templateGenerator; public UserTaskConfigGenerator(KogitoBuildContext context, List collectedResources) { - this.collectedResources = collectedResources; templateGenerator = TemplatedGenerator.builder() .withTemplateBasePath("/class-templates/usertask") .build(context, "UserTaskConfig"); @@ -54,7 +52,6 @@ public String configClassName() { public GeneratedFile generate() { CompilationUnit unit = templateGenerator.compilationUnit().get(); String packageName = unit.getPackageDeclaration().get().getNameAsString(); - unit.getPackageDeclaration().get().setName(packageName); ClassOrInterfaceDeclaration clazzDeclaration = unit.findFirst(ClassOrInterfaceDeclaration.class).get(); clazzDeclaration.setName(configClassName()); diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/RestResourceUserTaskQuarkusTemplate.java b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/RestResourceUserTaskQuarkusTemplate.java deleted file mode 100644 index 36f93ac4624..00000000000 --- a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/RestResourceUserTaskQuarkusTemplate.java +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.myspace.demo; - -import java.util.List; - -import jakarta.ws.rs.NotFoundException; -import jakarta.ws.rs.core.Context; -import jakarta.ws.rs.core.Response; -import jakarta.ws.rs.core.UriInfo; - -import org.kie.kogito.auth.IdentityProviders; -import org.kie.kogito.auth.SecurityPolicy; -import org.kie.kogito.process.Process; -import org.kie.kogito.process.ProcessInstance; -import org.kie.kogito.process.ProcessInstanceReadMode; -import org.kie.kogito.process.WorkItem; -import org.kie.kogito.process.impl.Sig; -import org.kie.kogito.services.uow.UnitOfWorkExecutor; - -public class $Type$Resource { - - @POST - @Path("/{id}/$taskName$") - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) - public Response signal(@PathParam("id") final String id, - @QueryParam("user") final String user, - @QueryParam("group") final List groups, - @Context UriInfo uriInfo) { - return processService.signalWorkItem(process, id, "$taskName$", SecurityPolicy.of(user, groups)) - .map(task -> Response - .created(uriInfo.getAbsolutePathBuilder().path(task.getId()).build()) - .entity(task.getResults()) - .build()) - .orElseThrow(NotFoundException::new); - } - - @POST - @Path("/{id}/$taskName$/{taskId}") - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) - public $Type$Output completeTask(@PathParam("id") final String id, - @PathParam("taskId") final String taskId, - @QueryParam("phase") @DefaultValue("complete") final String phase, - @QueryParam("user") final String user, - @QueryParam("group") final List groups, - final $TaskOutput$ model) { - return processService.transitionWorkItem(process, id, taskId, phase, SecurityPolicy.of(user, groups), model) - .orElseThrow(NotFoundException::new); - } - - @PUT - @Path("/{id}/$taskName$/{taskId}") - @Consumes(MediaType.APPLICATION_JSON) - public $TaskOutput$ saveTask(@PathParam("id") final String id, - @PathParam("taskId") final String taskId, - @QueryParam("user") final String user, - @QueryParam("group") final List groups, - final $TaskOutput$ model) { - return processService.setWorkItemOutput(process, id, taskId, SecurityPolicy.of(user, groups), model, $TaskOutput$::fromMap) - .orElseThrow(NotFoundException::new); - } - - @POST - @Path("/{id}/$taskName$/{taskId}/phases/{phase}") - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) - public $Type$Output taskTransition( - @PathParam("id") final String id, - @PathParam("taskId") final String taskId, - @PathParam("phase") final String phase, - @QueryParam("user") final String user, - @QueryParam("group") final List groups, - final $TaskOutput$ model) { - return processService.transitionWorkItem(process, id, taskId, phase, SecurityPolicy.of(user, groups), model) - .orElseThrow(NotFoundException::new); - } - - @GET - @Path("/{id}/$taskName$/{taskId}") - @Produces(MediaType.APPLICATION_JSON) - public $TaskModel$ getWorkItem(@PathParam("id") String id, - @PathParam("taskId") String taskId, - @QueryParam("user") final String user, - @QueryParam("group") final List groups) { - return processService.getWorkItem(process, id, taskId, SecurityPolicy.of(user, groups), $TaskModel$::from) - .orElseThrow(NotFoundException::new); - } - - @DELETE - @Path("/{id}/$taskName$/{taskId}") - @Produces(MediaType.APPLICATION_JSON) - public $Type$Output abortTask(@PathParam("id") final String id, - @PathParam("taskId") final String taskId, - @QueryParam("phase") @DefaultValue("abort") final String phase, - @QueryParam("user") final String user, - @QueryParam("group") final List groups) { - return processService.transitionWorkItem(process, id, taskId, phase, SecurityPolicy.of(user, groups), null) - .orElseThrow(NotFoundException::new); - } - - @GET - @Path("$taskName$/schema") - @Produces(MediaType.APPLICATION_JSON) - public Map getSchema() { - return JsonSchemaUtil.load(this.getClass().getClassLoader(), process.id(), "$taskName$"); - } - - @GET - @Path("/{id}/$taskName$/{taskId}/schema") - @Produces(MediaType.APPLICATION_JSON) - public Map getSchemaAndPhases(@PathParam("id") final String id, - @PathParam("taskId") final String taskId, - @QueryParam("user") final String user, - @QueryParam("group") final List groups) { - return processService.getWorkItemSchemaAndPhases(process, id, taskId, "$taskName$", SecurityPolicy.of(user, groups)); - } - - @POST - @Path("/{id}/$taskName$/{taskId}/comments") - @Consumes(MediaType.TEXT_PLAIN) - @Produces(MediaType.APPLICATION_JSON) - public Response addComment(@PathParam("id") final String id, - @PathParam("taskId") final String taskId, - @QueryParam("user") final String user, - @QueryParam("group") final List groups, - String commentInfo, - @Context UriInfo uriInfo) { - return processService.addComment(process, id, taskId, SecurityPolicy.of(user, groups), commentInfo) - .map(comment -> Response.created(uriInfo.getAbsolutePathBuilder().path(comment.getId().toString()).build()) - .entity(comment).build()) - .orElseThrow(NotFoundException::new); - } - - @PUT - @Path("/{id}/$taskName$/{taskId}/comments/{commentId}") - @Consumes(MediaType.TEXT_PLAIN) - @Produces(MediaType.APPLICATION_JSON) - public Comment updateComment(@PathParam("id") final String id, - @PathParam("taskId") final String taskId, - @PathParam("commentId") final String commentId, - @QueryParam("user") final String user, - @QueryParam("group") final List groups, - String comment) { - return processService.updateComment(process, id, taskId, commentId, SecurityPolicy.of(user, groups), comment) - .orElseThrow(NotFoundException::new); - } - - @DELETE - @Path("/{id}/$taskName$/{taskId}/comments/{commentId}") - public Response deleteComment(@PathParam("id") final String id, - @PathParam("taskId") final String taskId, - @PathParam("commentId") final String commentId, - @QueryParam("user") final String user, - @QueryParam("group") final List groups) { - return processService.deleteComment(process, id, taskId, commentId, SecurityPolicy.of(user, groups)) - .map(removed -> (removed ? Response.ok() : Response.status(Status.NOT_FOUND)).build()) - .orElseThrow(NotFoundException::new); - } - - @POST - @Path("/{id}/$taskName$/{taskId}/attachments") - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) - public Response addAttachment(@PathParam("id") final String id, - @PathParam("taskId") final String taskId, - @QueryParam("user") final String user, - @QueryParam("group") final List groups, - AttachmentInfo attachmentInfo, - @Context UriInfo uriInfo) { - return processService.addAttachment(process, id, taskId, SecurityPolicy.of(user, groups), attachmentInfo) - .map(attachment -> Response - .created(uriInfo.getAbsolutePathBuilder().path(attachment.getId().toString()).build()) - .entity(attachment).build()) - .orElseThrow(NotFoundException::new); - } - - @PUT - @Path("/{id}/$taskName$/{taskId}/attachments/{attachmentId}") - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) - public Attachment updateAttachment(@PathParam("id") final String id, - @PathParam("taskId") final String taskId, - @PathParam("attachmentId") final String attachmentId, - @QueryParam("user") final String user, - @QueryParam("group") final List groups, - AttachmentInfo attachment) { - return processService.updateAttachment(process, id, taskId, attachmentId, SecurityPolicy.of(user, groups), attachment) - .orElseThrow(NotFoundException::new); - } - - @DELETE - @Path("/{id}/$taskName$/{taskId}/attachments/{attachmentId}") - public Response deleteAttachment(@PathParam("id") final String id, - @PathParam("taskId") final String taskId, - @PathParam("attachmentId") final String attachmentId, - @QueryParam("user") final String user, - @QueryParam("group") final List groups) { - return processService.deleteAttachment(process, id, taskId, attachmentId, SecurityPolicy.of(user, groups)) - .map(removed -> (removed ? Response.ok() : Response.status(Status.NOT_FOUND)).build()) - .orElseThrow(NotFoundException::new); - } - - @GET - @Path("/{id}/$taskName$/{taskId}/attachments/{attachmentId}") - @Produces(MediaType.APPLICATION_JSON) - public Attachment getAttachment(@PathParam("id") final String id, - @PathParam("taskId") final String taskId, - @PathParam("attachmentId") final String attachmentId, - @QueryParam("user") final String user, - @QueryParam("group") final List groups) { - return processService.getAttachment(process, id, taskId, attachmentId, SecurityPolicy.of(user, groups)) - .orElseThrow(() -> new NotFoundException("Attachment " + attachmentId + " not found")); - } - - @GET - @Path("/{id}/$taskName$/{taskId}/attachments") - @Produces(MediaType.APPLICATION_JSON) - public Collection getAttachments(@PathParam("id") final String id, - @PathParam("taskId") final String taskId, - @QueryParam("user") final String user, - @QueryParam("group") final List groups) { - return processService.getAttachments(process, id, taskId, SecurityPolicy.of(user, groups)) - .orElseThrow(NotFoundException::new); - } - - @GET - @Path("/{id}/$taskName$/{taskId}/comments/{commentId}") - @Produces(MediaType.APPLICATION_JSON) - public Comment getComment(@PathParam("id") final String id, - @PathParam("taskId") final String taskId, - @PathParam("commentId") final String commentId, - @QueryParam("user") final String user, - @QueryParam("group") final List groups) { - return processService.getComment(process, id, taskId, commentId, SecurityPolicy.of(user, groups)) - .orElseThrow(() -> new NotFoundException("Comment " + commentId + " not found")); - } - - @GET - @Path("/{id}/$taskName$/{taskId}/comments") - @Produces(MediaType.APPLICATION_JSON) - public Collection getComments(@PathParam("id") final String id, - @PathParam("taskId") final String taskId, - @QueryParam("user") final String user, - @QueryParam("group") final List groups) { - return processService.getComments(process, id, taskId, SecurityPolicy.of(user, groups)) - .orElseThrow(NotFoundException::new); - } -} diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/RestResourceUserTaskSpringTemplate.java b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/RestResourceUserTaskSpringTemplate.java deleted file mode 100644 index a18e56c3379..00000000000 --- a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/RestResourceUserTaskSpringTemplate.java +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 com.myspace.demo; - -import java.util.List; -import java.util.Map; - -import org.jbpm.util.JsonSchemaUtil; -import org.kie.kogito.auth.IdentityProviders; -import org.kie.kogito.auth.SecurityPolicy; -import org.kie.kogito.process.ProcessInstance; -import org.kie.kogito.process.WorkItem; -import org.kie.kogito.process.impl.Sig; -import org.kie.kogito.process.workitem.Comment; -import org.kie.kogito.process.workitem.TaskMetaInfo; -import org.kie.kogito.services.uow.UnitOfWorkExecutor; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PatchMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.util.UriComponentsBuilder; - -public class $Type$Resource { - - @PostMapping(value = "/{id}/$taskName$", produces = MediaType.APPLICATION_JSON_VALUE, - consumes = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity signal(@PathVariable("id") final String id, - @RequestParam("user") final String user, - @RequestParam("group") final List groups, - final UriComponentsBuilder uriComponentsBuilder) { - - return processService.signalWorkItem(process, id, "$taskName$", SecurityPolicy.of(user, groups)) - .map(task -> ResponseEntity - .created(uriComponentsBuilder - .path("/$name$/{id}/$taskName$/{taskId}") - .buildAndExpand(id, task.getId()).toUri()) - .body(task.getResults())) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); - } - - @PostMapping(value = "/{id}/$taskName$/{taskId}/phases/{phase}", produces = MediaType.APPLICATION_JSON_VALUE, - consumes = MediaType.APPLICATION_JSON_VALUE) - public $Type$Output completeTask(@PathVariable("id") final String id, - @PathVariable("taskId") final String taskId, - @PathVariable("phase") final String phase, - @RequestParam("user") final String user, - @RequestParam("group") final List groups, - @RequestBody(required = false) final $TaskOutput$ model) { - return processService.transitionWorkItem(process, id, taskId, phase, SecurityPolicy.of(user, groups), model) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); - } - - @PutMapping(value = "/{id}/$taskName$/{taskId}", consumes = MediaType.APPLICATION_JSON_VALUE) - public $TaskOutput$ saveTask(@PathVariable("id") final String id, - @PathVariable("taskId") final String taskId, - @RequestParam(value = "user", required = false) final String user, - @RequestParam(value = "group", required = false) final List groups, - @RequestBody(required = false) final $TaskOutput$ model) { - return processService.setWorkItemOutput(process, id, taskId, SecurityPolicy.of(user, groups), model, $TaskOutput$::fromMap) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); - } - - @PostMapping(value = "/{id}/$taskName$/{taskId}", produces = MediaType.APPLICATION_JSON_VALUE, - consumes = MediaType.APPLICATION_JSON_VALUE) - public $Type$Output taskTransition(@PathVariable("id") final String id, - @PathVariable("taskId") final String taskId, - @RequestParam(value = "phase", required = false, - defaultValue = "complete") final String phase, - @RequestParam(value = "user", - required = false) final String user, - @RequestParam(value = "group", - required = false) final List groups, - @RequestBody(required = false) final $TaskOutput$ model) { - return processService.transitionWorkItem(process, id, taskId, phase, SecurityPolicy.of(user, groups), model) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); - } - - @GetMapping(value = "/{id}/$taskName$/{taskId}", produces = MediaType.APPLICATION_JSON_VALUE) - public $TaskModel$ getTask(@PathVariable("id") String id, - @PathVariable("taskId") String taskId, - @RequestParam(value = "user", required = false) final String user, - @RequestParam(value = "group", - required = false) final List groups) { - return processService.getWorkItem(process, id, taskId, SecurityPolicy.of(user, groups), $TaskModel$::from) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); - } - - @DeleteMapping(value = "/{id}/$taskName$/{taskId}", produces = MediaType.APPLICATION_JSON_VALUE) - public $Type$Output abortTask(@PathVariable("id") final String id, - @PathVariable("taskId") final String taskId, - @RequestParam(value = "phase", required = false, - defaultValue = "abort") final String phase, - @RequestParam(value = "user", required = false) final String user, - @RequestParam(value = "group", - required = false) final List groups) { - return processService.transitionWorkItem(process, id, taskId, phase, SecurityPolicy.of(user, groups), null) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); - } - - @GetMapping(value = "$taskName$/schema", produces = MediaType.APPLICATION_JSON_VALUE) - public Map getSchema() { - return JsonSchemaUtil.load(this.getClass().getClassLoader(), process.id(), "$taskName$"); - } - - @GetMapping(value = "/{id}/$taskName$/{taskId}/schema", produces = MediaType.APPLICATION_JSON_VALUE) - public Map getSchemaAndPhases(@PathVariable("id") final String id, - @PathVariable("taskId") final String taskId, - @RequestParam(value = "user", required = false) final String user, - @RequestParam(value = "group", - required = false) final List groups) { - return processService.getWorkItemSchemaAndPhases(process, id, taskId, "$taskName$", SecurityPolicy.of(user, groups)); - } - - @PostMapping(value = "/{id}/$taskName$/{taskId}/comments", produces = MediaType.APPLICATION_JSON_VALUE, - consumes = MediaType.TEXT_PLAIN_VALUE) - public ResponseEntity addComment(@PathVariable("id") final String id, - @PathVariable("taskId") final String taskId, - @RequestParam(value = "user", required = false) final String user, - @RequestParam(value = "group", - required = false) final List groups, - @RequestBody String commentInfo, - UriComponentsBuilder uriComponentsBuilder) { - return processService.addComment(process, id, taskId, SecurityPolicy.of(user, groups), commentInfo) - .map(comment -> ResponseEntity - .created(uriComponentsBuilder.path("/$name$/{id}/$taskName$/{taskId}/comments/{commentId}") - .buildAndExpand(id, taskId, comment.getId().toString()).toUri()) - .body(comment)) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); - } - - @PutMapping(value = "/{id}/$taskName$/{taskId}/comments/{commentId}", produces = MediaType.APPLICATION_JSON_VALUE, - consumes = MediaType.TEXT_PLAIN_VALUE) - public Comment updateComment(@PathVariable("id") final String id, - @PathVariable("taskId") final String taskId, - @PathVariable("commentId") final String commentId, - @RequestParam(value = "user", required = false) final String user, - @RequestParam(value = "group", - required = false) final List groups, - @RequestBody String comment) { - return processService.updateComment(process, id, taskId, commentId, SecurityPolicy.of(user, groups), comment) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); - } - - @DeleteMapping(value = "/{id}/$taskName$/{taskId}/comments/{commentId}") - public ResponseEntity deleteComment(@PathVariable("id") final String id, - @PathVariable("taskId") final String taskId, - @PathVariable("commentId") final String commentId, - @RequestParam(value = "user", required = false) final String user, - @RequestParam(value = "group", required = false) final List groups) { - return processService.deleteComment(process, id, taskId, commentId, SecurityPolicy.of(user, groups)) - .map(removed -> (removed ? ResponseEntity.ok().build() : ResponseEntity.notFound().build())) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); - } - - @PostMapping(value = "/{id}/$taskName$/{taskId}/attachments", produces = MediaType.APPLICATION_JSON_VALUE, - consumes = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity addAttachment(@PathVariable("id") final String id, - @PathVariable("taskId") final String taskId, - @RequestParam(value = "user", required = false) final String user, - @RequestParam(value = "group", - required = false) final List groups, - @RequestBody AttachmentInfo attachmentInfo, - UriComponentsBuilder uriComponentsBuilder) { - return processService.addAttachment(process, id, taskId, SecurityPolicy.of(user, groups), attachmentInfo) - .map(attachment -> ResponseEntity - .created(uriComponentsBuilder.path( - "/$name$/{id}/$taskName$/{taskId}/attachments/{attachmentId}") - .buildAndExpand(id, - taskId, attachment.getId()) - .toUri()) - .body(attachment)) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); - } - - @PutMapping(value = "/{id}/$taskName$/{taskId}/attachments/{attachmentId}", - produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE) - public Attachment updateAttachment(@PathVariable("id") final String id, - @PathVariable("taskId") final String taskId, - @PathVariable("attachmentId") final String attachmentId, - @RequestParam(value = "user", - required = false) final String user, - @RequestParam(value = "group", - required = false) final List groups, - @RequestBody AttachmentInfo attachment) { - return processService.updateAttachment(process, id, taskId, attachmentId, SecurityPolicy.of(user, groups), attachment) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); - } - - @DeleteMapping(value = "/{id}/$taskName$/{taskId}/attachments/{attachmentId}") - public ResponseEntity deleteAttachment(@PathVariable("id") final String id, - @PathVariable("taskId") final String taskId, - @PathVariable("attachmentId") final String attachmentId, - @RequestParam(value = "user", required = false) final String user, - @RequestParam(value = "group", required = false) final List groups) { - - return processService.deleteAttachment(process, id, taskId, attachmentId, SecurityPolicy.of(user, groups)) - .map(removed -> (removed ? ResponseEntity.ok() : ResponseEntity.notFound()).build()) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); - } - - @GetMapping(value = "/{id}/$taskName$/{taskId}/attachments/{attachmentId}", produces = MediaType.APPLICATION_JSON_VALUE) - public Attachment getAttachment(@PathVariable("id") final String id, - @PathVariable("taskId") final String taskId, - @PathVariable("attachmentId") final String attachmentId, - @RequestParam(value = "user", required = false) final String user, - @RequestParam(value = "group", required = false) final List groups) { - return processService.getAttachment(process, id, taskId, attachmentId, SecurityPolicy.of(user, groups)) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Attachment " + attachmentId + " not found")); - } - - @GetMapping(value = "/{id}/$taskName$/{taskId}/attachments", produces = MediaType.APPLICATION_JSON_VALUE) - public Collection getAttachments(@PathVariable("id") final String id, - @PathVariable("taskId") final String taskId, - @RequestParam(value = "user") final String user, - @RequestParam(value = "group") final List groups) { - return processService.getAttachments(process, id, taskId, SecurityPolicy.of(user, groups)) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); - } - - @GetMapping(value = "/{id}/$taskName$/{taskId}/comments/{commentId}", produces = MediaType.APPLICATION_JSON_VALUE) - public Comment getComment(@PathVariable("id") final String id, - @PathVariable("taskId") final String taskId, - @PathVariable("commentId") final String commentId, - @RequestParam(value = "user", required = false) final String user, - @RequestParam(value = "group", required = false) final List groups) { - return processService.getComment(process, id, taskId, commentId, SecurityPolicy.of(user, groups)) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Comment " + commentId + " not found")); - } - - @GetMapping(value = "/{id}/$taskName$/{taskId}/comments", produces = MediaType.APPLICATION_JSON_VALUE) - public Collection getComments(@PathVariable("id") final String id, - @PathVariable("taskId") final String taskId, - @RequestParam(value = "user", required = false) final String user, - @RequestParam(value = "group", required = false) final List groups) { - return processService.getComments(process, id, taskId, SecurityPolicy.of(user, groups)) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); - } -} \ No newline at end of file diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/RestResourceWorkItemQuarkusTemplate.java b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/RestResourceWorkItemQuarkusTemplate.java new file mode 100644 index 00000000000..d2f37a7432e --- /dev/null +++ b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/RestResourceWorkItemQuarkusTemplate.java @@ -0,0 +1,139 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 com.myspace.demo; + +import java.util.List; + +import jakarta.ws.rs.NotFoundException; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriInfo; + +import org.kie.kogito.auth.IdentityProviders; +import org.kie.kogito.auth.SecurityPolicy; +import org.kie.kogito.process.Process; +import org.kie.kogito.process.ProcessInstance; +import org.kie.kogito.process.ProcessInstanceReadMode; +import org.kie.kogito.process.WorkItem; +import org.kie.kogito.process.impl.Sig; +import org.kie.kogito.services.uow.UnitOfWorkExecutor; +import org.kie.kogito.usertask.UserTaskService; + +import jakarta.inject.Inject; + +public class $Type$Resource { + + @POST + @Path("/{id}/$taskName$") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response signal(@PathParam("id") final String id, + @QueryParam("user") final String user, + @QueryParam("group") final List groups, + @Context UriInfo uriInfo) { + return processService.signalWorkItem(process, id, "$taskName$", SecurityPolicy.of(user, groups)) + .map(task -> Response + .created(uriInfo.getAbsolutePathBuilder().path(task.getId()).build()) + .entity(task.getResults()) + .build()) + .orElseThrow(NotFoundException::new); + } + + @POST + @Path("/{id}/$taskName$/{taskId}") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public $Type$Output completeTask(@PathParam("id") final String id, + @PathParam("taskId") final String taskId, + @QueryParam("phase") @DefaultValue("complete") final String phase, + @QueryParam("user") final String user, + @QueryParam("group") final List groups, + final $TaskOutput$ model) { + return processService.transitionWorkItem(process, id, taskId, phase, SecurityPolicy.of(user, groups), model) + .orElseThrow(NotFoundException::new); + } + + @PUT + @Path("/{id}/$taskName$/{taskId}") + @Consumes(MediaType.APPLICATION_JSON) + public $TaskOutput$ saveTask(@PathParam("id") final String id, + @PathParam("taskId") final String taskId, + @QueryParam("user") final String user, + @QueryParam("group") final List groups, + final $TaskOutput$ model) { + return processService.setWorkItemOutput(process, id, taskId, SecurityPolicy.of(user, groups), model, $TaskOutput$::fromMap) + .orElseThrow(NotFoundException::new); + } + + @POST + @Path("/{id}/$taskName$/{taskId}/phases/{phase}") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public $Type$Output taskTransition( + @PathParam("id") final String id, + @PathParam("taskId") final String taskId, + @PathParam("phase") final String phase, + @QueryParam("user") final String user, + @QueryParam("group") final List groups, + final $TaskOutput$ model) { + return processService.transitionWorkItem(process, id, taskId, phase, SecurityPolicy.of(user, groups), model) + .orElseThrow(NotFoundException::new); + } + + @GET + @Path("/{id}/$taskName$/{taskId}") + @Produces(MediaType.APPLICATION_JSON) + public $TaskModel$ getWorkItem(@PathParam("id") String id, + @PathParam("taskId") String taskId, + @QueryParam("user") final String user, + @QueryParam("group") final List groups) { + return processService.getWorkItem(process, id, taskId, SecurityPolicy.of(user, groups), $TaskModel$::from) + .orElseThrow(NotFoundException::new); + } + + @DELETE + @Path("/{id}/$taskName$/{taskId}") + @Produces(MediaType.APPLICATION_JSON) + public $Type$Output abortTask(@PathParam("id") final String id, + @PathParam("taskId") final String taskId, + @QueryParam("phase") @DefaultValue("abort") final String phase, + @QueryParam("user") final String user, + @QueryParam("group") final List groups) { + return processService.transitionWorkItem(process, id, taskId, phase, SecurityPolicy.of(user, groups), null) + .orElseThrow(NotFoundException::new); + } + + @GET + @Path("$taskName$/schema") + @Produces(MediaType.APPLICATION_JSON) + public Map getSchema() { + return JsonSchemaUtil.load(this.getClass().getClassLoader(), process.id(), "$taskName$"); + } + + @GET + @Path("/{id}/$taskName$/{taskId}/schema") + @Produces(MediaType.APPLICATION_JSON) + public Map getSchemaAndPhases(@PathParam("id") final String id, + @PathParam("taskId") final String taskId, + @QueryParam("user") final String user, + @QueryParam("group") final List groups) { + return processService.getWorkItemSchemaAndPhases(process, id, taskId, "$taskName$", SecurityPolicy.of(user, groups)); + } + +} diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/RestResourceWorkItemSpringTemplate.java b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/RestResourceWorkItemSpringTemplate.java new file mode 100644 index 00000000000..0cdf675149f --- /dev/null +++ b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/RestResourceWorkItemSpringTemplate.java @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 com.myspace.demo; + +import java.util.List; +import java.util.Map; + +import org.jbpm.util.JsonSchemaUtil; +import org.kie.kogito.auth.IdentityProviders; +import org.kie.kogito.auth.SecurityPolicy; +import org.kie.kogito.process.ProcessInstance; +import org.kie.kogito.process.WorkItem; +import org.kie.kogito.process.impl.Sig; +import org.kie.kogito.process.workitem.Comment; +import org.kie.kogito.process.workitem.TaskMetaInfo; +import org.kie.kogito.services.uow.UnitOfWorkExecutor; +import org.kie.kogito.usertask.UserTaskService; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.util.UriComponentsBuilder; +import org.springframework.beans.factory.annotation.Autowired; + +public class $Type$Resource { + + @PostMapping(value = "/{id}/$taskName$", produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity signal(@PathVariable("id") final String id, + @RequestParam("user") final String user, + @RequestParam("group") final List groups, + final UriComponentsBuilder uriComponentsBuilder) { + + return processService.signalWorkItem(process, id, "$taskName$", SecurityPolicy.of(user, groups)) + .map(task -> ResponseEntity + .created(uriComponentsBuilder + .path("/$name$/{id}/$taskName$/{taskId}") + .buildAndExpand(id, task.getId()).toUri()) + .body(task.getResults())) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); + } + + @PostMapping(value = "/{id}/$taskName$/{taskId}/phases/{phase}", produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE) + public $Type$Output completeTask(@PathVariable("id") final String id, + @PathVariable("taskId") final String taskId, + @PathVariable("phase") final String phase, + @RequestParam("user") final String user, + @RequestParam("group") final List groups, + @RequestBody(required = false) final $TaskOutput$ model) { + return processService.transitionWorkItem(process, id, taskId, phase, SecurityPolicy.of(user, groups), model) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); + } + + @PutMapping(value = "/{id}/$taskName$/{taskId}", consumes = MediaType.APPLICATION_JSON_VALUE) + public $TaskOutput$ saveTask(@PathVariable("id") final String id, + @PathVariable("taskId") final String taskId, + @RequestParam(value = "user", required = false) final String user, + @RequestParam(value = "group", required = false) final List groups, + @RequestBody(required = false) final $TaskOutput$ model) { + return processService.setWorkItemOutput(process, id, taskId, SecurityPolicy.of(user, groups), model, $TaskOutput$::fromMap) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); + } + + @PostMapping(value = "/{id}/$taskName$/{taskId}", produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE) + public $Type$Output taskTransition(@PathVariable("id") final String id, + @PathVariable("taskId") final String taskId, + @RequestParam(value = "phase", required = false, + defaultValue = "complete") final String phase, + @RequestParam(value = "user", + required = false) final String user, + @RequestParam(value = "group", + required = false) final List groups, + @RequestBody(required = false) final $TaskOutput$ model) { + return processService.transitionWorkItem(process, id, taskId, phase, SecurityPolicy.of(user, groups), model) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); + } + + @GetMapping(value = "/{id}/$taskName$/{taskId}", produces = MediaType.APPLICATION_JSON_VALUE) + public $TaskModel$ getTask(@PathVariable("id") String id, + @PathVariable("taskId") String taskId, + @RequestParam(value = "user", required = false) final String user, + @RequestParam(value = "group", + required = false) final List groups) { + return processService.getWorkItem(process, id, taskId, SecurityPolicy.of(user, groups), $TaskModel$::from) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); + } + + @DeleteMapping(value = "/{id}/$taskName$/{taskId}", produces = MediaType.APPLICATION_JSON_VALUE) + public $Type$Output abortTask(@PathVariable("id") final String id, + @PathVariable("taskId") final String taskId, + @RequestParam(value = "phase", required = false, + defaultValue = "abort") final String phase, + @RequestParam(value = "user", required = false) final String user, + @RequestParam(value = "group", + required = false) final List groups) { + return processService.transitionWorkItem(process, id, taskId, phase, SecurityPolicy.of(user, groups), null) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); + } + + @GetMapping(value = "$taskName$/schema", produces = MediaType.APPLICATION_JSON_VALUE) + public Map getSchema() { + return JsonSchemaUtil.load(this.getClass().getClassLoader(), process.id(), "$taskName$"); + } + + @GetMapping(value = "/{id}/$taskName$/{taskId}/schema", produces = MediaType.APPLICATION_JSON_VALUE) + public Map getSchemaAndPhases(@PathVariable("id") final String id, + @PathVariable("taskId") final String taskId, + @RequestParam(value = "user", required = false) final String user, + @RequestParam(value = "group", + required = false) final List groups) { + return processService.getWorkItemSchemaAndPhases(process, id, taskId, "$taskName$", SecurityPolicy.of(user, groups)); + } + +} \ No newline at end of file diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/RestResourceUserTaskQuarkusTemplate.java b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/RestResourceUserTaskQuarkusTemplate.java new file mode 100644 index 00000000000..0aa79a4ec8b --- /dev/null +++ b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/RestResourceUserTaskQuarkusTemplate.java @@ -0,0 +1,234 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 com.myspace.demo; + +import java.util.List; + +import jakarta.ws.rs.NotFoundException; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriInfo; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.Consumes; + +import org.kie.kogito.auth.IdentityProviders; +import org.kie.kogito.auth.SecurityPolicy; +import org.kie.kogito.process.Process; +import org.kie.kogito.process.ProcessInstance; +import org.kie.kogito.process.ProcessInstanceReadMode; +import org.kie.kogito.process.WorkItem; +import org.kie.kogito.process.impl.Sig; +import org.kie.kogito.services.uow.UnitOfWorkExecutor; +import org.kie.kogito.usertask.UserTaskService; +import org.kie.kogito.usertask.view.UserTaskView; +import org.kie.kogito.usertask.view.UserTaskTransitionView; + +import jakarta.inject.Inject; + +@Path("usertasks") +public class UserTasksResource { + + @Inject + UserTaskService userTaskService; + + @GET + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public List list(@QueryParam("user") String user, @QueryParam("group") List groups) { + return userTaskService.list(IdentityProviders.of(user, groups)); + } + + @POST + @Path("/{taskId}/transition") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public UserTaskView transition( + @PathParam("taskId") String taskId, + @QueryParam("transitionId") String transitionId, + @QueryParam("user") String user, + @QueryParam("group") List groups, Map data) { + return userTaskService.transition(taskId, transitionId, data, IdentityProviders.of(user, groups)).orElseThrow(NotFoundException::new); + } + + @Get + @Path("/{taskId}/transition") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public List transition( + @PathParam("taskId") String taskId, + @QueryParam("user") String user, + @QueryParam("group") List groups) { + return userTaskService.allowedTransitions(taskId, IdentityProviders.of(user, groups)).orElseThrow(NotFoundException::new); + } + + @PUT + @Path("/{taskId}/outputs") + @Consumes(MediaType.APPLICATION_JSON) + public UserTaskView setOutput( + @PathParam("taskId") String taskId, + @QueryParam("user") String user, + @QueryParam("group") List groups, + Map data) { + return userTaskService.setOutputs(taskId, data, IdentityProviders.of(user, groups)).orElseThrow(NotFoundException::new); + } + + @PUT + @Path("/{taskId}/inputs") + @Consumes(MediaType.APPLICATION_JSON) + public UserTaskView setOutput(@PathParam("id") String id, + @PathParam("taskId") String taskId, + @QueryParam("user") String user, + @QueryParam("group") List groups, + Map data) { + return userTaskService.setInputs(taskId, data, IdentityProviders.of(user, groups)).orElseThrow(NotFoundException::new); + } + + @GET + @Path("/{taskId}/comments") + @Produces(MediaType.APPLICATION_JSON) + public Collection getComments( + @PathParam("taskId") String taskId, + @QueryParam("user") String user, + @QueryParam("group") List groups) { + return userTaskService.getComments(taskId, IdentityProviders.of(user, groups)).orElseThrow(NotFoundException::new); + } + + @POST + @Path("/{taskId}/comments") + @Consumes(MediaType.TEXT_PLAIN) + @Produces(MediaType.APPLICATION_JSON) + public Response addComment( + @PathParam("taskId") String taskId, + @QueryParam("user") String user, + @QueryParam("group") List groups, + String commentInfo, + @Context UriInfo uriInfo) { + return userTaskService.addComment(taskId, IdentityProviders.of(user, groups), commentInfo) + .map(comment -> Response.created(uriInfo.getAbsolutePathBuilder().path(comment.getId().toString()).build()) + .entity(comment).build()) + .orElseThrow(NotFoundException::new); + } + + @GET + @Path("/{taskId}/comments/{commentId}") + @Produces(MediaType.APPLICATION_JSON) + public Comment getComment( + @PathParam("taskId") String taskId, + @PathParam("commentId") String commentId, + @QueryParam("user") String user, + @QueryParam("group") List groups) { + return userTaskService.getComment(taskId, commentId, IdentityProviders.of(user, groups)) + .orElseThrow(() -> new NotFoundException("Comment " + commentId + " not found")); + } + + @PUT + @Path("/{taskId}/comments/{commentId}") + @Consumes(MediaType.TEXT_PLAIN) + @Produces(MediaType.APPLICATION_JSON) + public Comment updateComment( + @PathParam("taskId") String taskId, + @PathParam("commentId") String commentId, + @QueryParam("user") String user, + @QueryParam("group") List groups, + String comment) { + return userTaskService.updateComment(taskId, commentId, IdentityProviders.of(user, groups), comment) + .orElseThrow(NotFoundException::new); + } + + @DELETE + @Path("/{taskId}/comments/{commentId}") + public Response deleteComment( + @PathParam("taskId") String taskId, + @PathParam("commentId") String commentId, + @QueryParam("user") String user, + @QueryParam("group") List groups) { + return userTaskService.deleteComment(taskId, commentId, IdentityProviders.of(user, groups)) + .map(removed -> (removed ? Response.ok() : Response.status(Status.NOT_FOUND)).build()) + .orElseThrow(NotFoundException::new); + } + + @GET + @Path("/{taskId}/attachments") + @Produces(MediaType.APPLICATION_JSON) + public Collection getAttachments( + @PathParam("taskId") String taskId, + @QueryParam("user") String user, + @QueryParam("group") List groups) { + return userTaskService.getAttachments(taskId, IdentityProviders.of(user, groups)) + .orElseThrow(NotFoundException::new); + } + + @POST + @Path("/{taskId}/attachments") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response addAttachment( + @PathParam("taskId") String taskId, + @QueryParam("user") String user, + @QueryParam("group") List groups, + AttachmentInfo attachmentInfo, + @Context UriInfo uriInfo) { + return userTaskService.addAttachment(taskId, IdentityProviders.of(user, groups), attachmentInfo) + .map(attachment -> Response + .created(uriInfo.getAbsolutePathBuilder().path(attachment.getId().toString()).build()) + .entity(attachment).build()) + .orElseThrow(NotFoundException::new); + } + + @PUT + @Path("/{taskId}/attachments/{attachmentId}") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Attachment updateAttachment( + @PathParam("taskId") String taskId, + @PathParam("attachmentId") String attachmentId, + @QueryParam("user") String user, + @QueryParam("group") List groups, + AttachmentInfo attachment) { + return userTaskService.updateAttachment(taskId, attachmentId, IdentityProviders.of(user, groups), attachment) + .orElseThrow(NotFoundException::new); + } + + @DELETE + @Path("/{taskId}/attachments/{attachmentId}") + public Response deleteAttachment( + @PathParam("taskId") String taskId, + @PathParam("attachmentId") String attachmentId, + @QueryParam("user") String user, + @QueryParam("group") List groups) { + return userTaskService.deleteAttachment(taskId, attachmentId, IdentityProviders.of(user, groups)) + .map(removed -> (removed ? Response.ok() : Response.status(Status.NOT_FOUND)).build()) + .orElseThrow(NotFoundException::new); + } + + @GET + @Path("/{taskId}/attachments/{attachmentId}") + @Produces(MediaType.APPLICATION_JSON) + public Attachment getAttachment( + @PathParam("taskId") String taskId, + @PathParam("attachmentId") String attachmentId, + @QueryParam("user") String user, + @QueryParam("group") List groups) { + return userTaskService.getAttachment(taskId, attachmentId, IdentityProviders.of(user, groups)) + .orElseThrow(() -> new NotFoundException("Attachment " + attachmentId + " not found")); + } + +} diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/RestResourceUserTaskSpringTemplate.java b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/RestResourceUserTaskSpringTemplate.java new file mode 100644 index 00000000000..4f15852b2c8 --- /dev/null +++ b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/RestResourceUserTaskSpringTemplate.java @@ -0,0 +1,192 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 com.myspace.demo; + +import java.util.List; +import java.util.Map; + +import org.jbpm.util.JsonSchemaUtil; +import org.kie.kogito.auth.IdentityProviders; +import org.kie.kogito.auth.SecurityPolicy; +import org.kie.kogito.process.ProcessInstance; +import org.kie.kogito.process.WorkItem; +import org.kie.kogito.process.impl.Sig; +import org.kie.kogito.process.workitem.Comment; +import org.kie.kogito.process.workitem.TaskMetaInfo; +import org.kie.kogito.services.uow.UnitOfWorkExecutor; +import org.kie.kogito.usertask.UserTaskService; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.util.UriComponentsBuilder; +import org.springframework.beans.factory.annotation.Autowired; + +@RequestMapping(value = "/usertasks", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE) +public class UserTasksResource { + + @Autowired + UserTaskService userTaskService; + + @GetMapping(value = "/{taskId}/", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE) + public List list(@RequestParam("user") String user, @RequestParam("group") List groups) { + return userTaskService.list(IdentityProviders.of(user, groups)); + } + + @PostMapping + public UserTaskView transition( + @PathVariable("taskId") String taskId, + @RequestParam("transitionId") String transitionId, + @RequestParam("user") String user, + @RequestParam("group") List groups, Map data) { + return userTaskService.transition(taskId, transitionId, data, IdentityProviders.of(user, groups)).orElseThrow(NotFoundException::new); + } + + @PutMapping("/{taskId}/outputs") + public UserTaskView setOutput( + @PathVariable("taskId") String taskId, + @RequestParam("user") String user, + @RequestParam("group") List groups, + Map data) { + return userTaskService.setOutputs(taskId, data, IdentityProviders.of(user, groups)).orElseThrow(NotFoundException::new); + } + + @PUT("/{taskId}/inputs") + public UserTaskView setOutput(@PathVariable("id") String id, + @PathVariable("taskId") String taskId, + @RequestParam("user") String user, + @RequestParam("group") List groups, + Map data) { + return userTaskService.setInputs(taskId, data, IdentityProviders.of(user, groups)).orElseThrow(NotFoundException::new); + } + + @GetMapping("/{taskId}/comments") + public Collection getComments( + @PathVariable("taskId") String taskId, + @RequestParam("user") String user, + @RequestParam("group") List groups) { + return userTaskService.getComments(taskId, IdentityProviders.of(user, groups)).orElseThrow(NotFoundException::new); + } + + @PostMapping("/{taskId}/comments") + public Response addComment( + @PathVariable("taskId") String taskId, + @RequestParam("user") String user, + @RequestParam("group") List groups, + String commentInfo, + @Context UriInfo uriInfo) { + return userTaskService.addComment(taskId, IdentityProviders.of(user, groups), commentInfo) + .map(comment -> Response.created(uriInfo.getAbsolutePathBuilder().path(comment.getId().toString()).build()) + .entity(comment).build()) + .orElseThrow(NotFoundException::new); + } + + @GetMapping("/{taskId}/comments/{commentId}") + public Comment getComment( + @PathVariable("taskId") String taskId, + @PathVariable("commentId") String commentId, + @RequestParam("user") String user, + @RequestParam("group") List groups) { + return userTaskService.getComment(taskId, commentId, IdentityProviders.of(user, groups)) + .orElseThrow(() -> new NotFoundException("Comment " + commentId + " not found")); + } + + @PutMapping("/{taskId}/comments/{commentId}") + public Comment updateComment( + @PathVariable("taskId") String taskId, + @PathVariable("commentId") String commentId, + @RequestParam("user") String user, + @RequestParam("group") List groups, + String comment) { + return userTaskService.updateComment(taskId, commentId, IdentityProviders.of(user, groups), comment) + .orElseThrow(NotFoundException::new); + } + + @DeleteMapping("/{taskId}/comments/{commentId}") + public Response deleteComment( + @PathVariable("taskId") String taskId, + @PathVariable("commentId") String commentId, + @RequestParam("user") String user, + @RequestParam("group") List groups) { + return userTaskService.deleteComment(taskId, commentId, IdentityProviders.of(user, groups)) + .map(removed -> (removed ? Response.ok() : Response.status(Status.NOT_FOUND)).build()) + .orElseThrow(NotFoundException::new); + } + + @GetMapping("/{taskId}/attachments") + public Collection getAttachments( + @PathVariable("taskId") String taskId, + @RequestParam("user") String user, + @RequestParam("group") List groups) { + return userTaskService.getAttachments(taskId, IdentityProviders.of(user, groups)) + .orElseThrow(NotFoundException::new); + } + + @PostMapping("/{taskId}/attachments") + public Response addAttachment( + @PathVariable("taskId") String taskId, + @RequestParam("user") String user, + @RequestParam("group") List groups, + AttachmentInfo attachmentInfo, + @Context UriInfo uriInfo) { + return userTaskService.addAttachment(taskId, IdentityProviders.of(user, groups), attachmentInfo) + .map(attachment -> Response + .created(uriInfo.getAbsolutePathBuilder().path(attachment.getId().toString()).build()) + .entity(attachment).build()) + .orElseThrow(NotFoundException::new); + } + + @PutMapping("/{taskId}/attachments/{attachmentId}") + public Attachment updateAttachment( + @PathVariable("taskId") String taskId, + @PathVariable("attachmentId") String attachmentId, + @RequestParam("user") String user, + @RequestParam("group") List groups, + AttachmentInfo attachment) { + return userTaskService.updateAttachment(taskId, attachmentId, IdentityProviders.of(user, groups), attachment) + .orElseThrow(NotFoundException::new); + } + + @DeleteMapping("/{taskId}/attachments/{attachmentId}") + public Response deleteAttachment( + @PathVariable("taskId") String taskId, + @PathVariable("attachmentId") String attachmentId, + @RequestParam("user") String user, + @RequestParam("group") List groups) { + return userTaskService.deleteAttachment(taskId, attachmentId, IdentityProviders.of(user, groups)) + .map(removed -> (removed ? Response.ok() : Response.status(Status.NOT_FOUND)).build()) + .orElseThrow(NotFoundException::new); + } + + @GetMapping("/{taskId}/attachments/{attachmentId}") + public Attachment getAttachment( + @PathVariable("taskId") String taskId, + @PathVariable("attachmentId") String attachmentId, + @RequestParam("user") String user, + @RequestParam("group") List groups) { + return userTaskService.getAttachment(taskId, attachmentId, IdentityProviders.of(user, groups)) + .orElseThrow(() -> new NotFoundException("Attachment " + attachmentId + " not found")); + } + +} \ No newline at end of file diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTaskConfigQuarkusTemplate.java b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTaskConfigQuarkusTemplate.java index 3486fad3843..3d327fb03f8 100644 --- a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTaskConfigQuarkusTemplate.java +++ b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTaskConfigQuarkusTemplate.java @@ -1,3 +1,4 @@ + /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -16,6 +17,8 @@ * specific language governing permissions and limitations * under the License. */ +import java.util.List; + import org.kie.api.event.process.ProcessEventListener; import org.kie.kogito.auth.IdentityProvider; import org.kie.kogito.event.EventPublisher; @@ -27,26 +30,31 @@ import org.kie.kogito.uow.events.UnitOfWorkEventListener; import org.kie.kogito.usertask.impl.DefaultUserTaskConfig; import org.kie.kogito.usertask.lifecycle.UserTaskLifeCycle; +import org.kie.kogito.usertask.UserTaskAssignmentStrategyConfig; import org.kie.kogito.usertask.UserTaskEventListenerConfig; +import org.kie.kogito.usertask.UserTaskInstances; import jakarta.enterprise.inject.Instance; @jakarta.inject.Singleton public class UserTaskConfig extends DefaultUserTaskConfig { - - + @jakarta.inject.Inject public UserTaskConfig( Instance workItemHandlerConfig, Instance unitOfWorkManager, Instance jobsService, Instance identityProvider, - Instance userTaskLifeCycle) { - super(workItemHandlerConfig, + Instance userTaskLifeCycle, + Instance userTaskAssignmentStrategyConfigs, + Instance userTaskInstances) { + super(workItemHandlerConfig, unitOfWorkManager, jobsService, identityProvider, - userTaskLifeCycle); + userTaskLifeCycle, + userTaskAssignmentStrategyConfigs, + userTaskInstances); } - + } \ No newline at end of file diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTaskConfigSpringTemplate.java b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTaskConfigSpringTemplate.java index d8e00dbc576..11f7935f436 100644 --- a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTaskConfigSpringTemplate.java +++ b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTaskConfigSpringTemplate.java @@ -1,3 +1,4 @@ + /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -29,24 +30,30 @@ import org.kie.kogito.uow.events.UnitOfWorkEventListener; import org.kie.kogito.usertask.impl.DefaultUserTaskConfig; import org.kie.kogito.usertask.lifecycle.UserTaskLifeCycle; +import org.kie.kogito.usertask.UserTaskAssignmentStrategyConfig; import org.kie.kogito.usertask.UserTaskEventListenerConfig; +import org.kie.kogito.usertask.UserTaskInstances; @org.springframework.stereotype.Component public class UserTaskConfig extends DefaultUserTaskConfig { - + @org.springframework.beans.factory.annotation.Autowired public UserTaskConfig( List workItemHandlerConfig, List unitOfWorkManager, List jobsService, List identityProvider, - List userTaskLifeCycle) { + List userTaskLifeCycle, + List userTaskAssignmentStrategyConfigs, + List userTaskInstances) { - super(workItemHandlerConfig, + super(workItemHandlerConfig, unitOfWorkManager, jobsService, identityProvider, - userTaskLifeCycle); + userTaskLifeCycle, + userTaskAssignmentStrategyConfigs, + userTaskInstances); } - + } \ No newline at end of file diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTasksContainerQuarkusTemplate.java b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTasksContainerQuarkusTemplate.java index 9d22ea14eee..722b6e58b7c 100644 --- a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTasksContainerQuarkusTemplate.java +++ b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTasksContainerQuarkusTemplate.java @@ -22,7 +22,15 @@ import java.util.HashMap; import java.util.Map; import java.util.List; + +import org.kie.kogito.uow.events.UnitOfWorkUserTaskEventListener; +import org.kie.kogito.usertask.impl.DefaultUserTaskInstance; +import org.kie.kogito.usertask.impl.KogitoUserTaskEventSupportImpl; import org.kie.kogito.usertask.UserTask; +import org.kie.kogito.usertask.UserTaskConfig; +import org.kie.kogito.usertask.UserTaskInstance; +import org.kie.kogito.usertask.UserTaskInstances; + import jakarta.enterprise.inject.Instance; @jakarta.enterprise.context.ApplicationScoped @@ -31,6 +39,9 @@ public class UserTasks implements org.kie.kogito.usertask.UserTasks { @jakarta.inject.Inject jakarta.enterprise.inject.Instance userTasks; + @jakarta.inject.Inject + Application application; + private Map mappedUserTask = new HashMap<>(); @jakarta.annotation.PostConstruct @@ -38,6 +49,9 @@ public void setup() { for (UserTask userTask : userTasks) { mappedUserTask.put(userTask.id(), userTask); } + UserTaskInstances userTaskInstances = application.config().get(UserTaskConfig.class).userTaskInstances(); + userTaskInstances.setDisconnectUserTaskInstance(this::disconnect); + userTaskInstances.setReconnectUserTaskInstance(this::connect); } public UserTask userTaskById(String userTaskId) { @@ -47,4 +61,31 @@ public UserTask userTaskById(String userTaskId) { public Collection userTaskIds() { return mappedUserTask.keySet(); } + + @Override + public UserTaskInstances instances() { + return application.config().get(UserTaskConfig.class).userTaskInstances(); + } + + private UserTaskInstance disconnect(UserTaskInstance userTaskInstance) { + DefaultUserTaskInstance instance = (DefaultUserTaskInstance) userTaskInstance; + instance.setUserTask(null); + instance.setUserTaskEventSupport(null); + instance.setUserTaskLifeCycle(null); + instance.setInstances(null); + return instance; + } + + public UserTaskInstance connect(UserTaskInstance userTaskInstance) { + DefaultUserTaskInstance instance = (DefaultUserTaskInstance) userTaskInstance; + UserTaskConfig userTaskConfig = application.config().get(UserTaskConfig.class); + KogitoUserTaskEventSupportImpl impl = new KogitoUserTaskEventSupportImpl(userTaskConfig.identityProvider()); + userTaskConfig.userTaskEventListeners().listeners().forEach(impl::addEventListener); + impl.addEventListener(new UnitOfWorkUserTaskEventListener(application.unitOfWorkManager())); + instance.setUserTask(application.get(UserTasks.class).userTaskById(instance.getUserTaskId())); + instance.setUserTaskEventSupport(impl); + instance.setUserTaskLifeCycle(userTaskConfig.userTaskLifeCycle()); + instance.setInstances(application.config().get(UserTaskConfig.class).userTaskInstances()); + return instance; + } } \ No newline at end of file diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTasksContainerSpringTemplate.java b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTasksContainerSpringTemplate.java index 1e65367bfc5..626c0f51212 100644 --- a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTasksContainerSpringTemplate.java +++ b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTasksContainerSpringTemplate.java @@ -22,24 +22,38 @@ import java.util.HashMap; import java.util.Map; import java.util.List; + +import org.kie.kogito.Application; +import org.kie.kogito.uow.events.UnitOfWorkUserTaskEventListener; +import org.kie.kogito.usertask.impl.DefaultUserTaskInstance; +import org.kie.kogito.usertask.impl.KogitoUserTaskEventSupportImpl; import org.kie.kogito.usertask.UserTask; +import org.kie.kogito.usertask.UserTaskConfig; +import org.kie.kogito.usertask.UserTaskInstance; +import org.kie.kogito.usertask.UserTaskInstances; @org.springframework.web.context.annotation.ApplicationScope @org.springframework.stereotype.Component public class UserTasks implements org.kie.kogito.usertask.UserTasks { - + @org.springframework.beans.factory.annotation.Autowired Collection userTasks; + @org.springframework.beans.factory.annotation.Autowired + Application application; + private Map mappedUserTask = new HashMap<>(); - + @jakarta.annotation.PostConstruct public void setup() { for (UserTask userTask : userTasks) { mappedUserTask.put(userTask.id(), userTask); } + UserTaskInstances userTaskInstances = application.config().get(UserTaskConfig.class).userTaskInstances(); + userTaskInstances.setDisconnectUserTaskInstance(this::disconnect); + userTaskInstances.setReconnectUserTaskInstance(this::connect); } - + public UserTask userTaskById(String userTaskId) { return mappedUserTask.get(userTaskId); } @@ -47,4 +61,31 @@ public UserTask userTaskById(String userTaskId) { public Collection userTaskIds() { return mappedUserTask.keySet(); } + + @Override + public UserTaskInstances instances() { + return application.config().get(UserTaskConfig.class).userTaskInstances(); + } + + private UserTaskInstance disconnect(UserTaskInstance userTaskInstance) { + DefaultUserTaskInstance instance = (DefaultUserTaskInstance) userTaskInstance; + instance.setUserTask(null); + instance.setUserTaskEventSupport(null); + instance.setUserTaskLifeCycle(null); + instance.setInstances(null); + return instance; + } + + public UserTaskInstance connect(UserTaskInstance userTaskInstance) { + DefaultUserTaskInstance instance = (DefaultUserTaskInstance) userTaskInstance; + UserTaskConfig userTaskConfig = application.config().get(UserTaskConfig.class); + KogitoUserTaskEventSupportImpl impl = new KogitoUserTaskEventSupportImpl(userTaskConfig.identityProvider()); + userTaskConfig.userTaskEventListeners().listeners().forEach(impl::addEventListener); + impl.addEventListener(new UnitOfWorkUserTaskEventListener(application.unitOfWorkManager())); + instance.setUserTask(application.get(UserTasks.class).userTaskById(instance.getUserTaskId())); + instance.setUserTaskEventSupport(impl); + instance.setUserTaskLifeCycle(userTaskConfig.userTaskLifeCycle()); + instance.setInstances(application.config().get(UserTaskConfig.class).userTaskInstances()); + return instance; + } } \ No newline at end of file diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTasksServiceProducerQuarkusTemplate.java b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTasksServiceProducerQuarkusTemplate.java new file mode 100644 index 00000000000..d4f47ccb7f0 --- /dev/null +++ b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTasksServiceProducerQuarkusTemplate.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 $Package$; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; + +import org.kie.kogito.Application; +import org.kie.kogito.internal.process.workitem.KogitoWorkItemHandler; +import org.kie.kogito.jbpm.usertask.handler.UserTaskKogitoWorkItemHandler; +import org.kie.kogito.jbpm.usertask.handler.UserTaskKogitoWorkItemHandlerProcessListener; +import org.kie.kogito.process.Processes; +import org.kie.kogito.usertask.UserTaskEventListener; +import org.kie.kogito.usertask.UserTaskService; +import org.kie.kogito.usertask.impl.UserTaskServiceImpl; + +@ApplicationScoped +public class UserTasksServiceProducer { + + @Produces + public UserTaskService userTaskService(Application application) { + return new UserTaskServiceImpl(application); + } + + @Produces + public UserTaskEventListener userTaskListener(Application application) { + return new UserTaskKogitoWorkItemHandlerProcessListener(application.get(Processes.class)); + } + + @Produces + public KogitoWorkItemHandler userTaskWorkItemHandler(Application application) { + return new UserTaskKogitoWorkItemHandler(application); + } +} diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTasksServiceProducerSpringTemplate.java b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTasksServiceProducerSpringTemplate.java new file mode 100644 index 00000000000..58f5b5e879b --- /dev/null +++ b/kogito-codegen-modules/kogito-codegen-processes/src/main/resources/class-templates/usertask/UserTasksServiceProducerSpringTemplate.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 $Package$; + +import org.kie.kogito.Application; +import org.kie.kogito.internal.process.workitem.KogitoWorkItemHandler; +import org.kie.kogito.jbpm.usertask.handler.UserTaskKogitoWorkItemHandler; +import org.kie.kogito.jbpm.usertask.handler.UserTaskKogitoWorkItemHandlerProcessListener; +import org.kie.kogito.process.ProcessService; +import org.kie.kogito.process.Processes; +import org.kie.kogito.process.impl.ProcessServiceImpl; +import org.kie.kogito.usertask.UserTaskEventListener; +import org.kie.kogito.usertask.impl.UserTaskServiceImpl; +import org.kie.kogito.usertask.UserTaskService; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class UserTaskServiceProducer { + + @Bean + public UserTaskService userTaskService(Application application) { + return new UserTaskServiceImpl(application); + } + + @Bean + public UserTaskEventListener userTaskListener(Application application) { + return new UserTaskKogitoWorkItemHandlerProcessListener(application.get(Processes.class)); + } + + @Bean + public KogitoWorkItemHandler userTaskWorkItemHandler(Application application) { + return new UserTaskKogitoWorkItemHandler(application); + } + +} diff --git a/quarkus/integration-tests/integration-tests-quarkus-processes/src/test/java/org/kie/kogito/integrationtests/quarkus/TaskIT.java b/quarkus/integration-tests/integration-tests-quarkus-processes/src/test/java/org/kie/kogito/integrationtests/quarkus/TaskIT.java index d5a5513a165..52778a76471 100644 --- a/quarkus/integration-tests/integration-tests-quarkus-processes/src/test/java/org/kie/kogito/integrationtests/quarkus/TaskIT.java +++ b/quarkus/integration-tests/integration-tests-quarkus-processes/src/test/java/org/kie/kogito/integrationtests/quarkus/TaskIT.java @@ -25,7 +25,6 @@ import java.util.Set; import org.acme.travels.Traveller; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.kie.kogito.task.management.service.TaskInfo; import org.kie.kogito.usertask.model.AttachmentInfo; @@ -160,7 +159,6 @@ void testSaveTask() { } @Test - @Disabled("Revisited after ht endpoints") void testCommentAndAttachment() { Traveller traveller = new Traveller("pepe", "rubiales", "pepe.rubiales@gmail.com", "Spanish"); diff --git a/springboot/integration-tests/integration-tests-springboot-processes-it/src/test/java/org/kie/kogito/integrationtests/springboot/TaskTest.java b/springboot/integration-tests/integration-tests-springboot-processes-it/src/test/java/org/kie/kogito/integrationtests/springboot/TaskTest.java index 970968e6497..0208f9c8a12 100644 --- a/springboot/integration-tests/integration-tests-springboot-processes-it/src/test/java/org/kie/kogito/integrationtests/springboot/TaskTest.java +++ b/springboot/integration-tests/integration-tests-springboot-processes-it/src/test/java/org/kie/kogito/integrationtests/springboot/TaskTest.java @@ -31,7 +31,6 @@ import org.acme.travels.Address; import org.acme.travels.Traveller; import org.jbpm.util.JsonSchemaUtil; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.kie.kogito.task.management.service.TaskInfo; @@ -111,7 +110,6 @@ void testJsonSchemaFiles() { } @Test - @Disabled("Revisited after ht endpoints") void testCommentAndAttachment() { Traveller traveller = new Traveller("pepe", "rubiales", "pepe.rubiales@gmail.com", "Spanish", null);