TSK-1131 updateOwner on multiple tasks
This commit is contained in:
parent
21a4110f2a
commit
20bd0922db
|
@ -249,7 +249,9 @@ public interface TaskService {
|
||||||
* cannot be found
|
* cannot be found
|
||||||
* @throws NotAuthorizedException if the current user is not authorized to update the task
|
* @throws NotAuthorizedException if the current user is not authorized to update the task
|
||||||
* @throws AttachmentPersistenceException if an Attachment with ID will be added multiple times
|
* @throws AttachmentPersistenceException if an Attachment with ID will be added multiple times
|
||||||
* without using the task-methods.
|
* without using the task-methods
|
||||||
|
* @throws InvalidStateException if an attempt is made to change the owner of the task and the
|
||||||
|
* task is not in state READY .
|
||||||
*/
|
*/
|
||||||
Task updateTask(Task task)
|
Task updateTask(Task task)
|
||||||
throws InvalidArgumentException, TaskNotFoundException, ConcurrencyException,
|
throws InvalidArgumentException, TaskNotFoundException, ConcurrencyException,
|
||||||
|
@ -373,4 +375,14 @@ public interface TaskService {
|
||||||
*/
|
*/
|
||||||
BulkOperationResults<String, TaskanaException> setCallbackStateForTasks(
|
BulkOperationResults<String, TaskanaException> setCallbackStateForTasks(
|
||||||
List<String> externalIds, CallbackState state);
|
List<String> externalIds, CallbackState state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the owner on a list of tasks. The owner will only be set on tasks that are in state READY.
|
||||||
|
*
|
||||||
|
* @param owner the new owner of the tasks
|
||||||
|
* @param taskIds the IDs of the tasks on which the owner is to be set.
|
||||||
|
* @return the result of the operations with Id and Exception for each failed task deletion.
|
||||||
|
*/
|
||||||
|
BulkOperationResults<String, TaskanaException> setOwnerOfTasks(
|
||||||
|
String owner, List<String> taskIds);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
package pro.taskana.task.api.exceptions;
|
||||||
|
|
||||||
|
import pro.taskana.common.api.exceptions.TaskanaException;
|
||||||
|
|
||||||
|
/** This exception will be thrown if a specific task is not in the database. */
|
||||||
|
public class UpdateFailedException extends TaskanaException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public UpdateFailedException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package pro.taskana.task.internal;
|
package pro.taskana.task.internal;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.apache.ibatis.annotations.Delete;
|
import org.apache.ibatis.annotations.Delete;
|
||||||
|
@ -132,6 +133,16 @@ public interface TaskMapper {
|
||||||
void setCallbackStateMultiple(
|
void setCallbackStateMultiple(
|
||||||
@Param("externalIds") List<String> externalIds, @Param("state") CallbackState state);
|
@Param("externalIds") List<String> externalIds, @Param("state") CallbackState state);
|
||||||
|
|
||||||
|
@Update(
|
||||||
|
"<script>UPDATE TASK SET OWNER = #{owner}, MODIFIED = #{modified} "
|
||||||
|
+ "WHERE STATE = 'READY' "
|
||||||
|
+ "AND ID IN <foreach item='taskId' index='index' separator=',' open='(' close=')' collection='taskIds'>#{taskId}</foreach> "
|
||||||
|
+ "</script>")
|
||||||
|
int setOwnerOfTasks(
|
||||||
|
@Param("owner") String owner,
|
||||||
|
@Param("taskIds") List<String> taskIds,
|
||||||
|
@Param("modified") Instant modified);
|
||||||
|
|
||||||
@Select(
|
@Select(
|
||||||
"<script>SELECT ID, EXTERNAL_ID, CREATED, CLAIMED, COMPLETED, MODIFIED, PLANNED, DUE, NAME, CREATOR, DESCRIPTION, PRIORITY, STATE, CLASSIFICATION_CATEGORY, CLASSIFICATION_KEY, CLASSIFICATION_ID, WORKBASKET_ID, WORKBASKET_KEY, DOMAIN, BUSINESS_PROCESS_ID, PARENT_BUSINESS_PROCESS_ID, OWNER, POR_COMPANY, POR_SYSTEM, POR_INSTANCE, POR_TYPE, POR_VALUE, IS_READ, IS_TRANSFERRED, CUSTOM_ATTRIBUTES, CUSTOM_1, CUSTOM_2, CUSTOM_3, CUSTOM_4, CUSTOM_5, CUSTOM_6, CUSTOM_7, "
|
"<script>SELECT ID, EXTERNAL_ID, CREATED, CLAIMED, COMPLETED, MODIFIED, PLANNED, DUE, NAME, CREATOR, DESCRIPTION, PRIORITY, STATE, CLASSIFICATION_CATEGORY, CLASSIFICATION_KEY, CLASSIFICATION_ID, WORKBASKET_ID, WORKBASKET_KEY, DOMAIN, BUSINESS_PROCESS_ID, PARENT_BUSINESS_PROCESS_ID, OWNER, POR_COMPANY, POR_SYSTEM, POR_INSTANCE, POR_TYPE, POR_VALUE, IS_READ, IS_TRANSFERRED, CUSTOM_ATTRIBUTES, CUSTOM_1, CUSTOM_2, CUSTOM_3, CUSTOM_4, CUSTOM_5, CUSTOM_6, CUSTOM_7, "
|
||||||
+ "CUSTOM_8, CUSTOM_9, CUSTOM_10, CUSTOM_11, CUSTOM_12, CUSTOM_13, CUSTOM_14, CUSTOM_15, CUSTOM_16 "
|
+ "CUSTOM_8, CUSTOM_9, CUSTOM_10, CUSTOM_11, CUSTOM_12, CUSTOM_13, CUSTOM_14, CUSTOM_15, CUSTOM_16 "
|
||||||
|
@ -214,7 +225,7 @@ public interface TaskMapper {
|
||||||
@Param("referencetask") TaskSummaryImpl referencetask);
|
@Param("referencetask") TaskSummaryImpl referencetask);
|
||||||
|
|
||||||
@Select(
|
@Select(
|
||||||
"<script>SELECT ID, EXTERNAL_ID, STATE, WORKBASKET_ID, CALLBACK_STATE FROM TASK "
|
"<script>SELECT ID, EXTERNAL_ID, STATE, WORKBASKET_ID, OWNER, CALLBACK_STATE FROM TASK "
|
||||||
+ "<where> "
|
+ "<where> "
|
||||||
+ "<if test='taskIds != null'>ID IN(<foreach item='item' collection='taskIds' separator=',' >#{item}</foreach>)</if> "
|
+ "<if test='taskIds != null'>ID IN(<foreach item='item' collection='taskIds' separator=',' >#{item}</foreach>)</if> "
|
||||||
+ "<if test='externalIds != null'>EXTERNAL_ID IN(<foreach item='item' collection='externalIds' separator=',' >#{item}</foreach>)</if> "
|
+ "<if test='externalIds != null'>EXTERNAL_ID IN(<foreach item='item' collection='externalIds' separator=',' >#{item}</foreach>)</if> "
|
||||||
|
@ -226,6 +237,7 @@ public interface TaskMapper {
|
||||||
@Result(property = "taskId", column = "ID"),
|
@Result(property = "taskId", column = "ID"),
|
||||||
@Result(property = "externalId", column = "EXTERNAL_ID"),
|
@Result(property = "externalId", column = "EXTERNAL_ID"),
|
||||||
@Result(property = "workbasketId", column = "WORKBASKET_ID"),
|
@Result(property = "workbasketId", column = "WORKBASKET_ID"),
|
||||||
|
@Result(property = "owner", column = "OWNER"),
|
||||||
@Result(property = "taskState", column = "STATE"),
|
@Result(property = "taskState", column = "STATE"),
|
||||||
@Result(property = "callbackState", column = "CALLBACK_STATE")
|
@Result(property = "callbackState", column = "CALLBACK_STATE")
|
||||||
})
|
})
|
||||||
|
@ -274,4 +286,25 @@ public interface TaskMapper {
|
||||||
+ "</script>")
|
+ "</script>")
|
||||||
@Results(value = {@Result(property = "taskId", column = "ID")})
|
@Results(value = {@Result(property = "taskId", column = "ID")})
|
||||||
List<String> filterTaskIdsForNotCompleted(@Param("taskIds") List<String> taskIds);
|
List<String> filterTaskIdsForNotCompleted(@Param("taskIds") List<String> taskIds);
|
||||||
|
|
||||||
|
@Select(
|
||||||
|
"<script> "
|
||||||
|
+ "SELECT t.ID FROM TASK t WHERE t.ID IN(<foreach item='item' collection='taskIds' separator=',' >#{item}</foreach>)"
|
||||||
|
+ "<if test='accessIds != null'> "
|
||||||
|
+ "AND NOT (t.WORKBASKET_ID IN ( "
|
||||||
|
+ "<choose>"
|
||||||
|
+ "<when test=\"_databaseId == 'db2'\">"
|
||||||
|
+ "SELECT WID from (SELECT WORKBASKET_ID as WID, MAX(PERM_READ) as MAX_READ FROM WORKBASKET_ACCESS_LIST AS s where "
|
||||||
|
+ "</when>"
|
||||||
|
+ "<otherwise>"
|
||||||
|
+ "SELECT WID from (SELECT WORKBASKET_ID as WID, MAX(PERM_READ::int) as MAX_READ FROM WORKBASKET_ACCESS_LIST AS s where "
|
||||||
|
+ "</otherwise>"
|
||||||
|
+ "</choose>"
|
||||||
|
+ "ACCESS_ID IN (<foreach item='item' collection='accessIds' separator=',' >#{item}</foreach>) "
|
||||||
|
+ "group by WORKBASKET_ID ) AS f where max_read = 1 ))"
|
||||||
|
+ "</if> "
|
||||||
|
+ "</script>")
|
||||||
|
@Results(value = {@Result(property = "id", column = "ID")})
|
||||||
|
List<String> filterTaskIdsNotAuthorizedFor(
|
||||||
|
@Param("taskIds") List<String> taskIds, @Param("accessIds") List<String> accessIds);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import org.apache.ibatis.exceptions.PersistenceException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import pro.taskana.TaskanaEngineConfiguration;
|
||||||
import pro.taskana.classification.api.ClassificationService;
|
import pro.taskana.classification.api.ClassificationService;
|
||||||
import pro.taskana.classification.api.exceptions.ClassificationNotFoundException;
|
import pro.taskana.classification.api.exceptions.ClassificationNotFoundException;
|
||||||
import pro.taskana.classification.api.models.Classification;
|
import pro.taskana.classification.api.models.Classification;
|
||||||
|
@ -48,6 +49,7 @@ import pro.taskana.task.api.exceptions.InvalidOwnerException;
|
||||||
import pro.taskana.task.api.exceptions.InvalidStateException;
|
import pro.taskana.task.api.exceptions.InvalidStateException;
|
||||||
import pro.taskana.task.api.exceptions.TaskAlreadyExistException;
|
import pro.taskana.task.api.exceptions.TaskAlreadyExistException;
|
||||||
import pro.taskana.task.api.exceptions.TaskNotFoundException;
|
import pro.taskana.task.api.exceptions.TaskNotFoundException;
|
||||||
|
import pro.taskana.task.api.exceptions.UpdateFailedException;
|
||||||
import pro.taskana.task.api.models.Attachment;
|
import pro.taskana.task.api.models.Attachment;
|
||||||
import pro.taskana.task.api.models.ObjectReference;
|
import pro.taskana.task.api.models.ObjectReference;
|
||||||
import pro.taskana.task.api.models.Task;
|
import pro.taskana.task.api.models.Task;
|
||||||
|
@ -617,6 +619,55 @@ public class TaskServiceImpl implements TaskService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BulkOperationResults<String, TaskanaException> setOwnerOfTasks(
|
||||||
|
String owner, List<String> argTaskIds) {
|
||||||
|
if (LOGGER.isDebugEnabled()) {
|
||||||
|
LOGGER.debug(
|
||||||
|
"entry to setOwnerOfTasks(owner = {}, tasks = {})",
|
||||||
|
owner,
|
||||||
|
LoggerUtils.listToString(argTaskIds));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
taskanaEngine.openConnection();
|
||||||
|
BulkOperationResults<String, TaskanaException> bulkLog = new BulkOperationResults<>();
|
||||||
|
if (argTaskIds == null || argTaskIds.isEmpty()) {
|
||||||
|
return bulkLog;
|
||||||
|
}
|
||||||
|
// remove duplicates
|
||||||
|
List<String> taskIds = argTaskIds.stream().sorted().distinct().collect(Collectors.toList());
|
||||||
|
final int requestSize = taskIds.size();
|
||||||
|
// use only elements we are authorized for
|
||||||
|
taskIds = filterForAuthorized(taskIds, bulkLog);
|
||||||
|
// set the Owner of these tasks we are authorized for
|
||||||
|
if (taskIds.isEmpty()) {
|
||||||
|
return bulkLog;
|
||||||
|
} else {
|
||||||
|
final int numberOfAffectedTasks = taskMapper.setOwnerOfTasks(owner, taskIds, Instant.now());
|
||||||
|
// check the outcome
|
||||||
|
List<MinimalTaskSummary> existingMinimalTaskSummaries =
|
||||||
|
taskMapper.findExistingTasks(taskIds, null);
|
||||||
|
// check for tasks that don't exist
|
||||||
|
handleNonExistingTasks(taskIds, existingMinimalTaskSummaries, bulkLog);
|
||||||
|
// add all errors that occured to bulkLog
|
||||||
|
addErrorsToResultObject(owner, bulkLog, existingMinimalTaskSummaries);
|
||||||
|
LOGGER.debug(
|
||||||
|
"Received the Request to set owner on "
|
||||||
|
+ requestSize
|
||||||
|
+ " tasks, "
|
||||||
|
+ "actually modified tasks = "
|
||||||
|
+ numberOfAffectedTasks
|
||||||
|
+ ", could not set owner on "
|
||||||
|
+ bulkLog.getFailedIds().size()
|
||||||
|
+ " tasks.");
|
||||||
|
return bulkLog;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
LOGGER.debug("exit from setOwnerOfTasks()");
|
||||||
|
taskanaEngine.returnConnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Set<String> findTasksIdsAffectedByClassificationChange(String classificationId) {
|
public Set<String> findTasksIdsAffectedByClassificationChange(String classificationId) {
|
||||||
LOGGER.debug(
|
LOGGER.debug(
|
||||||
"entry to findTasksIdsAffectedByClassificationChange(classificationId = {})",
|
"entry to findTasksIdsAffectedByClassificationChange(classificationId = {})",
|
||||||
|
@ -757,6 +808,77 @@ public class TaskServiceImpl implements TaskService {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addErrorsToResultObject(
|
||||||
|
String owner,
|
||||||
|
BulkOperationResults<String, TaskanaException> bulkLog,
|
||||||
|
List<MinimalTaskSummary> existingMinimalTaskSummaries) {
|
||||||
|
for (MinimalTaskSummary taskSummary : existingMinimalTaskSummaries) {
|
||||||
|
if (!owner.equals(taskSummary.getOwner())) { // owner was not set
|
||||||
|
if (!taskSummary.getTaskState().equals(TaskState.READY)) { // due to invalid state
|
||||||
|
bulkLog.addError(
|
||||||
|
taskSummary.getTaskId(),
|
||||||
|
new InvalidStateException(
|
||||||
|
"Task "
|
||||||
|
+ taskSummary.getTaskId()
|
||||||
|
+ " is in state "
|
||||||
|
+ taskSummary.getTaskState()));
|
||||||
|
} else { // due to unknown reason
|
||||||
|
bulkLog.addError(
|
||||||
|
taskSummary.getTaskId(),
|
||||||
|
new UpdateFailedException(
|
||||||
|
"Could not set owner of Task " + taskSummary.getTaskId() + "."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleNonExistingTasks(
|
||||||
|
List<String> taskIds,
|
||||||
|
List<MinimalTaskSummary> existingMinimalTaskSummaries,
|
||||||
|
BulkOperationResults<String, TaskanaException> bulkLog) {
|
||||||
|
List<String> nonExistingTaskIds = new ArrayList<>(taskIds);
|
||||||
|
List<String> existingTaskIds =
|
||||||
|
existingMinimalTaskSummaries.stream()
|
||||||
|
.map(MinimalTaskSummary::getTaskId)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
nonExistingTaskIds.removeAll(existingTaskIds);
|
||||||
|
for (String taskId : nonExistingTaskIds) {
|
||||||
|
bulkLog.addError(taskId, new TaskNotFoundException(taskId, "Task was not found"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> filterForAuthorized(
|
||||||
|
List<String> taskIds, BulkOperationResults<String, TaskanaException> bulkLog) {
|
||||||
|
List<String> accessIds = getAccessIds();
|
||||||
|
List<String> tasksAuthorizedFor = new ArrayList<>(taskIds);
|
||||||
|
if (accessIds != null) {
|
||||||
|
List<String> tasksNotAuthorizedFor =
|
||||||
|
taskMapper.filterTaskIdsNotAuthorizedFor(taskIds, accessIds);
|
||||||
|
tasksAuthorizedFor.removeAll(tasksNotAuthorizedFor);
|
||||||
|
String currentUserId = CurrentUserContext.getUserid();
|
||||||
|
for (String taskId : tasksNotAuthorizedFor) {
|
||||||
|
bulkLog.addError(
|
||||||
|
taskId,
|
||||||
|
new NotAuthorizedException(
|
||||||
|
"Current user not authorized for task " + taskId, currentUserId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tasksAuthorizedFor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getAccessIds() {
|
||||||
|
List<String> accessIds;
|
||||||
|
if (taskanaEngine.getEngine().isUserInRole(TaskanaRole.ADMIN)) {
|
||||||
|
accessIds = null;
|
||||||
|
} else {
|
||||||
|
accessIds = CurrentUserContext.getAccessIds();
|
||||||
|
if (TaskanaEngineConfiguration.shouldUseLowerCaseForAccessIds()) {
|
||||||
|
accessIds = accessIds.stream().map(String::toLowerCase).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return accessIds;
|
||||||
|
}
|
||||||
|
|
||||||
private Task claim(String taskId, boolean forceClaim)
|
private Task claim(String taskId, boolean forceClaim)
|
||||||
throws TaskNotFoundException, InvalidStateException, InvalidOwnerException,
|
throws TaskNotFoundException, InvalidStateException, InvalidOwnerException,
|
||||||
NotAuthorizedException {
|
NotAuthorizedException {
|
||||||
|
|
|
@ -9,6 +9,7 @@ public class MinimalTaskSummary {
|
||||||
private String taskId;
|
private String taskId;
|
||||||
private String externalId;
|
private String externalId;
|
||||||
private String workbasketId;
|
private String workbasketId;
|
||||||
|
private String owner;
|
||||||
private TaskState taskState;
|
private TaskState taskState;
|
||||||
private CallbackState callbackState;
|
private CallbackState callbackState;
|
||||||
|
|
||||||
|
@ -38,6 +39,14 @@ public class MinimalTaskSummary {
|
||||||
this.workbasketId = workbasketKey;
|
this.workbasketId = workbasketKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getOwner() {
|
||||||
|
return owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOwner(String owner) {
|
||||||
|
this.owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
public TaskState getTaskState() {
|
public TaskState getTaskState() {
|
||||||
return taskState;
|
return taskState;
|
||||||
}
|
}
|
||||||
|
@ -58,10 +67,16 @@ public class MinimalTaskSummary {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "MinimalTaskSummary [taskId="
|
return "MinimalTaskSummary [taskId="
|
||||||
+ taskId
|
+ taskId
|
||||||
|
+ ", externalId="
|
||||||
|
+ externalId
|
||||||
+ ", workbasketId="
|
+ ", workbasketId="
|
||||||
+ workbasketId
|
+ workbasketId
|
||||||
|
+ ", owner="
|
||||||
|
+ owner
|
||||||
+ ", taskState="
|
+ ", taskState="
|
||||||
+ taskState
|
+ taskState
|
||||||
|
+ ", callbackState="
|
||||||
|
+ callbackState
|
||||||
+ "]";
|
+ "]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
package pro.taskana.task.internal.models;
|
||||||
|
|
||||||
|
import pro.taskana.task.api.TaskState;
|
||||||
|
|
||||||
|
public class TaskIdOwnerState {
|
||||||
|
private String taskId;
|
||||||
|
private String owner;
|
||||||
|
private TaskState taskState;
|
||||||
|
|
||||||
|
TaskIdOwnerState() {}
|
||||||
|
|
||||||
|
public String getTaskId() {
|
||||||
|
return taskId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTaskId(String taskId) {
|
||||||
|
this.taskId = taskId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOwner() {
|
||||||
|
return owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOwner(String owner) {
|
||||||
|
this.owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TaskState getTaskState() {
|
||||||
|
return taskState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTaskState(TaskState taskState) {
|
||||||
|
this.taskState = taskState;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "TaskIdOwnerState [taskId="
|
||||||
|
+ taskId
|
||||||
|
+ ", owner="
|
||||||
|
+ owner
|
||||||
|
+ ", taskState="
|
||||||
|
+ taskState
|
||||||
|
+ "]";
|
||||||
|
}
|
||||||
|
}
|
|
@ -66,6 +66,9 @@ public interface WorkbasketService {
|
||||||
* @param workbasket The Workbasket to update
|
* @param workbasket The Workbasket to update
|
||||||
* @return the updated Workbasket
|
* @return the updated Workbasket
|
||||||
* @throws NotAuthorizedException if the current user is not authorized to update the work basket
|
* @throws NotAuthorizedException if the current user is not authorized to update the work basket
|
||||||
|
* @throws WorkbasketNotFoundException if the workbasket cannot be found.
|
||||||
|
* @throws ConcurrencyException if an attempt is made to update the workbasket and another user
|
||||||
|
* updated it already
|
||||||
*/
|
*/
|
||||||
Workbasket updateWorkbasket(Workbasket workbasket)
|
Workbasket updateWorkbasket(Workbasket workbasket)
|
||||||
throws NotAuthorizedException, WorkbasketNotFoundException, ConcurrencyException;
|
throws NotAuthorizedException, WorkbasketNotFoundException, ConcurrencyException;
|
||||||
|
|
|
@ -0,0 +1,265 @@
|
||||||
|
package acceptance.task;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
|
||||||
|
import acceptance.AbstractAccTest;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
|
||||||
|
import pro.taskana.classification.api.exceptions.ClassificationNotFoundException;
|
||||||
|
import pro.taskana.common.api.BulkOperationResults;
|
||||||
|
import pro.taskana.common.api.exceptions.ConcurrencyException;
|
||||||
|
import pro.taskana.common.api.exceptions.InvalidArgumentException;
|
||||||
|
import pro.taskana.common.api.exceptions.NotAuthorizedException;
|
||||||
|
import pro.taskana.common.api.exceptions.TaskanaException;
|
||||||
|
import pro.taskana.common.internal.TaskanaEngineProxyForTest;
|
||||||
|
import pro.taskana.security.JaasExtension;
|
||||||
|
import pro.taskana.security.WithAccessId;
|
||||||
|
import pro.taskana.task.api.TaskService;
|
||||||
|
import pro.taskana.task.api.TaskState;
|
||||||
|
import pro.taskana.task.api.exceptions.AttachmentPersistenceException;
|
||||||
|
import pro.taskana.task.api.exceptions.InvalidOwnerException;
|
||||||
|
import pro.taskana.task.api.exceptions.InvalidStateException;
|
||||||
|
import pro.taskana.task.api.exceptions.TaskNotFoundException;
|
||||||
|
import pro.taskana.task.api.models.Task;
|
||||||
|
import pro.taskana.task.api.models.TaskSummary;
|
||||||
|
|
||||||
|
/** Acceptance test for all "update task" scenarios. */
|
||||||
|
@ExtendWith(JaasExtension.class)
|
||||||
|
public class SetOwnerAccTest extends AbstractAccTest {
|
||||||
|
|
||||||
|
@WithAccessId(
|
||||||
|
userName = "user_1_2",
|
||||||
|
groupNames = {"group_1"})
|
||||||
|
@Test
|
||||||
|
void testSetOwnerAndSubsequentClaimSucceeds()
|
||||||
|
throws TaskNotFoundException, NotAuthorizedException, InvalidStateException,
|
||||||
|
InvalidOwnerException, ClassificationNotFoundException, InvalidArgumentException,
|
||||||
|
ConcurrencyException, AttachmentPersistenceException {
|
||||||
|
|
||||||
|
TaskService taskService = taskanaEngine.getTaskService();
|
||||||
|
String taskReadyId = "TKI:000000000000000000000000000000000025";
|
||||||
|
Task taskReady = taskService.getTask(taskReadyId);
|
||||||
|
assertThat(taskReady.getState()).isEqualTo(TaskState.READY);
|
||||||
|
assertThat(taskReady.getOwner()).isNull();
|
||||||
|
String anyUserName = "TestUser27";
|
||||||
|
Task modifiedTaskReady = setOwner(taskReadyId, anyUserName);
|
||||||
|
|
||||||
|
assertThat(modifiedTaskReady.getState()).isEqualTo(TaskState.READY);
|
||||||
|
assertThat(modifiedTaskReady.getOwner()).isEqualTo(anyUserName);
|
||||||
|
Task taskClaimed = taskService.claim(taskReadyId);
|
||||||
|
assertThat(taskClaimed.getState()).isEqualTo(TaskState.CLAIMED);
|
||||||
|
assertThat(taskClaimed.getOwner()).isEqualTo("user_1_2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@WithAccessId(
|
||||||
|
userName = "user_1_2",
|
||||||
|
groupNames = {"group_1"})
|
||||||
|
@Test
|
||||||
|
void testSetOwnerViaUpdateTaskNotAuthorized()
|
||||||
|
throws TaskNotFoundException, NotAuthorizedException, InvalidStateException,
|
||||||
|
InvalidOwnerException {
|
||||||
|
|
||||||
|
TaskService taskService = taskanaEngine.getTaskService();
|
||||||
|
String taskReadyId = "TKI:000000000000000000000000000000000024";
|
||||||
|
String anyUserName = "TestUser3";
|
||||||
|
|
||||||
|
assertThatThrownBy(() -> taskService.getTask(taskReadyId))
|
||||||
|
.isInstanceOf(NotAuthorizedException.class);
|
||||||
|
assertThatThrownBy(() -> setOwner(taskReadyId, anyUserName))
|
||||||
|
.isInstanceOf(NotAuthorizedException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@WithAccessId(
|
||||||
|
userName = "user_1_2",
|
||||||
|
groupNames = {"group_1"})
|
||||||
|
@Test
|
||||||
|
void testSetOwnerOfClaimedTaskFails()
|
||||||
|
throws TaskNotFoundException, NotAuthorizedException, InvalidStateException,
|
||||||
|
InvalidOwnerException {
|
||||||
|
|
||||||
|
TaskService taskService = taskanaEngine.getTaskService();
|
||||||
|
String taskClaimedId = "TKI:000000000000000000000000000000000026";
|
||||||
|
String anyUserName = "TestUser007";
|
||||||
|
Task taskClaimed = taskService.getTask(taskClaimedId);
|
||||||
|
assertThat(taskClaimed.getState()).isEqualTo(TaskState.CLAIMED);
|
||||||
|
assertThat(taskClaimed.getOwner()).isEqualTo("user_1_1");
|
||||||
|
assertThatThrownBy(() -> setOwner(taskClaimedId, anyUserName))
|
||||||
|
.isInstanceOf(InvalidStateException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@WithAccessId(
|
||||||
|
userName = "user_1_2",
|
||||||
|
groupNames = {"group_1"})
|
||||||
|
@Test
|
||||||
|
void testSetOwnerNotAuthorized()
|
||||||
|
throws TaskNotFoundException, NotAuthorizedException, InvalidStateException,
|
||||||
|
InvalidOwnerException {
|
||||||
|
|
||||||
|
TaskService taskService = taskanaEngine.getTaskService();
|
||||||
|
String taskReadyId = "TKI:000000000000000000000000000000000024";
|
||||||
|
String anyUserName = "TestUser3";
|
||||||
|
|
||||||
|
assertThatThrownBy(() -> taskService.getTask(taskReadyId))
|
||||||
|
.isInstanceOf(NotAuthorizedException.class);
|
||||||
|
BulkOperationResults<String, TaskanaException> results =
|
||||||
|
taskService.setOwnerOfTasks(anyUserName, Arrays.asList(taskReadyId));
|
||||||
|
assertThat(results.containsErrors()).isTrue();
|
||||||
|
assertThat(results.getErrorForId(taskReadyId)).isInstanceOf(NotAuthorizedException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@WithAccessId(
|
||||||
|
userName = "user_3_2",
|
||||||
|
groupNames = {"group_2"})
|
||||||
|
@Test
|
||||||
|
void testSetOwnerOfTasksWithDuplicatesSucceeds() {
|
||||||
|
|
||||||
|
List<String> taskIds =
|
||||||
|
Arrays.asList(
|
||||||
|
"TKI:000000000000000000000000000000000058",
|
||||||
|
"TKI:000000000000000000000000000000000059",
|
||||||
|
"TKI:000000000000000000000000000000000058",
|
||||||
|
"TKI:000000000000000000000000000000000060");
|
||||||
|
BulkOperationResults<String, TaskanaException> results =
|
||||||
|
taskanaEngine.getTaskService().setOwnerOfTasks("someUser", taskIds);
|
||||||
|
assertThat(results.containsErrors()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@WithAccessId(
|
||||||
|
userName = "user_3_2",
|
||||||
|
groupNames = {"group_2"})
|
||||||
|
@Test
|
||||||
|
void testSetOwnerOfTasksWithDuplicatesAndNotExistingSucceeds() {
|
||||||
|
List<String> taskIds =
|
||||||
|
Arrays.asList(
|
||||||
|
"TKI:000000000000000000000000000000000058",
|
||||||
|
"TKI:000000000000000000000000000047110059",
|
||||||
|
"TKI:000000000000000000000000000000000059",
|
||||||
|
"TKI:000000000000000000000000000000000058",
|
||||||
|
"TKI:000000000000000000000000000000000060");
|
||||||
|
BulkOperationResults<String, TaskanaException> results =
|
||||||
|
taskanaEngine.getTaskService().setOwnerOfTasks("someUser", taskIds);
|
||||||
|
assertThat(results.containsErrors()).isTrue();
|
||||||
|
assertThat(results.getErrorMap().size()).isEqualTo(1);
|
||||||
|
assertThat(results.getErrorForId("TKI:000000000000000000000000000047110059"))
|
||||||
|
.isInstanceOf(TaskNotFoundException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@WithAccessId(
|
||||||
|
userName = "user_3_2",
|
||||||
|
groupNames = {"group_2"})
|
||||||
|
@Test
|
||||||
|
void testSetOwnerOfTasksWithNoQualifyingTasks() {
|
||||||
|
|
||||||
|
List<String> taskIds =
|
||||||
|
Arrays.asList(
|
||||||
|
"TKI:000000000000000000000000000000000008",
|
||||||
|
"TKI:000000000000000000000000000000000009",
|
||||||
|
"TKI:000000000000000000000000000000000008",
|
||||||
|
"TKI:000000000000000000000000000000000010");
|
||||||
|
BulkOperationResults<String, TaskanaException> results =
|
||||||
|
taskanaEngine.getTaskService().setOwnerOfTasks("someUser", taskIds);
|
||||||
|
assertThat(results.containsErrors()).isTrue();
|
||||||
|
assertThat(results.getErrorMap().size()).isEqualTo(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@WithAccessId(
|
||||||
|
userName = "user_3_2",
|
||||||
|
groupNames = {"group_2"})
|
||||||
|
@Test
|
||||||
|
void testSetOwnerWithEmptyList() {
|
||||||
|
List<String> taskIds = new ArrayList<>();
|
||||||
|
BulkOperationResults<String, TaskanaException> results =
|
||||||
|
taskanaEngine.getTaskService().setOwnerOfTasks("someUser", taskIds);
|
||||||
|
assertThat(results.containsErrors()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@WithAccessId(
|
||||||
|
userName = "user_1_2",
|
||||||
|
groupNames = {"group_2"})
|
||||||
|
@Test
|
||||||
|
void testSetOwnerWithAllTasksAndVariousExceptions()
|
||||||
|
throws NoSuchFieldException, IllegalAccessException, SQLException {
|
||||||
|
resetDb(false);
|
||||||
|
List<TaskSummary> allTaskSummaries =
|
||||||
|
new TaskanaEngineProxyForTest(taskanaEngine)
|
||||||
|
.getEngine()
|
||||||
|
.runAsAdmin(
|
||||||
|
() -> {
|
||||||
|
return taskanaEngine.getTaskService().createTaskQuery().list();
|
||||||
|
});
|
||||||
|
List<String> allTaskIds =
|
||||||
|
allTaskSummaries.stream().map(TaskSummary::getId).collect(Collectors.toList());
|
||||||
|
BulkOperationResults<String, TaskanaException> results =
|
||||||
|
taskanaEngine.getTaskService().setOwnerOfTasks("theWorkaholic", allTaskIds);
|
||||||
|
assertThat(allTaskSummaries.size()).isEqualTo(73);
|
||||||
|
assertThat(results.containsErrors()).isTrue();
|
||||||
|
assertThat(results.getErrorMap().size()).isEqualTo(48);
|
||||||
|
long numberOfInvalidStateExceptions =
|
||||||
|
results.getErrorMap().entrySet().stream()
|
||||||
|
.filter(
|
||||||
|
e ->
|
||||||
|
e.getValue()
|
||||||
|
.getClass()
|
||||||
|
.getName()
|
||||||
|
.equals(InvalidStateException.class.getCanonicalName()))
|
||||||
|
.count();
|
||||||
|
assertThat(numberOfInvalidStateExceptions).isEqualTo(25);
|
||||||
|
|
||||||
|
long numberOfNotAuthorizedExceptions =
|
||||||
|
results.getErrorMap().entrySet().stream()
|
||||||
|
.filter(
|
||||||
|
e ->
|
||||||
|
e.getValue()
|
||||||
|
.getClass()
|
||||||
|
.getName()
|
||||||
|
.equals(NotAuthorizedException.class.getCanonicalName()))
|
||||||
|
.count();
|
||||||
|
assertThat(numberOfNotAuthorizedExceptions).isEqualTo(23);
|
||||||
|
}
|
||||||
|
|
||||||
|
@WithAccessId(
|
||||||
|
userName = "admin",
|
||||||
|
groupNames = {"group_2"})
|
||||||
|
@Test
|
||||||
|
void testSetOwnerWithAllTasksAndVariousExceptionsAsAdmin()
|
||||||
|
throws NoSuchFieldException, IllegalAccessException, SQLException {
|
||||||
|
resetDb(false);
|
||||||
|
List<TaskSummary> allTaskSummaries = taskanaEngine.getTaskService().createTaskQuery().list();
|
||||||
|
List<String> allTaskIds =
|
||||||
|
allTaskSummaries.stream().map(TaskSummary::getId).collect(Collectors.toList());
|
||||||
|
BulkOperationResults<String, TaskanaException> results =
|
||||||
|
taskanaEngine.getTaskService().setOwnerOfTasks("theWorkaholic", allTaskIds);
|
||||||
|
assertThat(allTaskSummaries.size()).isEqualTo(73);
|
||||||
|
assertThat(results.containsErrors()).isTrue();
|
||||||
|
assertThat(results.getErrorMap().size()).isEqualTo(26);
|
||||||
|
long numberOfInvalidStateExceptions =
|
||||||
|
results.getErrorMap().entrySet().stream()
|
||||||
|
.filter(
|
||||||
|
e ->
|
||||||
|
e.getValue()
|
||||||
|
.getClass()
|
||||||
|
.getName()
|
||||||
|
.equals(InvalidStateException.class.getCanonicalName()))
|
||||||
|
.count();
|
||||||
|
assertThat(numberOfInvalidStateExceptions).isEqualTo(26);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Task setOwner(String taskReadyId, String anyUserName)
|
||||||
|
throws TaskNotFoundException, NotAuthorizedException, ClassificationNotFoundException,
|
||||||
|
InvalidArgumentException, ConcurrencyException, AttachmentPersistenceException,
|
||||||
|
InvalidStateException {
|
||||||
|
TaskService taskService = taskanaEngine.getTaskService();
|
||||||
|
Task task = taskService.getTask(taskReadyId);
|
||||||
|
task.setOwner(anyUserName);
|
||||||
|
task = taskService.updateTask(task);
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,7 +22,6 @@ import pro.taskana.security.WithAccessId;
|
||||||
import pro.taskana.task.api.TaskService;
|
import pro.taskana.task.api.TaskService;
|
||||||
import pro.taskana.task.api.TaskState;
|
import pro.taskana.task.api.TaskState;
|
||||||
import pro.taskana.task.api.exceptions.AttachmentPersistenceException;
|
import pro.taskana.task.api.exceptions.AttachmentPersistenceException;
|
||||||
import pro.taskana.task.api.exceptions.InvalidOwnerException;
|
|
||||||
import pro.taskana.task.api.exceptions.InvalidStateException;
|
import pro.taskana.task.api.exceptions.InvalidStateException;
|
||||||
import pro.taskana.task.api.exceptions.TaskAlreadyExistException;
|
import pro.taskana.task.api.exceptions.TaskAlreadyExistException;
|
||||||
import pro.taskana.task.api.exceptions.TaskNotFoundException;
|
import pro.taskana.task.api.exceptions.TaskNotFoundException;
|
||||||
|
@ -337,75 +336,4 @@ class UpdateTaskAccTest extends AbstractAccTest {
|
||||||
|
|
||||||
assertThat(retrievedUpdatedTask.getCallbackInfo()).isEqualTo(callbackInfo);
|
assertThat(retrievedUpdatedTask.getCallbackInfo()).isEqualTo(callbackInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@WithAccessId(
|
|
||||||
userName = "user_1_2",
|
|
||||||
groupNames = {"group_1"})
|
|
||||||
@Test
|
|
||||||
void testSetOwnerAndSubsequentClaimSucceeds()
|
|
||||||
throws TaskNotFoundException, NotAuthorizedException, InvalidStateException,
|
|
||||||
InvalidOwnerException, ClassificationNotFoundException, InvalidArgumentException,
|
|
||||||
ConcurrencyException, AttachmentPersistenceException {
|
|
||||||
|
|
||||||
TaskService taskService = taskanaEngine.getTaskService();
|
|
||||||
String taskReadyId = "TKI:000000000000000000000000000000000025";
|
|
||||||
Task taskReady = taskService.getTask(taskReadyId);
|
|
||||||
assertThat(taskReady.getState()).isEqualTo(TaskState.READY);
|
|
||||||
assertThat(taskReady.getOwner()).isNull();
|
|
||||||
String anyUserName = "Holger";
|
|
||||||
Task modifiedTaskReady = setOwner(taskReadyId, anyUserName);
|
|
||||||
|
|
||||||
assertThat(modifiedTaskReady.getState()).isEqualTo(TaskState.READY);
|
|
||||||
assertThat(modifiedTaskReady.getOwner()).isEqualTo(anyUserName);
|
|
||||||
Task taskClaimed = taskService.claim(taskReadyId);
|
|
||||||
assertThat(taskClaimed.getState()).isEqualTo(TaskState.CLAIMED);
|
|
||||||
assertThat(taskClaimed.getOwner()).isEqualTo("user_1_2");
|
|
||||||
}
|
|
||||||
|
|
||||||
@WithAccessId(
|
|
||||||
userName = "user_1_2",
|
|
||||||
groupNames = {"group_1"})
|
|
||||||
@Test
|
|
||||||
void testSetOwnerNotAuthorized()
|
|
||||||
throws TaskNotFoundException, NotAuthorizedException, InvalidStateException,
|
|
||||||
InvalidOwnerException {
|
|
||||||
|
|
||||||
TaskService taskService = taskanaEngine.getTaskService();
|
|
||||||
String taskReadyId = "TKI:000000000000000000000000000000000024";
|
|
||||||
String anyUserName = "Benjamin";
|
|
||||||
|
|
||||||
assertThatThrownBy(() -> taskService.getTask(taskReadyId))
|
|
||||||
.isInstanceOf(NotAuthorizedException.class);
|
|
||||||
assertThatThrownBy(() -> setOwner(taskReadyId, anyUserName))
|
|
||||||
.isInstanceOf(NotAuthorizedException.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@WithAccessId(
|
|
||||||
userName = "user_1_2",
|
|
||||||
groupNames = {"group_1"})
|
|
||||||
@Test
|
|
||||||
void testSetOwnerOfClaimedTaskFails()
|
|
||||||
throws TaskNotFoundException, NotAuthorizedException, InvalidStateException,
|
|
||||||
InvalidOwnerException {
|
|
||||||
|
|
||||||
TaskService taskService = taskanaEngine.getTaskService();
|
|
||||||
String taskClaimedId = "TKI:000000000000000000000000000000000026";
|
|
||||||
String anyUserName = "Mustapha";
|
|
||||||
Task taskClaimed = taskService.getTask(taskClaimedId);
|
|
||||||
assertThat(taskClaimed.getState()).isEqualTo(TaskState.CLAIMED);
|
|
||||||
assertThat(taskClaimed.getOwner()).isEqualTo("user_1_1");
|
|
||||||
assertThatThrownBy(() -> setOwner(taskClaimedId, anyUserName))
|
|
||||||
.isInstanceOf(InvalidStateException.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task setOwner(String taskReadyId, String anyUserName)
|
|
||||||
throws TaskNotFoundException, NotAuthorizedException, ClassificationNotFoundException,
|
|
||||||
InvalidArgumentException, ConcurrencyException, AttachmentPersistenceException,
|
|
||||||
InvalidStateException {
|
|
||||||
TaskService taskService = taskanaEngine.getTaskService();
|
|
||||||
Task task = taskService.getTask(taskReadyId);
|
|
||||||
task.setOwner(anyUserName);
|
|
||||||
task = taskService.updateTask(task);
|
|
||||||
return task;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue