From 57bf8cd453b355dcd933e691c4f4b839539929fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Heffner?= Date: Thu, 12 Dec 2019 13:08:34 +0100 Subject: [PATCH] TSK-909 - Claim task in camunda if task is claimed in TASKANA -Added a new CallbackState which is needed when a task was claimed by TASKANA -Added ruleset logic to determine if the public API is allowed to set the desired CallbackState -Added test for the ruleset logic --- .../main/java/pro/taskana/CallbackState.java | 2 +- .../pro/taskana/impl/TaskServiceImpl.java | 42 +++- .../acceptance/task/CallbackStateAccTest.java | 180 ++++++++++++++++-- 3 files changed, 207 insertions(+), 17 deletions(-) diff --git a/lib/taskana-core/src/main/java/pro/taskana/CallbackState.java b/lib/taskana-core/src/main/java/pro/taskana/CallbackState.java index b584638bd..91e020dcc 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/CallbackState.java +++ b/lib/taskana-core/src/main/java/pro/taskana/CallbackState.java @@ -4,5 +4,5 @@ package pro.taskana; * This enum contains all status of synchronization between a taskana task and a task in a remote system. */ public enum CallbackState { - NONE, CALLBACK_PROCESSING_REQUIRED, CALLBACK_PROCESSING_COMPLETED + NONE, CALLBACK_PROCESSING_REQUIRED, CLAIMED, CALLBACK_PROCESSING_COMPLETED } diff --git a/lib/taskana-core/src/main/java/pro/taskana/impl/TaskServiceImpl.java b/lib/taskana-core/src/main/java/pro/taskana/impl/TaskServiceImpl.java index a1c07f8ce..363f1428d 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/impl/TaskServiceImpl.java +++ b/lib/taskana-core/src/main/java/pro/taskana/impl/TaskServiceImpl.java @@ -459,7 +459,7 @@ public class TaskServiceImpl implements TaskService { Iterator taskIdIterator = externalIds.iterator(); while (taskIdIterator.hasNext()) { - removeSingleTaskForCallbackStateByExternalId(bulkLog, taskSummaries, taskIdIterator); + removeSingleTaskForCallbackStateByExternalId(bulkLog, taskSummaries, taskIdIterator, state); } if (!externalIds.isEmpty()) { taskMapper.setCallbackStateMultiple(externalIds, state); @@ -505,7 +505,7 @@ public class TaskServiceImpl implements TaskService { private void removeSingleTaskForCallbackStateByExternalId(BulkOperationResults bulkLog, - List taskSummaries, Iterator externalIdIterator) { + List taskSummaries, Iterator externalIdIterator, CallbackState desiredCallbackState) { LOGGER.debug("entry to removeSingleTask()"); String currentExternalId = externalIdIterator.next(); if (currentExternalId == null || currentExternalId.equals("")) { @@ -521,14 +521,50 @@ public class TaskServiceImpl implements TaskService { bulkLog.addError(currentExternalId, new TaskNotFoundException(currentExternalId, TASK_WITH_ID + currentExternalId + WAS_NOT_FOUND2)); externalIdIterator.remove(); - } else if (!TaskState.COMPLETED.equals(foundSummary.getTaskState())) { + } else if (!desiredCallbackStateCanBeSetForFoundSummary(foundSummary, desiredCallbackState)) { bulkLog.addError(currentExternalId, new InvalidStateException(currentExternalId)); externalIdIterator.remove(); } + } LOGGER.debug("exit from removeSingleTask()"); } + private boolean desiredCallbackStateCanBeSetForFoundSummary(MinimalTaskSummary foundSummary, CallbackState desiredCallbackState) { + + CallbackState currentTaskCallbackState = foundSummary.getCallbackState(); + TaskState currentTaskState = foundSummary.getTaskState(); + + switch (desiredCallbackState) { + + case CALLBACK_PROCESSING_COMPLETED: + if (!(currentTaskState.equals(TaskState.COMPLETED))) { + return false; + } + return true; + + case CLAIMED: + if (!currentTaskState.equals(TaskState.CLAIMED)) { + return false; + } else if (!currentTaskCallbackState.equals(CallbackState.CALLBACK_PROCESSING_REQUIRED)) { + return false; + } + return true; + + case CALLBACK_PROCESSING_REQUIRED: + if (currentTaskCallbackState.equals(CallbackState.CALLBACK_PROCESSING_COMPLETED)) { + return false; + } + return true; + case NONE: + return false; + + default: + return false; + } + + } + @Override public List updateTasks(ObjectReference selectionCriteria, Map customFieldsToUpdate) throws InvalidArgumentException { diff --git a/lib/taskana-core/src/test/java/acceptance/task/CallbackStateAccTest.java b/lib/taskana-core/src/test/java/acceptance/task/CallbackStateAccTest.java index 63fe2d9ee..9871321cf 100644 --- a/lib/taskana-core/src/test/java/acceptance/task/CallbackStateAccTest.java +++ b/lib/taskana-core/src/test/java/acceptance/task/CallbackStateAccTest.java @@ -1,6 +1,5 @@ package acceptance.task; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -44,7 +43,6 @@ import pro.taskana.security.WithAccessId; @ExtendWith(JAASExtension.class) class CallbackStateAccTest extends AbstractAccTest { - @WithAccessId( userName = "user_1_1", groupNames = {"group_1"}) @@ -65,6 +63,11 @@ class CallbackStateAccTest extends AbstractAccTest { createdTask = createTask(taskService, CallbackState.CALLBACK_PROCESSING_COMPLETED); createdTask = (TaskImpl) taskService.getTask(createdTask.getId()); assertEquals(CallbackState.CALLBACK_PROCESSING_COMPLETED, createdTask.getCallbackState()); + + createdTask = createTask(taskService, CallbackState.CLAIMED); + createdTask = (TaskImpl) taskService.getTask(createdTask.getId()); + assertEquals(CallbackState.CLAIMED, createdTask.getCallbackState()); + } @WithAccessId( @@ -73,16 +76,17 @@ class CallbackStateAccTest extends AbstractAccTest { @Test void testDeletionOfTaskWithWrongCallbackStateIsBlocked() throws WorkbasketNotFoundException, ClassificationNotFoundException, NotAuthorizedException, - TaskAlreadyExistException, InvalidArgumentException, TaskNotFoundException, InvalidStateException, InvalidOwnerException { + TaskAlreadyExistException, InvalidArgumentException, TaskNotFoundException, InvalidStateException, + InvalidOwnerException { TaskService taskService = taskanaEngine.getTaskService(); - final TaskImpl createdTask = createTask(taskanaEngine.getTaskService(), CallbackState.CALLBACK_PROCESSING_REQUIRED); + final TaskImpl createdTask = createTask(taskanaEngine.getTaskService(), + CallbackState.CALLBACK_PROCESSING_REQUIRED); assertEquals(CallbackState.CALLBACK_PROCESSING_REQUIRED, createdTask.getCallbackState()); assertEquals(TaskState.READY, createdTask.getState()); String endOfMessage = " cannot be deleted because its callback is not yet processed"; - Throwable t = Assertions.assertThrows(InvalidStateException.class, () -> { taskService.forceDeleteTask(createdTask.getId()); }); @@ -111,7 +115,8 @@ class CallbackStateAccTest extends AbstractAccTest { @Test void testUpdateOfCallbackState() throws WorkbasketNotFoundException, ClassificationNotFoundException, NotAuthorizedException, - TaskAlreadyExistException, InvalidArgumentException, TaskNotFoundException, InvalidStateException, InvalidOwnerException { + TaskAlreadyExistException, InvalidArgumentException, TaskNotFoundException, InvalidStateException, + InvalidOwnerException { TaskService taskService = taskanaEngine.getTaskService(); TaskImpl createdTask1 = createTask(taskanaEngine.getTaskService(), CallbackState.CALLBACK_PROCESSING_REQUIRED); @@ -131,8 +136,10 @@ class CallbackStateAccTest extends AbstractAccTest { assertEquals(TaskState.COMPLETED, createdTask2.getState()); assertEquals(TaskState.COMPLETED, createdTask3.getState()); - List taskIds = new ArrayList<>(Arrays.asList(createdTask1.getId(), createdTask2.getId(), createdTask3.getId())); - List externalIds = new ArrayList<>(Arrays.asList(createdTask1.getExternalId(), createdTask2.getExternalId(), createdTask3.getExternalId())); + List taskIds = new ArrayList<>( + Arrays.asList(createdTask1.getId(), createdTask2.getId(), createdTask3.getId())); + List externalIds = new ArrayList<>( + Arrays.asList(createdTask1.getExternalId(), createdTask2.getExternalId(), createdTask3.getExternalId())); // delete should fail because callback_state = CALLBACK_PROCESSING_REQUIRED BulkOperationResults bulkResult1 = taskService.deleteTasks(taskIds); @@ -146,7 +153,8 @@ class CallbackStateAccTest extends AbstractAccTest { } // now enable deletion by setting callback state to CALLBACK_PROCESSING_COMPLETED - BulkOperationResults bulkResult2 = taskService.setCallbackStateForTasks(externalIds, CallbackState.CALLBACK_PROCESSING_COMPLETED); + BulkOperationResults bulkResult2 = taskService.setCallbackStateForTasks(externalIds, + CallbackState.CALLBACK_PROCESSING_COMPLETED); assertFalse(bulkResult2.containsErrors()); taskIds = new ArrayList<>(Arrays.asList(createdTask1.getId(), createdTask2.getId(), createdTask3.getId())); @@ -155,13 +163,155 @@ class CallbackStateAccTest extends AbstractAccTest { assertFalse(bulkResult3.containsErrors()); } + @WithAccessId( + userName = "admin", + groupNames = {"group_1"}) + @Test + void testInvalidUpdateOfCallbackStateToNone() + throws WorkbasketNotFoundException, ClassificationNotFoundException, NotAuthorizedException, + TaskAlreadyExistException, InvalidArgumentException { + + TaskService taskService = taskanaEngine.getTaskService(); + + TaskImpl createdTask1 = createTask(taskService, CallbackState.CALLBACK_PROCESSING_REQUIRED); + assertEquals(CallbackState.CALLBACK_PROCESSING_REQUIRED, createdTask1.getCallbackState()); + + TaskImpl createdTask2 = createTask(taskService, CallbackState.CLAIMED); + assertEquals(CallbackState.CLAIMED, createdTask2.getCallbackState()); + + TaskImpl createdTask3 = createTask(taskService, CallbackState.CALLBACK_PROCESSING_COMPLETED); + assertEquals(CallbackState.CALLBACK_PROCESSING_COMPLETED, createdTask3.getCallbackState()); + + List externalIds = new ArrayList<>( + Arrays.asList(createdTask1.getExternalId(), createdTask2.getExternalId(), createdTask3.getExternalId())); + + //try to set CallbackState to NONE + BulkOperationResults bulkResult = taskService.setCallbackStateForTasks(externalIds, + CallbackState.NONE); + + //It's never allowed to set CallbackState to NONE over public API + assertTrue(bulkResult.containsErrors()); + List failedTaskIds = bulkResult.getFailedIds(); + assertTrue(failedTaskIds.size() == 3); + + } + + @WithAccessId( + userName = "admin", + groupNames = {"group_1"}) + @Test + void testInvalidUpdateOfCallbackStateToComplete() + throws WorkbasketNotFoundException, ClassificationNotFoundException, NotAuthorizedException, + TaskAlreadyExistException, InvalidArgumentException, InvalidOwnerException, InvalidStateException, + TaskNotFoundException { + + TaskService taskService = taskanaEngine.getTaskService(); + + TaskImpl createdTask1 = createTask(taskService, CallbackState.CALLBACK_PROCESSING_REQUIRED); + assertEquals(CallbackState.CALLBACK_PROCESSING_REQUIRED, createdTask1.getCallbackState()); + + TaskImpl createdTask2 = createTask(taskService, CallbackState.CLAIMED); + assertEquals(CallbackState.CLAIMED, createdTask2.getCallbackState()); + + TaskImpl createdTask3 = createTask(taskService, CallbackState.CALLBACK_PROCESSING_COMPLETED); + assertEquals(CallbackState.CALLBACK_PROCESSING_COMPLETED, createdTask3.getCallbackState()); + + List externalIds = new ArrayList<>( + Arrays.asList(createdTask1.getExternalId(), createdTask2.getExternalId(), createdTask3.getExternalId())); + + + //complete a task + createdTask3 = (TaskImpl) taskService.forceCompleteTask(createdTask3.getId()); + + //It's only allowed to set CallbackState to COMPLETE, if TaskState equals COMPLETE, therefore 2 tasks should not get updated + BulkOperationResults bulkResult = taskService.setCallbackStateForTasks(externalIds, + CallbackState.CALLBACK_PROCESSING_COMPLETED); + assertTrue(bulkResult.containsErrors()); + List failedTaskIds = bulkResult.getFailedIds(); + assertTrue(failedTaskIds.size() == 2 && !failedTaskIds.contains(createdTask3.getExternalId())); + + } + + @WithAccessId( + userName = "admin", + groupNames = {"group_1"}) + @Test + void testInvalidUpdateOfCallbackStateToClaimed() + throws WorkbasketNotFoundException, ClassificationNotFoundException, NotAuthorizedException, + TaskAlreadyExistException, InvalidArgumentException, TaskNotFoundException, InvalidStateException, + InvalidOwnerException { + + TaskService taskService = taskanaEngine.getTaskService(); + + TaskImpl createdTask1 = createTask(taskService, CallbackState.CALLBACK_PROCESSING_REQUIRED); + assertEquals(CallbackState.CALLBACK_PROCESSING_REQUIRED, createdTask1.getCallbackState()); + + TaskImpl createdTask2 = createTask(taskService, CallbackState.CLAIMED); + assertEquals(CallbackState.CLAIMED, createdTask2.getCallbackState()); + + TaskImpl createdTask3 = createTask(taskService, CallbackState.CALLBACK_PROCESSING_COMPLETED); + assertEquals(CallbackState.CALLBACK_PROCESSING_COMPLETED, createdTask3.getCallbackState()); + + List externalIds = new ArrayList<>( + Arrays.asList(createdTask1.getExternalId(), createdTask2.getExternalId(), createdTask3.getExternalId())); + + + //claim two tasks + createdTask1 = (TaskImpl) taskService.forceClaim(createdTask1.getId()); + createdTask2 = (TaskImpl) taskService.forceClaim(createdTask2.getId()); + + //It's only allowed to claim a task if the TaskState equals CLAIMED and the CallbackState equals REQUIRED + //Therefore 2 tasks should not get updated + BulkOperationResults bulkResult = taskService.setCallbackStateForTasks(externalIds, + CallbackState.CLAIMED); + assertTrue(bulkResult.containsErrors()); + List failedTaskIds = bulkResult.getFailedIds(); + assertTrue(failedTaskIds.size() == 2 && !failedTaskIds.contains(createdTask1.getExternalId())); + + } + + @WithAccessId( + userName = "admin", + groupNames = {"group_1"}) + @Test + void testInvalidUpdateOfCallbackStateToRequired() + throws WorkbasketNotFoundException, ClassificationNotFoundException, NotAuthorizedException, + TaskAlreadyExistException, InvalidArgumentException { + + + TaskService taskService = taskanaEngine.getTaskService(); + + TaskImpl createdTask1 = createTask(taskService, CallbackState.CALLBACK_PROCESSING_REQUIRED); + assertEquals(CallbackState.CALLBACK_PROCESSING_REQUIRED, createdTask1.getCallbackState()); + + TaskImpl createdTask2 = createTask(taskService, CallbackState.CLAIMED); + assertEquals(CallbackState.CLAIMED, createdTask2.getCallbackState()); + + TaskImpl createdTask3 = createTask(taskService, CallbackState.CALLBACK_PROCESSING_COMPLETED); + assertEquals(CallbackState.CALLBACK_PROCESSING_COMPLETED, createdTask3.getCallbackState()); + + List externalIds = new ArrayList<>( + Arrays.asList(createdTask1.getExternalId(), createdTask2.getExternalId(), createdTask3.getExternalId())); + + + //It's only allowed to set the CallbackState to REQUIRED if the TaskState doesn't equal COMPLETE + //Therefore 1 task should not get updated + BulkOperationResults bulkResult = taskService.setCallbackStateForTasks(externalIds, + CallbackState.CALLBACK_PROCESSING_REQUIRED); + assertTrue(bulkResult.containsErrors()); + List failedTaskIds = bulkResult.getFailedIds(); + assertTrue(failedTaskIds.size() == 1 && failedTaskIds.contains(createdTask3.getExternalId())); + + } + @WithAccessId( userName = "admin", groupNames = {"group_1"}) @Test void testQueriesWithCallbackState() throws WorkbasketNotFoundException, ClassificationNotFoundException, NotAuthorizedException, - TaskAlreadyExistException, InvalidArgumentException, TaskNotFoundException, InvalidStateException, InvalidOwnerException, SQLException, IOException { + TaskAlreadyExistException, InvalidArgumentException, TaskNotFoundException, InvalidStateException, + InvalidOwnerException, SQLException, IOException { resetDb(false); TaskService taskService = taskanaEngine.getTaskService(); @@ -180,7 +330,8 @@ class CallbackStateAccTest extends AbstractAccTest { .list(); long numberOfCompletedTasksAtStartOfTest = completedTasks.size(); List externalIds = completedTasks.stream().map(TaskSummary::getExternalId).collect(Collectors.toList()); - BulkOperationResults bulkResultCompleted = taskService.setCallbackStateForTasks(externalIds, CallbackState.CALLBACK_PROCESSING_REQUIRED); + BulkOperationResults bulkResultCompleted = taskService.setCallbackStateForTasks( + externalIds, CallbackState.CALLBACK_PROCESSING_REQUIRED); assertFalse(bulkResultCompleted.containsErrors()); // now complete some additional tasks @@ -196,7 +347,8 @@ class CallbackStateAccTest extends AbstractAccTest { assertTrue(tasksToBeActedUpon.size() == numberOfCompletedTasksAtStartOfTest); // now we set callback state to callback_processing_completed externalIds = tasksToBeActedUpon.stream().map(TaskSummary::getExternalId).collect(Collectors.toList()); - BulkOperationResults bulkResult = taskService.setCallbackStateForTasks(externalIds, CallbackState.CALLBACK_PROCESSING_COMPLETED); + BulkOperationResults bulkResult = taskService.setCallbackStateForTasks(externalIds, + CallbackState.CALLBACK_PROCESSING_COMPLETED); assertFalse(bulkResult.containsErrors()); long numOfTasksRemaining = taskService.createTaskQuery() @@ -207,7 +359,9 @@ class CallbackStateAccTest extends AbstractAccTest { } - private TaskImpl createTask(TaskService taskService, CallbackState callbackState) throws WorkbasketNotFoundException, ClassificationNotFoundException, NotAuthorizedException, TaskAlreadyExistException, InvalidArgumentException { + private TaskImpl createTask(TaskService taskService, CallbackState callbackState) + throws WorkbasketNotFoundException, ClassificationNotFoundException, NotAuthorizedException, + TaskAlreadyExistException, InvalidArgumentException { Task newTask = taskService.newTask("USER_1_1", "DOMAIN_A"); newTask.setClassificationKey("L12010"); newTask.setPrimaryObjRef(createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"));