Skip to content

Commit

Permalink
Closes Taskana#2496 : Set Owner of Tasks when Transferring
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesrdi committed Feb 20, 2024
1 parent 9666a8a commit b264fc7
Show file tree
Hide file tree
Showing 8 changed files with 405 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,100 @@ BulkOperationResults<String, TaskanaException> transferTasks(
WorkbasketNotFoundException,
NotAuthorizedOnWorkbasketException;

/**
* Transfers a List of {@linkplain Task Tasks} to another {@linkplain Workbasket} and set owner to
* {@param owner} while always setting {@linkplain Task#isTransferred isTransferred} to true.
*
* @see #transferTasksWithOwner(String, List, String, boolean)
*/
@SuppressWarnings("checkstyle:JavadocMethod")
default BulkOperationResults<String, TaskanaException> transferTasksWithOwner(
String destinationWorkbasketId, List<String> taskIds, String owner)
throws InvalidArgumentException,
WorkbasketNotFoundException,
NotAuthorizedOnWorkbasketException {
return transferTasksWithOwner(destinationWorkbasketId, taskIds, owner, true);
}

/**
* Transfers a List of {@linkplain Task Tasks} to another {@linkplain Workbasket} and set the
* owner.
*
* <p>The transfer resets {@linkplain Task#isRead() isRead} and sets {@linkplain
* Task#isTransferred() isTransferred} if setTransferFlag is true. Exceptions will be thrown if
* the caller got no {@linkplain WorkbasketPermission} on the target or if the target {@linkplain
* Workbasket} doesn't exist. Other Exceptions will be stored and returned in the end.
*
* @param destinationWorkbasketId {@linkplain Workbasket#getId() id} of the target {@linkplain
* Workbasket}
* @param taskIds List of source {@linkplain Task Tasks} which will be moved
* @param owner the new owner of the transferred tasks
* @param setTransferFlag controls whether to set {@linkplain Task#isTransferred() isTransferred}
* or not
* @return Bulkresult with {@linkplain Task#getId() ids} and Error for each failed transactions
* @throws NotAuthorizedOnWorkbasketException if the current user has no {@linkplain
* WorkbasketPermission#READ} for the source {@linkplain Workbasket} or no {@linkplain
* WorkbasketPermission#TRANSFER} for the target {@linkplain Workbasket}
* @throws InvalidArgumentException if the method parameters are empty or NULL
* @throws WorkbasketNotFoundException if the target {@linkplain Workbasket} can't be found
*/
BulkOperationResults<String, TaskanaException> transferTasksWithOwner(
String destinationWorkbasketId, List<String> taskIds, String owner, boolean setTransferFlag)
throws InvalidArgumentException,
WorkbasketNotFoundException,
NotAuthorizedOnWorkbasketException;

/**
* Transfers a List of {@linkplain Task Tasks} to another {@linkplain Workbasket} and set owner
* while always setting {@linkplain Task#isTransferred() isTransferred} to true.
*
* @see #transferTasksWithOwner(String, String, List, String, boolean)
*/
@SuppressWarnings("checkstyle:JavadocMethod")
default BulkOperationResults<String, TaskanaException> transferTasksWithOwner(
String destinationWorkbasketKey,
String destinationWorkbasketDomain,
List<String> taskIds,
String owner)
throws InvalidArgumentException,
WorkbasketNotFoundException,
NotAuthorizedOnWorkbasketException {
return transferTasksWithOwner(
destinationWorkbasketKey, destinationWorkbasketDomain, taskIds, owner, true);
}

/**
* Transfers a List of {@linkplain Task Tasks} to another {@linkplain Workbasket} and set owner.
*
* <p>The transfer resets {@linkplain Task#isRead() isRead} and sets {@linkplain
* Task#isTransferred() isTransferred} if setTransferFlag is true. Exceptions will be thrown if
* the caller got no {@linkplain WorkbasketPermission} on the target {@linkplain Workbasket} or if
* it doesn't exist. Other Exceptions will be stored and returned in the end.
*
* @param destinationWorkbasketKey target {@linkplain Workbasket#getKey() key}
* @param destinationWorkbasketDomain target {@linkplain Workbasket#getDomain() domain}
* @param taskIds List of {@linkplain Task#getId() ids} of source {@linkplain Task Tasks} which
* will be moved
* @param owner the new owner of the transferred tasks
* @param setTransferFlag controls whether to set {@linkplain Task#isTransferred() isTransferred}
* or not
* @return BulkResult with {@linkplain Task#getId() ids} and Error for each failed transactions
* @throws NotAuthorizedOnWorkbasketException if the current user has no {@linkplain
* WorkbasketPermission#READ} for the source {@linkplain Workbasket} or no {@linkplain
* WorkbasketPermission#TRANSFER} for the target {@linkplain Workbasket}
* @throws InvalidArgumentException if the method parameters are empty or NULL
* @throws WorkbasketNotFoundException if the target {@linkplain Workbasket} can't be found
*/
BulkOperationResults<String, TaskanaException> transferTasksWithOwner(
String destinationWorkbasketKey,
String destinationWorkbasketDomain,
List<String> taskIds,
String owner,
boolean setTransferFlag)
throws InvalidArgumentException,
WorkbasketNotFoundException,
NotAuthorizedOnWorkbasketException;

/**
* Update a {@linkplain Task}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,30 @@ public BulkOperationResults<String, TaskanaException> transferTasks(
taskIds, destinationWorkbasketKey, destinationWorkbasketDomain, setTransferFlag);
}

@Override
public BulkOperationResults<String, TaskanaException> transferTasksWithOwner(
String destinationWorkbasketId, List<String> taskIds, String owner, boolean setTransferFlag)
throws InvalidArgumentException,
WorkbasketNotFoundException,
NotAuthorizedOnWorkbasketException {
return taskTransferrer.transferTasksWithOwner(
taskIds, destinationWorkbasketId, owner, setTransferFlag);
}

@Override
public BulkOperationResults<String, TaskanaException> transferTasksWithOwner(
String destinationWorkbasketKey,
String destinationWorkbasketDomain,
List<String> taskIds,
String owner,
boolean setTransferFlag)
throws InvalidArgumentException,
WorkbasketNotFoundException,
NotAuthorizedOnWorkbasketException {
return taskTransferrer.transferTasksWithOwner(
taskIds, destinationWorkbasketKey, destinationWorkbasketDomain, owner, setTransferFlag);
}

@Override
public void deleteTask(String taskId)
throws TaskNotFoundException,
Expand Down Expand Up @@ -707,7 +731,7 @@ public BulkOperationResults<String, TaskanaException> deleteTasks(List<String> t
}
if (historyEventManager.isEnabled()) {
taskIds.forEach(this::createTaskDeletedEvent);
}
}
}
return bulkLog;
} finally {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ BulkOperationResults<String, TaskanaException> transfer(
workbasketService.getWorkbasket(destinationWorkbasketId).asSummary();
checkDestinationWorkbasket(destinationWorkbasket);

return transferMultipleTasks(taskIds, destinationWorkbasket, setTransferFlag);
return transferMultipleTasks(taskIds, destinationWorkbasket, null, setTransferFlag);
}

BulkOperationResults<String, TaskanaException> transfer(
Expand All @@ -101,7 +101,35 @@ BulkOperationResults<String, TaskanaException> transfer(
workbasketService.getWorkbasket(destinationWorkbasketKey, destinationDomain).asSummary();
checkDestinationWorkbasket(destinationWorkbasket);

return transferMultipleTasks(taskIds, destinationWorkbasket, setTransferFlag);
return transferMultipleTasks(taskIds, destinationWorkbasket, null, setTransferFlag);
}

BulkOperationResults<String, TaskanaException> transferTasksWithOwner(
List<String> taskIds, String destinationWorkbasketId, String owner, boolean setTransferFlag)
throws WorkbasketNotFoundException,
InvalidArgumentException,
NotAuthorizedOnWorkbasketException {
WorkbasketSummary destinationWorkbasket =
workbasketService.getWorkbasket(destinationWorkbasketId).asSummary();
checkDestinationWorkbasket(destinationWorkbasket);

return transferMultipleTasks(taskIds, destinationWorkbasket, owner, setTransferFlag);
}

BulkOperationResults<String, TaskanaException> transferTasksWithOwner(
List<String> taskIds,
String destinationWorkbasketKey,
String destinationDomain,
String owner,
boolean setTransferFlag)
throws WorkbasketNotFoundException,
InvalidArgumentException,
NotAuthorizedOnWorkbasketException {
WorkbasketSummary destinationWorkbasket =
workbasketService.getWorkbasket(destinationWorkbasketKey, destinationDomain).asSummary();
checkDestinationWorkbasket(destinationWorkbasket);

return transferMultipleTasks(taskIds, destinationWorkbasket, owner, setTransferFlag);
}

private Task transferSingleTask(
Expand All @@ -120,7 +148,7 @@ private Task transferSingleTask(
WorkbasketSummary originWorkbasket = task.getWorkbasketSummary();
checkPreconditionsForTransferTask(task, destinationWorkbasket, originWorkbasket);

applyTransferValuesForTask(task, destinationWorkbasket, setTransferFlag);
applyTransferValuesForTask(task, destinationWorkbasket, null, setTransferFlag);
taskMapper.update(task);
if (historyEventManager.isEnabled()) {
createTransferredEvent(
Expand All @@ -136,6 +164,7 @@ private Task transferSingleTask(
private BulkOperationResults<String, TaskanaException> transferMultipleTasks(
List<String> taskToBeTransferred,
WorkbasketSummary destinationWorkbasket,
String owner,
boolean setTransferFlag)
throws InvalidArgumentException {
if (taskToBeTransferred == null || taskToBeTransferred.isEmpty()) {
Expand All @@ -154,7 +183,7 @@ private BulkOperationResults<String, TaskanaException> transferMultipleTasks(
() -> taskService.createTaskQuery().idIn(taskIds.toArray(new String[0])).list());
taskSummaries =
filterOutTasksWhichDoNotMatchTransferCriteria(taskIds, taskSummaries, bulkLog);
updateTransferableTasks(taskSummaries, destinationWorkbasket, setTransferFlag);
updateTransferableTasks(taskSummaries, destinationWorkbasket, owner, setTransferFlag);

return bulkLog;
} finally {
Expand Down Expand Up @@ -254,6 +283,7 @@ private Set<String> getSourceWorkbasketIdsWithTransferPermission(
private void updateTransferableTasks(
List<TaskSummary> taskSummaries,
WorkbasketSummary destinationWorkbasket,
String owner,
boolean setTransferFlag) {
Map<TaskState, List<TaskSummary>> summariesByState = groupTasksByState(taskSummaries);
for (Map.Entry<TaskState, List<TaskSummary>> entry : summariesByState.entrySet()) {
Expand All @@ -262,7 +292,7 @@ private void updateTransferableTasks(
if (!taskSummariesWithSameGoalState.isEmpty()) {
TaskImpl updateObject = new TaskImpl();
updateObject.setState(goalState);
applyTransferValuesForTask(updateObject, destinationWorkbasket, setTransferFlag);
applyTransferValuesForTask(updateObject, destinationWorkbasket, owner, setTransferFlag);
taskMapper.updateTransfered(
taskSummariesWithSameGoalState.stream()
.map(TaskSummary::getId)
Expand All @@ -275,7 +305,8 @@ private void updateTransferableTasks(
TaskSummaryImpl newSummary = (TaskSummaryImpl) oldSummary.copy();
newSummary.setId(oldSummary.getId());
newSummary.setExternalId(oldSummary.getExternalId());
applyTransferValuesForTask(newSummary, destinationWorkbasket, setTransferFlag);
applyTransferValuesForTask(
newSummary, destinationWorkbasket, owner, setTransferFlag);

createTransferredEvent(
oldSummary,
Expand All @@ -289,11 +320,11 @@ private void updateTransferableTasks(
}

private void applyTransferValuesForTask(
TaskSummaryImpl task, WorkbasketSummary workbasket, boolean setTransferFlag) {
TaskSummaryImpl task, WorkbasketSummary workbasket, String owner, boolean setTransferFlag) {
task.setRead(false);
task.setTransferred(setTransferFlag);
task.setState(getStateAfterTransfer(task));
task.setOwner(null);
task.setOwner(owner);
task.setWorkbasketSummary(workbasket);
task.setDomain(workbasket.getDomain());
task.setModified(Instant.now());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.catchThrowableOfType;

import acceptance.AbstractAccTest;
import java.time.Instant;
Expand All @@ -29,6 +30,7 @@
import pro.taskana.task.api.exceptions.TaskNotFoundException;
import pro.taskana.task.api.models.Task;
import pro.taskana.task.api.models.TaskSummary;
import pro.taskana.workbasket.api.WorkbasketPermission;
import pro.taskana.workbasket.api.exceptions.NotAuthorizedOnWorkbasketException;
import pro.taskana.workbasket.api.exceptions.WorkbasketNotFoundException;
import pro.taskana.workbasket.api.models.Workbasket;
Expand Down Expand Up @@ -435,4 +437,101 @@ void should_NotSetTheTransferFlagWithinBulkTransfer_When_SetTransferFlagNotReque

assertThat(transferredTasks).extracting(TaskSummary::isTransferred).containsOnly(false);
}

@WithAccessId(user = "teamlead-1", groups = GROUP_1_DN)
@Test
void should_BulkTransferOnlyValidTasksAndSetOwner_When_SomeTasksToTransferCauseExceptions()
throws Exception {
final Workbasket wb =
taskanaEngine
.getWorkbasketService()
.getWorkbasket("WBI:100000000000000000000000000000000009");
final Instant before = Instant.now().truncatedTo(ChronoUnit.MILLIS);
List<String> taskIdList =
Arrays.asList(
"TKI:000000000000000000000000000000000007", // working
"TKI:000000000000000000000000000000000041", // NotAuthorized READ
"TKI:000000000000000000000000000000000041", // NotAuthorized READ
"TKI:200000000000000000000000000000000008", // NotAuthorized TRANSFER
"", // InvalidArgument
null, // InvalidArgument
"TKI:000000000000000000000000000000000099", // not existing
"TKI:100000000000000000000000000000000006"); // already completed

BulkOperationResults<String, TaskanaException> results =
taskService.transferTasksWithOwner(
"WBI:100000000000000000000000000000000009", taskIdList, "teamlead-1");

// check for exceptions in bulk
assertThat(results.containsErrors()).isTrue();
assertThat(results.getErrorMap().values()).hasSize(6);
assertThat(results.getErrorForId("TKI:000000000000000000000000000000000041").getClass())
.isEqualTo(NotAuthorizedOnWorkbasketException.class);
assertThat(results.getErrorForId("TKI:200000000000000000000000000000000008").getClass())
.isEqualTo(NotAuthorizedOnWorkbasketException.class);
assertThat(results.getErrorForId("TKI:000000000000000000000000000000000099").getClass())
.isEqualTo(TaskNotFoundException.class);
assertThat(results.getErrorForId("TKI:100000000000000000000000000000000006").getClass())
.isEqualTo(InvalidTaskStateException.class);
assertThat(results.getErrorForId("").getClass()).isEqualTo(TaskNotFoundException.class);
assertThat(results.getErrorForId(null).getClass()).isEqualTo(TaskNotFoundException.class);

// verify owner follows new workbasket permission and valid requests
Task transferredTask = taskService.getTask("TKI:000000000000000000000000000000000007");
assertThat(transferredTask).isNotNull();
assertThat(transferredTask.isTransferred()).isTrue();
assertThat(transferredTask.isRead()).isFalse();
assertThat(transferredTask.getState()).isEqualTo(TaskState.READY);
assertThat(transferredTask.getWorkbasketKey()).isEqualTo(wb.getKey());
assertThat(transferredTask.getDomain()).isEqualTo(wb.getDomain());
assertThat(transferredTask.getModified().isBefore(before)).isFalse();
assertThat(transferredTask.getOwner()).isEqualTo("teamlead-1");

// verify owner follows new workbasket permission
ThrowingCallable call =
() ->
taskService.transfer(
"TKI:000000000000000000000000000000000007",
"WBI:100000000000000000000000000000000001");
NotAuthorizedOnWorkbasketException e =
catchThrowableOfType(call, NotAuthorizedOnWorkbasketException.class);
assertThat(e.getRequiredPermissions()).containsExactly(WorkbasketPermission.TRANSFER);
assertThat(e.getCurrentUserId()).isEqualTo("teamlead-1");
assertThat(e.getWorkbasketId()).isEqualTo("WBI:100000000000000000000000000000000009");
}

@WithAccessId(user = "teamlead-1", groups = GROUP_1_DN)
@Test
void should_BulkTransferTasksAndSetOwner_When_WorkbasketKeyAndDomainIsProvided()
throws Exception {
final Instant before = Instant.now().truncatedTo(ChronoUnit.MILLIS);
List<String> taskIdList =
List.of(
"TKI:000000000000000000000000000000000008", "TKI:000000000000000000000000000000000009");

BulkOperationResults<String, TaskanaException> results =
taskService.transferTasksWithOwner("GPK_B_KSC_1", "DOMAIN_B", taskIdList, "teamlead-1");
assertThat(results.containsErrors()).isFalse();

final Workbasket wb =
taskanaEngine.getWorkbasketService().getWorkbasket("GPK_B_KSC_1", "DOMAIN_B");
Task transferredTask = taskService.getTask("TKI:000000000000000000000000000000000008");
assertThat(transferredTask).isNotNull();
assertThat(transferredTask.isTransferred()).isTrue();
assertThat(transferredTask.isRead()).isFalse();
assertThat(transferredTask.getState()).isEqualTo(TaskState.READY);
assertThat(transferredTask.getWorkbasketKey()).isEqualTo(wb.getKey());
assertThat(transferredTask.getDomain()).isEqualTo(wb.getDomain());
assertThat(transferredTask.getModified().isBefore(before)).isFalse();
assertThat(transferredTask.getOwner()).isEqualTo("teamlead-1");
transferredTask = taskService.getTask("TKI:000000000000000000000000000000000009");
assertThat(transferredTask).isNotNull();
assertThat(transferredTask.isTransferred()).isTrue();
assertThat(transferredTask.isRead()).isFalse();
assertThat(transferredTask.getState()).isEqualTo(TaskState.READY);
assertThat(transferredTask.getWorkbasketKey()).isEqualTo(wb.getKey());
assertThat(transferredTask.getDomain()).isEqualTo(wb.getDomain());
assertThat(transferredTask.getModified().isBefore(before)).isFalse();
assertThat(transferredTask.getOwner()).isEqualTo("teamlead-1");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public final class RestEndpoints {
public static final String URL_TASKS_ID_TERMINATE = API_V1 + "tasks/{taskId}/terminate";
public static final String URL_TASKS_ID_TRANSFER_WORKBASKET_ID =
API_V1 + "tasks/{taskId}/transfer/{workbasketId}";
public static final String URL_TRANSFER_WORKBASKET_ID = API_V1 + "tasks/transfer/{workbasketId}";
public static final String URL_TASKS_ID_SET_READ = API_V1 + "tasks/{taskId}/set-read";

// task comment endpoints
Expand Down
Loading

0 comments on commit b264fc7

Please sign in to comment.