From 072d1e0dd147fd390291aa64ee2303cde444f27e Mon Sep 17 00:00:00 2001 From: jamesrdi Date: Tue, 6 Feb 2024 16:22:24 +0100 Subject: [PATCH] Closes #2491: Set Owner of Task when Transferring --- ...eateHistoryEventOnTaskTransferAccTest.java | 150 ++++++++++++ .../pro/taskana/task/api/TaskService.java | 211 +++++++++++++++++ .../task/internal/TaskServiceImpl.java | 45 ++++ .../task/internal/TaskTransferrer.java | 79 ++++++- .../task/transfer/TransferTaskAccTest.java | 216 ++++++++++++++++++ .../taskana/common/rest/RestEndpoints.java | 1 + .../pro/taskana/task/rest/TaskController.java | 86 ++++++- .../TransferTaskRepresentationModel.java | 40 ++++ .../task/rest/TaskControllerIntTest.java | 104 ++++++--- .../task/rest/TaskControllerRestDocTest.java | 27 ++- 10 files changed, 902 insertions(+), 57 deletions(-) create mode 100644 rest/taskana-rest-spring/src/main/java/pro/taskana/task/rest/models/TransferTaskRepresentationModel.java diff --git a/history/taskana-simplehistory-provider/src/test/java/acceptance/events/task/CreateHistoryEventOnTaskTransferAccTest.java b/history/taskana-simplehistory-provider/src/test/java/acceptance/events/task/CreateHistoryEventOnTaskTransferAccTest.java index 35d2770e2e..6bcbdb20b8 100644 --- a/history/taskana-simplehistory-provider/src/test/java/acceptance/events/task/CreateHistoryEventOnTaskTransferAccTest.java +++ b/history/taskana-simplehistory-provider/src/test/java/acceptance/events/task/CreateHistoryEventOnTaskTransferAccTest.java @@ -181,6 +181,156 @@ Stream should_CreateTransferredHistoryEvents_When_TaskBulkTransfer( return DynamicTest.stream(testCases.iterator(), Triplet::getLeft, test); } + @WithAccessId(user = "admin") + @TestFactory + Stream should_CreateTransferredHistoryEvent_When_TaskIsTransferredWithOwner() { + List>> testCases = + List.of( + /* + The workbasketId of the source Workbasket is parametrized. Putting the tested Tasks + into the same Workbasket would result in changes to the test data. This would require + changing tests that already use the tested Tasks. That's why workbasketId is + parametrized. + */ + Quadruple.of( + "Using WorkbasketId; Task doesn't have an Attachment" + + " or any secondary Object References", + "TKI:000000000000000000000000000000000005", + "WBI:100000000000000000000000000000000001", + wrap( + (String taskId) -> + taskService.transferWithOwner( + taskId, "WBI:100000000000000000000000000000000007", "USER-1-2"))), + Quadruple.of( + "Using WorkbasketId; Task has Attachment and secondary Object Reference", + "TKI:000000000000000000000000000000000001", + "WBI:100000000000000000000000000000000006", + wrap( + (String taskId) -> + taskService.transferWithOwner( + taskId, "WBI:100000000000000000000000000000000007", "USER-1-2"))), + Quadruple.of( + "Using WorkbasketKey and Domain", + "TKI:000000000000000000000000000000000006", + "WBI:100000000000000000000000000000000001", + wrap( + (String taskId) -> + taskService.transferWithOwner( + taskId, "USER-1-2", "DOMAIN_A", "USER-1-2")))); + ThrowingConsumer>> test = + q -> { + String taskId = q.getSecond(); + Consumer transferMethod = q.getFourth(); + + TaskHistoryQueryMapper taskHistoryQueryMapper = getHistoryQueryMapper(); + + List events = + taskHistoryQueryMapper.queryHistoryEvents( + (TaskHistoryQueryImpl) historyService.createTaskHistoryQuery().taskIdIn(taskId)); + + assertThat(events).isEmpty(); + + transferMethod.accept(taskId); + + events = + taskHistoryQueryMapper.queryHistoryEvents( + (TaskHistoryQueryImpl) historyService.createTaskHistoryQuery().taskIdIn(taskId)); + + assertThat(events).hasSize(1); + String sourceWorkbasketId = q.getThird(); + assertTransferHistoryEvent( + events.get(0).getId(), + sourceWorkbasketId, + "WBI:100000000000000000000000000000000007", + "admin"); + }; + return DynamicTest.stream(testCases.iterator(), Quadruple::getFirst, test); + } + + @WithAccessId(user = "admin") + @TestFactory + Stream should_CreateTransferredHistoryEvents_When_TaskBulkTransferWithOwner() { + List, Consumer>>> testCases = + List.of( + /* + The workbasketId of the source Workbasket is parametrized. Putting the tested Tasks + into the same Workbasket would result in changes to the test data. This would require + changing tests that already use the tested Tasks. That's why workbasketId is + parametrized. + */ + Triplet.of( + "Using WorkbasketId", + Map.ofEntries( + Map.entry( + "TKI:000000000000000000000000000000000010", + "WBI:100000000000000000000000000000000001"), + Map.entry( + "TKI:000000000000000000000000000000000011", + "WBI:100000000000000000000000000000000001"), + Map.entry( + "TKI:000000000000000000000000000000000012", + "WBI:100000000000000000000000000000000001")), + wrap( + (List taskIds) -> + taskService.transferTasksWithOwner( + "WBI:100000000000000000000000000000000007", taskIds, "user-1-2"))), + Triplet.of( + "Using WorkbasketKey and Domain", + Map.ofEntries( + Map.entry( + "TKI:000000000000000000000000000000000013", + "WBI:100000000000000000000000000000000001"), + Map.entry( + "TKI:000000000000000000000000000000000014", + "WBI:100000000000000000000000000000000001"), + Map.entry( + "TKI:000000000000000000000000000000000015", + "WBI:100000000000000000000000000000000001")), + wrap( + (List taskIds) -> + taskService.transferTasksWithOwner( + "USER-1-2", "DOMAIN_A", taskIds, "USER-1-2")))); + ThrowingConsumer, Consumer>>> test = + t -> { + Map taskIds = t.getMiddle(); + Consumer> transferMethod = t.getRight(); + + TaskHistoryQueryMapper taskHistoryQueryMapper = getHistoryQueryMapper(); + + List events = + taskHistoryQueryMapper.queryHistoryEvents( + (TaskHistoryQueryImpl) + historyService + .createTaskHistoryQuery() + .taskIdIn(taskIds.keySet().toArray(new String[0]))); + + assertThat(events).isEmpty(); + + transferMethod.accept(new ArrayList<>(taskIds.keySet())); + + events = + taskHistoryQueryMapper.queryHistoryEvents( + (TaskHistoryQueryImpl) + historyService + .createTaskHistoryQuery() + .taskIdIn(taskIds.keySet().toArray(new String[0]))); + + assertThat(events) + .extracting(TaskHistoryEvent::getTaskId) + .containsExactlyInAnyOrderElementsOf(taskIds.keySet()); + + for (TaskHistoryEvent event : events) { + assertTransferHistoryEvent( + event.getId(), + taskIds.get(event.getTaskId()), + "WBI:100000000000000000000000000000000007", + "admin"); + } + }; + + return DynamicTest.stream(testCases.iterator(), Triplet::getLeft, test); + } + private void assertTransferHistoryEvent( String eventId, String expectedOldValue, String expectedNewValue, String expectedUser) throws Exception { diff --git a/lib/taskana-core/src/main/java/pro/taskana/task/api/TaskService.java b/lib/taskana-core/src/main/java/pro/taskana/task/api/TaskService.java index d83214238c..e9147939f4 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/task/api/TaskService.java +++ b/lib/taskana-core/src/main/java/pro/taskana/task/api/TaskService.java @@ -471,6 +471,11 @@ Task terminateTask(String taskId) * Transfers a {@linkplain Task} to another {@linkplain Workbasket} while always setting * {@linkplain Task#isTransferred() isTransferred} to true. * + * @param taskId the {@linkplain Task#getId() id} of the {@linkplain Task} which should be + * transferred + * @param destinationWorkbasketId the {@linkplain Workbasket#getId() id} of the target {@linkplain + * Workbasket} + * @return the transferred {@linkplain Task} * @see #transfer(String, String, boolean) */ @SuppressWarnings("checkstyle:JavadocMethod") @@ -513,6 +518,13 @@ Task transfer(String taskId, String destinationWorkbasketId, boolean setTransfer * Transfers a {@linkplain Task} to another {@linkplain Workbasket} while always setting * {@linkplain Task#isTransferred isTransferred} . * + * @param taskId the {@linkplain Task#getId() id} of the {@linkplain Task} which should be + * transferred + * @param workbasketKey the {@linkplain Workbasket#getKey() key} of the target {@linkplain + * Workbasket} + * @param domain the {@linkplain Workbasket#getDomain() domain} of the target {@linkplain + * Workbasket} + * @return the transferred {@linkplain Task} * @see #transfer(String, String, String, boolean) */ @SuppressWarnings("checkstyle:JavadocMethod") @@ -553,6 +565,111 @@ Task transfer(String taskId, String workbasketKey, String domain, boolean setTra NotAuthorizedOnWorkbasketException, InvalidTaskStateException; + /** + * Transfers a {@linkplain Task} to another {@linkplain Workbasket} and set owner of {@linkplain + * Task} in the new {@linkplain Workbasket} to {@param owner} while always setting {@linkplain + * Task#isTransferred() isTransferred} to true. + * + * @param taskId the {@linkplain Task#getId() id} of the {@linkplain Task} which should be + * transferred + * @param destinationWorkbasketId the {@linkplain Workbasket#getId() id} of the target {@linkplain + * Workbasket} + * @param owner the owner of the transferred {@linkplain Task} in the new workbasket + * @return the transferred {@linkplain Task} + * @see #transferWithOwner(String, String, String, boolean) + */ + @SuppressWarnings("checkstyle:JavadocMethod") + default Task transferWithOwner(String taskId, String destinationWorkbasketId, String owner) + throws TaskNotFoundException, + WorkbasketNotFoundException, + NotAuthorizedOnWorkbasketException, + InvalidTaskStateException { + return transferWithOwner(taskId, destinationWorkbasketId, owner, true); + } + + /** + * Transfers a {@linkplain Task} to another {@linkplain Workbasket} and set owner. + * + *

