From ea31ccdf54f7b79174755a094f986531275a89af Mon Sep 17 00:00:00 2001 From: jamesrdi Date: Thu, 8 Feb 2024 13:53:24 +0100 Subject: [PATCH] Closes #2496 : Set Owner of Tasks when Transferring --- .../pro/taskana/task/api/TaskService.java | 94 ++++++++++++++++++ .../task/internal/TaskServiceImpl.java | 24 +++++ .../task/internal/TaskTransferrer.java | 47 +++++++-- .../task/transfer/TransferTaskAccTest.java | 99 +++++++++++++++++++ .../taskana/common/rest/RestEndpoints.java | 1 + .../pro/taskana/task/rest/TaskController.java | 49 +++++++++ .../TransferTaskRepresentationModel.java | 40 ++++++++ .../task/rest/TaskControllerIntTest.java | 84 +++++++++++----- .../task/rest/TaskControllerRestDocTest.java | 20 ++++ 9 files changed, 427 insertions(+), 31 deletions(-) create mode 100644 rest/taskana-rest-spring/src/main/java/pro/taskana/task/rest/models/TransferTaskRepresentationModel.java 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..b9faf72efc 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 @@ -639,6 +639,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..e3af825fda 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 @@ -631,6 +631,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..ac521d7ab4 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 @@ -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,7 +101,35 @@ 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); } private Task transferSingleTask( @@ -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( @@ -136,6 +164,7 @@ private Task transferSingleTask( private BulkOperationResults transferMultipleTasks( List taskToBeTransferred, WorkbasketSummary destinationWorkbasket, + String owner, boolean setTransferFlag) throws InvalidArgumentException { if (taskToBeTransferred == null || taskToBeTransferred.isEmpty()) { @@ -154,7 +183,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 +283,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 +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) @@ -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, @@ -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()); 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..320e6aa7f1 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 @@ -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; @@ -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; @@ -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 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 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 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"); + } } 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..e0c9fb51bf 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; @@ -555,6 +556,54 @@ public ResponseEntity transferTask( 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. * 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..2460c19260 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); } } @@ -2005,6 +2004,45 @@ void should_SetTransferFlagToTrue_When_TransferringWithoutRequestBody() { .isEqualTo("WBI:100000000000000000000000000000000006"); assertThat(response.getBody().isTransferred()).isTrue(); } + + @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); + } } @Nested 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..c26bbb96ac 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; @@ -244,4 +246,22 @@ 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", + "TKI:000000000000000000000000000000000002"); + 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()); + } }