The transfer resets {@linkplain Task#isRead() isRead} and sets {@linkplain + * Task#isTransferred() isTransferred} if setTransferFlag is true. + * + * @param taskId the {@linkplain Task#getId() id} of the {@linkplain Task} which should be + * transferred + * @param destinationWorkbasketId the {@linkplain Workbasket#getId() id} of the target {@linkplain + * Workbasket} + * @param owner the owner of the transferred {@linkplain Task} in the new workbasket + * @param setTransferFlag controls whether to set {@linkplain Task#isTransferred() isTransferred} + * to true or not + * @return the transferred {@linkplain Task} + * @throws TaskNotFoundException if the {@linkplain Task} with taskId wasn't found + * @throws WorkbasketNotFoundException if the target {@linkplain Workbasket} was not found + * @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 InvalidTaskStateException if the {@linkplain Task} is in one of the {@linkplain + * TaskState#END_STATES} + */ + Task transferWithOwner( + String taskId, String destinationWorkbasketId, String owner, boolean setTransferFlag) + throws TaskNotFoundException, + WorkbasketNotFoundException, + NotAuthorizedOnWorkbasketException, + InvalidTaskStateException; + + /** + * Transfers a {@linkplain Task} to another {@linkplain Workbasket} and set owner of {@linkplain + * Task} in new {@linkplain Workbasket} to {@param owner} while always setting {@linkplain + * Task#isTransferred isTransferred} . + * + * @param taskId the {@linkplain Task#getId() id} of the {@linkplain Task} which should be + * transferred + * @param workbasketKey the {@linkplain Workbasket#getKey() key} of the target {@linkplain + * Workbasket} + * @param domain the {@linkplain Workbasket#getDomain() domain} of the target {@linkplain + * Workbasket} + * @param owner the owner of the transferred {@linkplain Task} in the new workbasket + * @see #transferWithOwner(String, String, String, String, boolean) + */ + @SuppressWarnings("checkstyle:JavadocMethod") + default Task transferWithOwner(String taskId, String workbasketKey, String domain, String owner) + throws TaskNotFoundException, + WorkbasketNotFoundException, + NotAuthorizedOnWorkbasketException, + InvalidTaskStateException { + return transferWithOwner(taskId, workbasketKey, domain, owner, true); + } + + /** + * Transfers a {@linkplain Task} to another {@linkplain Workbasket} and set owner. + * + *

The transfer resets {@linkplain Task#isRead() isRead} and sets {@linkplain + * Task#isTransferred() isTransferred} if setTransferFlag is true. + * + * @param taskId the {@linkplain Task#getId() id} of the {@linkplain Task} which should be + * transferred + * @param workbasketKey the {@linkplain Workbasket#getKey() key} of the target {@linkplain + * Workbasket} + * @param domain the {@linkplain Workbasket#getDomain() domain} of the target {@linkplain + * Workbasket} + * @param owner the owner of the transferred {@linkplain Task} in the new workbasket + * @param setTransferFlag controls whether to set {@linkplain Task#isTransferred() isTransferred} + * or not + * @return the transferred {@linkplain Task} + * @throws TaskNotFoundException if the {@linkplain Task} with taskId was not found + * @throws WorkbasketNotFoundException if the target {@linkplain Workbasket} was not found + * @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 InvalidTaskStateException if the {@linkplain Task} is in one of the {@linkplain + * TaskState#END_STATES} + */ + Task transferWithOwner( + String taskId, String workbasketKey, String domain, String owner, boolean setTransferFlag) + throws TaskNotFoundException, + WorkbasketNotFoundException, + NotAuthorizedOnWorkbasketException, + InvalidTaskStateException; + /** * Transfers a List of {@linkplain Task Tasks} to another {@linkplain Workbasket} while always * setting {@linkplain Task#isTransferred isTransferred} to true. @@ -639,6 +756,100 @@ BulkOperationResults 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 transferTasksWithOwner( + String destinationWorkbasketId, List 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. + * + *

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 transferTasksWithOwner( + String destinationWorkbasketId, List 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 transferTasksWithOwner( + String destinationWorkbasketKey, + String destinationWorkbasketDomain, + List 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. + * + *

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 transferTasksWithOwner( + String destinationWorkbasketKey, + String destinationWorkbasketDomain, + List taskIds, + String owner, + boolean setTransferFlag) + throws InvalidArgumentException, + WorkbasketNotFoundException, + NotAuthorizedOnWorkbasketException; + /** * Update a {@linkplain Task}. * diff --git a/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskServiceImpl.java b/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskServiceImpl.java index 08e3baaac2..cc81bd4c1b 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskServiceImpl.java +++ b/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskServiceImpl.java @@ -474,6 +474,27 @@ public Task transfer(String taskId, String workbasketKey, String domain, boolean return taskTransferrer.transfer(taskId, workbasketKey, domain, setTransferFlag); } + @Override + public Task transferWithOwner( + String taskId, String destinationWorkbasketId, String owner, boolean setTransferFlag) + throws TaskNotFoundException, + WorkbasketNotFoundException, + NotAuthorizedOnWorkbasketException, + InvalidTaskStateException { + return taskTransferrer.transferWithOwner( + taskId, destinationWorkbasketId, owner, setTransferFlag); + } + + @Override + public Task transferWithOwner( + String taskId, String workbasketKey, String domain, String owner, boolean setTransferFlag) + throws TaskNotFoundException, + WorkbasketNotFoundException, + NotAuthorizedOnWorkbasketException, + InvalidTaskStateException { + return taskTransferrer.transferWithOwner(taskId, workbasketKey, domain, owner, setTransferFlag); + } + @Override public Task setTaskRead(String taskId, boolean isRead) throws TaskNotFoundException, NotAuthorizedOnWorkbasketException { @@ -631,6 +652,30 @@ public BulkOperationResults transferTasks( taskIds, destinationWorkbasketKey, destinationWorkbasketDomain, setTransferFlag); } + @Override + public BulkOperationResults transferTasksWithOwner( + String destinationWorkbasketId, List taskIds, String owner, boolean setTransferFlag) + throws InvalidArgumentException, + WorkbasketNotFoundException, + NotAuthorizedOnWorkbasketException { + return taskTransferrer.transferTasksWithOwner( + taskIds, destinationWorkbasketId, owner, setTransferFlag); + } + + @Override + public BulkOperationResults transferTasksWithOwner( + String destinationWorkbasketKey, + String destinationWorkbasketDomain, + List 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, diff --git a/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskTransferrer.java b/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskTransferrer.java index e84c540b56..8ec74c9a18 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskTransferrer.java +++ b/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskTransferrer.java @@ -60,7 +60,7 @@ Task transfer(String taskId, String destinationWorkbasketId, boolean setTransfer InvalidTaskStateException { WorkbasketSummary destinationWorkbasket = workbasketService.getWorkbasket(destinationWorkbasketId).asSummary(); - return transferSingleTask(taskId, destinationWorkbasket, setTransferFlag); + return transferSingleTask(taskId, destinationWorkbasket, null, setTransferFlag); } Task transfer( @@ -74,7 +74,7 @@ Task transfer( InvalidTaskStateException { WorkbasketSummary destinationWorkbasket = workbasketService.getWorkbasket(destinationWorkbasketKey, destinationDomain).asSummary(); - return transferSingleTask(taskId, destinationWorkbasket, setTransferFlag); + return transferSingleTask(taskId, destinationWorkbasket, null, setTransferFlag); } BulkOperationResults transfer( @@ -86,7 +86,7 @@ BulkOperationResults transfer( workbasketService.getWorkbasket(destinationWorkbasketId).asSummary(); checkDestinationWorkbasket(destinationWorkbasket); - return transferMultipleTasks(taskIds, destinationWorkbasket, setTransferFlag); + return transferMultipleTasks(taskIds, destinationWorkbasket, null, setTransferFlag); } BulkOperationResults transfer( @@ -101,11 +101,65 @@ BulkOperationResults transfer( workbasketService.getWorkbasket(destinationWorkbasketKey, destinationDomain).asSummary(); checkDestinationWorkbasket(destinationWorkbasket); - return transferMultipleTasks(taskIds, destinationWorkbasket, setTransferFlag); + return transferMultipleTasks(taskIds, destinationWorkbasket, null, setTransferFlag); + } + + BulkOperationResults transferTasksWithOwner( + List 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 transferTasksWithOwner( + List 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); + } + + Task transferWithOwner( + String taskId, String destinationWorkbasketId, String owner, boolean setTransferFlag) + throws TaskNotFoundException, + WorkbasketNotFoundException, + NotAuthorizedOnWorkbasketException, + InvalidTaskStateException { + WorkbasketSummary destinationWorkbasket = + workbasketService.getWorkbasket(destinationWorkbasketId).asSummary(); + return transferSingleTask(taskId, destinationWorkbasket, owner, setTransferFlag); + } + + Task transferWithOwner( + String taskId, + String destinationWorkbasketKey, + String destinationDomain, + String owner, + boolean setTransferFlag) + throws TaskNotFoundException, + WorkbasketNotFoundException, + NotAuthorizedOnWorkbasketException, + InvalidTaskStateException { + WorkbasketSummary destinationWorkbasket = + workbasketService.getWorkbasket(destinationWorkbasketKey, destinationDomain).asSummary(); + return transferSingleTask(taskId, destinationWorkbasket, owner, setTransferFlag); } private Task transferSingleTask( - String taskId, WorkbasketSummary destinationWorkbasket, boolean setTransferFlag) + String taskId, WorkbasketSummary destinationWorkbasket, String owner, boolean setTransferFlag) throws TaskNotFoundException, WorkbasketNotFoundException, NotAuthorizedOnWorkbasketException, @@ -120,7 +174,7 @@ private Task transferSingleTask( WorkbasketSummary originWorkbasket = task.getWorkbasketSummary(); checkPreconditionsForTransferTask(task, destinationWorkbasket, originWorkbasket); - applyTransferValuesForTask(task, destinationWorkbasket, setTransferFlag); + applyTransferValuesForTask(task, destinationWorkbasket, owner, setTransferFlag); taskMapper.update(task); if (historyEventManager.isEnabled()) { createTransferredEvent( @@ -136,6 +190,7 @@ private Task transferSingleTask( private BulkOperationResults transferMultipleTasks( List taskToBeTransferred, WorkbasketSummary destinationWorkbasket, + String owner, boolean setTransferFlag) throws InvalidArgumentException { if (taskToBeTransferred == null || taskToBeTransferred.isEmpty()) { @@ -154,7 +209,7 @@ private BulkOperationResults 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 { @@ -254,6 +309,7 @@ private Set getSourceWorkbasketIdsWithTransferPermission( private void updateTransferableTasks( List taskSummaries, WorkbasketSummary destinationWorkbasket, + String owner, boolean setTransferFlag) { Map> summariesByState = groupTasksByState(taskSummaries); for (Map.Entry> entry : summariesByState.entrySet()) { @@ -262,7 +318,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) @@ -275,7 +331,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, @@ -289,11 +346,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()); diff --git a/lib/taskana-core/src/test/java/acceptance/task/transfer/TransferTaskAccTest.java b/lib/taskana-core/src/test/java/acceptance/task/transfer/TransferTaskAccTest.java index e0f37ed8f5..a94c3e995f 100644 --- a/lib/taskana-core/src/test/java/acceptance/task/transfer/TransferTaskAccTest.java +++ b/lib/taskana-core/src/test/java/acceptance/task/transfer/TransferTaskAccTest.java @@ -435,4 +435,220 @@ void should_NotSetTheTransferFlagWithinBulkTransfer_When_SetTransferFlagNotReque assertThat(transferredTasks).extracting(TaskSummary::isTransferred).containsOnly(false); } + + @WithAccessId(user = "teamlead-1") + @Test + void should_SetOwnerAndNotBeAuthorized_When_TransferringTask() throws Exception { + taskService.transferWithOwner( + "TKI:000000000000000000000000000000000021", + "WBI:100000000000000000000000000000000005", + "teamlead-1"); + + Task transferredTask = taskService.getTask("TKI:000000000000000000000000000000000021"); + assertThat(transferredTask).isNotNull(); + assertThat(transferredTask.isTransferred()).isTrue(); + assertThat(transferredTask.isRead()).isFalse(); + assertThat(transferredTask.getState()).isEqualTo(TaskState.READY); + assertThat(transferredTask.getOwner()).isEqualTo("teamlead-1"); + assertThat(transferredTask.getWorkbasketSummary().getId()) + .isEqualTo("WBI:100000000000000000000000000000000005"); + } + + @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 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 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 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"); + + transferredTask = taskService.getTask("TKI:200000000000000000000000000000000008"); + assertThat(transferredTask).isNotNull(); + assertThat(transferredTask.isTransferred()).isFalse(); + assertThat(transferredTask.getWorkbasketKey()).isEqualTo("TPK_VIP"); + + transferredTask = taskService.getTask("TKI:100000000000000000000000000000000006"); + assertThat(transferredTask).isNotNull(); + assertThat(transferredTask.isTransferred()).isFalse(); + assertThat(transferredTask.getWorkbasketKey()).isEqualTo("TEAMLEAD-1"); + } + + @WithAccessId(user = "teamlead-1", groups = GROUP_1_DN) + @Test + void should_TransferTaskAndSetOwner_When_WorkbasketKeyAndDomainIsProvided() throws Exception { + taskService.transferWithOwner( + "TKI:200000000000000000000000000000000066", "USER-1-2", "DOMAIN_A", "teamlead-1"); + + Task transferredTask = taskService.getTask("TKI:200000000000000000000000000000000066"); + assertThat(transferredTask).isNotNull(); + assertThat(transferredTask.isTransferred()).isTrue(); + assertThat(transferredTask.isRead()).isFalse(); + assertThat(transferredTask.getState()).isEqualTo(TaskState.READY); + assertThat(transferredTask.getOwner()).isEqualTo("teamlead-1"); + assertThat(transferredTask.getWorkbasketSummary().getId()) + .isEqualTo("WBI:100000000000000000000000000000000007"); + } + + @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 taskIdList = + List.of( + "TKI:000000000000000000000000000000000008", "TKI:000000000000000000000000000000000009"); + + BulkOperationResults 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"); + } + + @WithAccessId(user = "admin") + @Test + void should_SetTransferFlagToFalse_When_WithOwnerAndWorkbasketIdGiven() throws Exception { + taskService.transferWithOwner( + "TKI:000000000000000000000000000000000010", + "WBI:100000000000000000000000000000000006", + "user-1-1", + false); + + Task transferredTask = taskService.getTask("TKI:000000000000000000000000000000000010"); + assertThat(transferredTask).isNotNull(); + assertThat(transferredTask.isTransferred()).isFalse(); + assertThat(transferredTask.isRead()).isFalse(); + assertThat(transferredTask.getState()).isEqualTo(TaskState.READY); + assertThat(transferredTask.getOwner()).isEqualTo("user-1-1"); + assertThat(transferredTask.getWorkbasketSummary().getId()) + .isEqualTo("WBI:100000000000000000000000000000000006"); + } + + @WithAccessId(user = "admin") + @Test + void should_NotSetTheTransferFlagWithinBulkTransferWithOwner_When_WorkbasketIdGiven() + throws Exception { + taskService.transferTasksWithOwner( + "WBI:100000000000000000000000000000000006", + List.of( + "TKI:000000000000000000000000000000000010", + "TKI:000000000000000000000000000000000011", + "TKI:000000000000000000000000000000000012"), + "user-1-1", + false); + + List transferredTasks = + taskService + .createTaskQuery() + .idIn( + "TKI:000000000000000000000000000000000010", + "TKI:000000000000000000000000000000000011", + "TKI:000000000000000000000000000000000012") + .list(); + + assertThat(transferredTasks).extracting(TaskSummary::isTransferred).containsOnly(false); + } + + @WithAccessId(user = "admin") + @Test + void should_SetTransferFlagToFalse_When_WithOwnerAndWorkbasketKeyAndDomainGiven() + throws Exception { + taskService.transferWithOwner( + "TKI:000000000000000000000000000000000011", "USER-1-1", "DOMAIN_A", "user-1-1", false); + + Task transferredTask = taskService.getTask("TKI:000000000000000000000000000000000011"); + assertThat(transferredTask).isNotNull(); + assertThat(transferredTask.isTransferred()).isFalse(); + assertThat(transferredTask.isRead()).isFalse(); + assertThat(transferredTask.getState()).isEqualTo(TaskState.READY); + assertThat(transferredTask.getOwner()).isEqualTo("user-1-1"); + assertThat(transferredTask.getWorkbasketSummary().getId()) + .isEqualTo("WBI:100000000000000000000000000000000006"); + } + + @WithAccessId(user = "admin") + @Test + void should_NotSetTheTransferFlagWithinBulkTransferWithOwner_When_WorkbasketKeyAndDomainGiven() + throws Exception { + taskService.transferTasksWithOwner( + "USER-1-1", + "DOMAIN_A", + List.of( + "TKI:000000000000000000000000000000000013", + "TKI:000000000000000000000000000000000014", + "TKI:000000000000000000000000000000000015"), + "user-1-1", + false); + + List transferredTasks = + taskService + .createTaskQuery() + .idIn( + "TKI:000000000000000000000000000000000013", + "TKI:000000000000000000000000000000000014", + "TKI:000000000000000000000000000000000015") + .list(); + + assertThat(transferredTasks).extracting(TaskSummary::isTransferred).containsOnly(false); + } } diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/RestEndpoints.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/RestEndpoints.java index 25e0c7f711..91ffc31c65 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/RestEndpoints.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/RestEndpoints.java @@ -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 diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/task/rest/TaskController.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/task/rest/TaskController.java index e373f7c3b9..66790070a2 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/task/rest/TaskController.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/task/rest/TaskController.java @@ -55,6 +55,7 @@ import pro.taskana.task.rest.models.TaskRepresentationModel; import pro.taskana.task.rest.models.TaskSummaryCollectionRepresentationModel; import pro.taskana.task.rest.models.TaskSummaryPagedRepresentationModel; +import pro.taskana.task.rest.models.TransferTaskRepresentationModel; import pro.taskana.workbasket.api.exceptions.NotAuthorizedOnWorkbasketException; import pro.taskana.workbasket.api.exceptions.WorkbasketNotFoundException; @@ -109,11 +110,12 @@ public ResponseEntity createTask( NotAuthorizedOnWorkbasketException { if (!taskRepresentationModel.getAttachments().stream() - .filter(att -> Objects.nonNull(att.getTaskId())) - .filter(att -> !att.getTaskId().equals(taskRepresentationModel.getTaskId())) - .collect(Collectors.toList()).isEmpty()) { + .filter(att -> Objects.nonNull(att.getTaskId())) + .filter(att -> !att.getTaskId().equals(taskRepresentationModel.getTaskId())) + .collect(Collectors.toList()) + .isEmpty()) { throw new InvalidArgumentException( - "An attachments' taskId must be empty or equal to the id of the task it belongs to"); + "An attachments' taskId must be empty or equal to the id of the task it belongs to"); } Task fromResource = taskRepresentationModelAssembler.toEntityModel(taskRepresentationModel); @@ -531,7 +533,8 @@ public ResponseEntity terminateTask(@PathVariable Strin * @title Transfer a Task to another Workbasket * @param taskId the Id of the Task which should be transferred * @param workbasketId the Id of the destination Workbasket - * @param setTransferFlag sets the tansfer flag of the task (default: true) + * @param transferTaskRepresentationModel sets the transfer flag of the Task (default: true) and + * Owner of task * @return the successfully transferred Task. * @throws TaskNotFoundException if the requested Task does not exist * @throws WorkbasketNotFoundException if the requested Workbasket does not exist @@ -544,17 +547,75 @@ public ResponseEntity terminateTask(@PathVariable Strin public ResponseEntity transferTask( @PathVariable String taskId, @PathVariable String workbasketId, - @RequestBody(required = false) Boolean setTransferFlag) + @RequestBody(required = false) + TransferTaskRepresentationModel transferTaskRepresentationModel) throws TaskNotFoundException, WorkbasketNotFoundException, NotAuthorizedOnWorkbasketException, InvalidTaskStateException { - Task updatedTask = - taskService.transfer(taskId, workbasketId, setTransferFlag == null || setTransferFlag); + Task updatedTask; + if (transferTaskRepresentationModel == null) { + updatedTask = taskService.transfer(taskId, workbasketId); + } else { + updatedTask = + taskService.transferWithOwner( + taskId, + workbasketId, + transferTaskRepresentationModel.getOwner(), + transferTaskRepresentationModel.getSetTransferFlag()); + } return ResponseEntity.ok(taskRepresentationModelAssembler.toModel(updatedTask)); } + /** + * This endpoint transfers a list of Tasks listed in the body to a given Workbasket, if possible. + * + * @title Transfer Tasks to another Workbasket + * @param workbasketId the Id of the destination Workbasket + * @param transferTaskRepresentationModel the TaskIds, owner and setTransferFlag of tasks to be + * transferred + * @return the successfully transferred Task. + * @throws WorkbasketNotFoundException if the requested Workbasket does not exist + * @throws NotAuthorizedOnWorkbasketException if the current user has no authorization to transfer + * the Task. + */ + @PostMapping(path = RestEndpoints.URL_TRANSFER_WORKBASKET_ID) + @Transactional(rollbackFor = Exception.class) + public ResponseEntity transferTasks( + @PathVariable String workbasketId, + @RequestBody TransferTaskRepresentationModel transferTaskRepresentationModel) + throws NotAuthorizedOnWorkbasketException, WorkbasketNotFoundException { + List taskIds = transferTaskRepresentationModel.getTaskIds(); + BulkOperationResults result; + if (transferTaskRepresentationModel.getOwner() == null) { + result = + taskService.transferTasks( + workbasketId, taskIds, transferTaskRepresentationModel.getSetTransferFlag()); + } else { + result = + taskService.transferTasksWithOwner( + workbasketId, + taskIds, + transferTaskRepresentationModel.getOwner(), + transferTaskRepresentationModel.getSetTransferFlag()); + } + + Set failedIds = new HashSet<>(result.getFailedIds()); + List successfullyTransferredTaskIds = + taskIds.stream().filter(not(failedIds::contains)).toList(); + + List successfullyTransferredTaskSummaries = + taskService + .createTaskQuery() + .idIn(successfullyTransferredTaskIds.toArray(new String[0])) + .list(); + + return ResponseEntity.ok( + taskSummaryRepresentationModelAssembler.toTaskanaCollectionModel( + successfullyTransferredTaskSummaries)); + } + /** * This endpoint updates a requested Task. * @@ -597,11 +658,12 @@ public ResponseEntity updateTask( } if (!taskRepresentationModel.getAttachments().stream() - .filter(att -> Objects.nonNull(att.getTaskId())) - .filter(att -> !att.getTaskId().equals(taskRepresentationModel.getTaskId())) - .collect(Collectors.toList()).isEmpty()) { + .filter(att -> Objects.nonNull(att.getTaskId())) + .filter(att -> !att.getTaskId().equals(taskRepresentationModel.getTaskId())) + .collect(Collectors.toList()) + .isEmpty()) { throw new InvalidArgumentException( - "An attachments' taskId must be empty or equal to the id of the task it belongs to"); + "An attachments' taskId must be empty or equal to the id of the task it belongs to"); } Task task = taskRepresentationModelAssembler.toEntityModel(taskRepresentationModel); diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/task/rest/models/TransferTaskRepresentationModel.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/task/rest/models/TransferTaskRepresentationModel.java new file mode 100644 index 0000000000..d921c1ca1d --- /dev/null +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/task/rest/models/TransferTaskRepresentationModel.java @@ -0,0 +1,40 @@ +package pro.taskana.task.rest.models; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.beans.ConstructorProperties; +import java.util.List; + +public class TransferTaskRepresentationModel { + + /** The value to set the Task property owner. */ + @JsonProperty("owner") + private final String owner; + + /** The value to set the Task property setTransferFlag. */ + @JsonProperty("setTransferFlag") + private final Boolean setTransferFlag; + + /** The value to set the Task property taskIds. */ + @JsonProperty("taskIds") + private final List taskIds; + + @ConstructorProperties({"setTransferFlag", "owner", "taskIds"}) + public TransferTaskRepresentationModel( + Boolean setTransferFlag, String owner, List taskIds) { + this.setTransferFlag = setTransferFlag == null || setTransferFlag; + this.owner = owner; + this.taskIds = taskIds; + } + + public Boolean getSetTransferFlag() { + return setTransferFlag; + } + + public String getOwner() { + return owner; + } + + public List getTaskIds() { + return taskIds; + } +} diff --git a/rest/taskana-rest-spring/src/test/java/pro/taskana/task/rest/TaskControllerIntTest.java b/rest/taskana-rest-spring/src/test/java/pro/taskana/task/rest/TaskControllerIntTest.java index 3f964d5079..a40137130b 100644 --- a/rest/taskana-rest-spring/src/test/java/pro/taskana/task/rest/TaskControllerIntTest.java +++ b/rest/taskana-rest-spring/src/test/java/pro/taskana/task/rest/TaskControllerIntTest.java @@ -54,6 +54,7 @@ import pro.taskana.task.rest.models.TaskSummaryCollectionRepresentationModel; import pro.taskana.task.rest.models.TaskSummaryPagedRepresentationModel; import pro.taskana.task.rest.models.TaskSummaryRepresentationModel; +import pro.taskana.task.rest.models.TransferTaskRepresentationModel; import pro.taskana.task.rest.routing.IntegrationTestTaskRouter; import pro.taskana.workbasket.rest.models.WorkbasketSummaryRepresentationModel; @@ -61,7 +62,6 @@ @TaskanaSpringBootTest class TaskControllerIntTest { - @Autowired TaskanaConfiguration taskanaConfiguration; private static final ParameterizedTypeReference TASK_SUMMARY_PAGE_MODEL_TYPE = new ParameterizedTypeReference<>() {}; private static final ParameterizedTypeReference @@ -71,6 +71,7 @@ class TaskControllerIntTest { private final RestHelper restHelper; private final DataSource dataSource; private final String schemaName; + @Autowired TaskanaConfiguration taskanaConfiguration; @Autowired TaskControllerIntTest( @@ -149,16 +150,16 @@ private ObjectReferenceRepresentationModel getObjectReferenceResourceSample() { private AttachmentRepresentationModel getAttachmentResourceSample() { AttachmentRepresentationModel attachmentRepresentationModel = - new AttachmentRepresentationModel(); + new AttachmentRepresentationModel(); attachmentRepresentationModel.setAttachmentId("A11010"); attachmentRepresentationModel.setObjectReference(getObjectReferenceResourceSample()); ClassificationSummaryRepresentationModel classificationSummaryRepresentationModel = - new ClassificationSummaryRepresentationModel(); - classificationSummaryRepresentationModel - .setClassificationId("CLI:100000000000000000000000000000000004"); + new ClassificationSummaryRepresentationModel(); + classificationSummaryRepresentationModel.setClassificationId( + "CLI:100000000000000000000000000000000004"); classificationSummaryRepresentationModel.setKey("L11010"); - attachmentRepresentationModel - .setClassificationSummary(classificationSummaryRepresentationModel); + attachmentRepresentationModel.setClassificationSummary( + classificationSummaryRepresentationModel); return attachmentRepresentationModel; } @@ -1550,16 +1551,16 @@ void should_CreateTaskWithError_When_SpecifyingAttachmentWrong() { String url = restHelper.toUrl(RestEndpoints.URL_TASKS); HttpEntity auth = - new HttpEntity<>( - taskRepresentationModel, RestHelper.generateHeadersForUser("teamlead-1")); + new HttpEntity<>( + taskRepresentationModel, RestHelper.generateHeadersForUser("teamlead-1")); ThrowingCallable httpCall = - () -> TEMPLATE.exchange(url, HttpMethod.POST, auth, TASK_MODEL_TYPE); + () -> TEMPLATE.exchange(url, HttpMethod.POST, auth, TASK_MODEL_TYPE); assertThatThrownBy(httpCall) - .extracting(HttpStatusCodeException.class::cast) - .extracting(HttpStatusCodeException::getStatusCode) - .isEqualTo(HttpStatus.BAD_REQUEST); + .extracting(HttpStatusCodeException.class::cast) + .extracting(HttpStatusCodeException::getStatusCode) + .isEqualTo(HttpStatus.BAD_REQUEST); } @Test @@ -1789,13 +1790,12 @@ void should_ChangeValueOfModified_When_UpdatingTask() { @Test void should_ThrowError_When_UpdatingTaskWithBadAttachment() { String url = - restHelper.toUrl(RestEndpoints.URL_TASKS_ID, - "TKI:100000000000000000000000000000000000"); + restHelper.toUrl(RestEndpoints.URL_TASKS_ID, "TKI:100000000000000000000000000000000000"); HttpEntity httpEntityWithoutBody = - new HttpEntity<>(RestHelper.generateHeadersForUser("teamlead-1")); + new HttpEntity<>(RestHelper.generateHeadersForUser("teamlead-1")); ResponseEntity responseGet = - TEMPLATE.exchange(url, HttpMethod.GET, httpEntityWithoutBody, TASK_MODEL_TYPE); + TEMPLATE.exchange(url, HttpMethod.GET, httpEntityWithoutBody, TASK_MODEL_TYPE); final TaskRepresentationModel originalTask = responseGet.getBody(); @@ -1803,17 +1803,16 @@ void should_ThrowError_When_UpdatingTaskWithBadAttachment() { attachmentRepresentationModel.setTaskId(originalTask.getTaskId() + "wrongId"); originalTask.setAttachments(Lists.newArrayList(attachmentRepresentationModel)); - HttpEntity httpEntity = - new HttpEntity<>(originalTask, RestHelper.generateHeadersForUser("teamlead-1")); + new HttpEntity<>(originalTask, RestHelper.generateHeadersForUser("teamlead-1")); ThrowingCallable httpCall = - () -> TEMPLATE.exchange(url, HttpMethod.PUT, httpEntity, TASK_MODEL_TYPE); + () -> TEMPLATE.exchange(url, HttpMethod.PUT, httpEntity, TASK_MODEL_TYPE); assertThatThrownBy(httpCall) - .extracting(HttpStatusCodeException.class::cast) - .extracting(HttpStatusCodeException::getStatusCode) - .isEqualTo(HttpStatus.BAD_REQUEST); + .extracting(HttpStatusCodeException.class::cast) + .extracting(HttpStatusCodeException::getStatusCode) + .isEqualTo(HttpStatus.BAD_REQUEST); } } @@ -1963,33 +1962,36 @@ void should_ThrowException_When_UpdatingTaskOwnerOfClaimedTask() { @TestInstance(Lifecycle.PER_CLASS) class TransferTasks { @TestFactory - Stream should_SetTransferFlagDependentOnRequestBody_When_TransferringTask() { - Iterator iterator = Arrays.asList(true, false).iterator(); + Stream should_SetTransferFlagAndOwnerDependentOnBody_When_TransferTask() { + Iterator> iterator = + Arrays.asList(Pair.of(false, "user-1-1"), Pair.of(true, "user-1-1")).iterator(); String url = restHelper.toUrl( RestEndpoints.URL_TASKS_ID_TRANSFER_WORKBASKET_ID, "TKI:000000000000000000000000000000000003", "WBI:100000000000000000000000000000000006"); - ThrowingConsumer test = - setTransferFlag -> { - HttpEntity auth = + ThrowingConsumer> test = + pair -> { + HttpEntity auth = new HttpEntity<>( - setTransferFlag.toString(), RestHelper.generateHeadersForUser("admin")); + new TransferTaskRepresentationModel(pair.getLeft(), pair.getRight(), null), + RestHelper.generateHeadersForUser("admin")); ResponseEntity response = TEMPLATE.exchange(url, HttpMethod.POST, auth, TASK_MODEL_TYPE); assertThat(response.getBody()).isNotNull(); assertThat(response.getBody().getWorkbasketSummary().getWorkbasketId()) .isEqualTo("WBI:100000000000000000000000000000000006"); - assertThat(response.getBody().isTransferred()).isEqualTo(setTransferFlag); + assertThat(response.getBody().isTransferred()).isEqualTo(pair.getLeft()); + assertThat(response.getBody().getOwner()).isEqualTo(pair.getRight()); }; return DynamicTest.stream(iterator, c -> "for setTransferFlag: " + c, test); } @Test - void should_SetTransferFlagToTrue_When_TransferringWithoutRequestBody() { + void should_SetTransferFlagToTrueAndOwnerToNull_When_TransferringWithoutRequestBody() { String url = restHelper.toUrl( RestEndpoints.URL_TASKS_ID_TRANSFER_WORKBASKET_ID, @@ -2004,6 +2006,46 @@ void should_SetTransferFlagToTrue_When_TransferringWithoutRequestBody() { assertThat(response.getBody().getWorkbasketSummary().getWorkbasketId()) .isEqualTo("WBI:100000000000000000000000000000000006"); assertThat(response.getBody().isTransferred()).isTrue(); + assertThat(response.getBody().getOwner()).isNull(); + } + + @TestFactory + Stream + should_SetTransferFlagAndOwnerDependentOnRequestBody_When_TransferringTasks() { + + Iterator> iterator = + Arrays.asList(Pair.of(true, "user-1-1"), Pair.of(false, "user-1-2")).iterator(); + String url = + restHelper.toUrl( + RestEndpoints.URL_TRANSFER_WORKBASKET_ID, "WBI:100000000000000000000000000000000006"); + + List taskIds = + Arrays.asList( + "TKI:000000000000000000000000000000000003", + "TKI:000000000000000000000000000000000004", + "TKI:000000000000000000000000000000000005"); + + ThrowingConsumer> test = + pair -> { + HttpEntity auth = + new HttpEntity<>( + new TransferTaskRepresentationModel(pair.getLeft(), pair.getRight(), taskIds), + RestHelper.generateHeadersForUser("admin")); + ResponseEntity response = + TEMPLATE.exchange(url, HttpMethod.POST, auth, TASK_SUMMARY_COLLECTION_MODEL_TYPE); + + assertThat(response.getBody()).isNotNull(); + assertThat((response.getBody()).getLink(IanaLinkRelations.SELF)).isNotNull(); + assertThat(response.getBody().getContent()).hasSize(3); + for (TaskSummaryRepresentationModel representationModel : + response.getBody().getContent()) { + assertThat(representationModel.getWorkbasketSummary().getWorkbasketId()) + .isEqualTo("WBI:100000000000000000000000000000000006"); + assertThat(representationModel.isTransferred()).isEqualTo(pair.getLeft()); + assertThat(representationModel.getOwner()).isEqualTo(pair.getRight()); + } + }; + return DynamicTest.stream(iterator, c -> "for setTransferFlag and owner: " + c, test); } } diff --git a/rest/taskana-rest-spring/src/test/java/pro/taskana/task/rest/TaskControllerRestDocTest.java b/rest/taskana-rest-spring/src/test/java/pro/taskana/task/rest/TaskControllerRestDocTest.java index d8d0380754..8b99ed6d7c 100644 --- a/rest/taskana-rest-spring/src/test/java/pro/taskana/task/rest/TaskControllerRestDocTest.java +++ b/rest/taskana-rest-spring/src/test/java/pro/taskana/task/rest/TaskControllerRestDocTest.java @@ -5,6 +5,7 @@ import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.put; +import java.util.List; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -18,6 +19,7 @@ import pro.taskana.task.rest.assembler.TaskRepresentationModelAssembler; import pro.taskana.task.rest.models.IsReadRepresentationModel; import pro.taskana.task.rest.models.TaskRepresentationModel; +import pro.taskana.task.rest.models.TransferTaskRepresentationModel; import pro.taskana.testapi.security.JaasExtension; import pro.taskana.testapi.security.WithAccessId; @@ -221,12 +223,15 @@ void createTaskDocTest() throws Exception { @Test void transferTaskDocTest() throws Exception { + TransferTaskRepresentationModel transferTaskRepresentationModel = + new TransferTaskRepresentationModel(true, "user-1-1", null); mockMvc .perform( post( - RestEndpoints.URL_TASKS_ID_TRANSFER_WORKBASKET_ID, - "TKI:000000000000000000000000000000000004", - "WBI:100000000000000000000000000000000001")) + RestEndpoints.URL_TASKS_ID_TRANSFER_WORKBASKET_ID, + "TKI:000000000000000000000000000000000004", + "WBI:100000000000000000000000000000000001") + .content(objectMapper.writeValueAsString(transferTaskRepresentationModel))) .andExpect(MockMvcResultMatchers.status().isOk()); } @@ -244,4 +249,20 @@ void updateTaskDocTest() throws Exception { .content(objectMapper.writeValueAsString(repModel))) .andExpect(MockMvcResultMatchers.status().isOk()); } + + @Test + void transferTasksDocTest() throws Exception { + List taskIds = + List.of( + "TKI:000000000000000000000000000000000000", "TKI:000000000000000000000000000000000001"); + TransferTaskRepresentationModel transferTaskRepresentationModel = + new TransferTaskRepresentationModel(true, "user-1-1", taskIds); + mockMvc + .perform( + post( + RestEndpoints.URL_TRANSFER_WORKBASKET_ID, + "WBI:100000000000000000000000000000000001") + .content(objectMapper.writeValueAsString(transferTaskRepresentationModel))) + .andExpect(MockMvcResultMatchers.status().isOk()); + } }