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
This commit is contained in:
Jörg Heffner 2019-12-12 13:08:34 +01:00 committed by BerndBreier
parent b49a18048e
commit 536e09911a
3 changed files with 207 additions and 17 deletions

View File

@ -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
}

View File

@ -459,7 +459,7 @@ public class TaskServiceImpl implements TaskService {
Iterator<String> 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<String,
TaskanaException> bulkLog,
List<MinimalTaskSummary> taskSummaries, Iterator<String> externalIdIterator) {
List<MinimalTaskSummary> taskSummaries, Iterator<String> 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<String> updateTasks(ObjectReference selectionCriteria,
Map<String, String> customFieldsToUpdate) throws InvalidArgumentException {

View File

@ -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<String> taskIds = new ArrayList<>(Arrays.asList(createdTask1.getId(), createdTask2.getId(), createdTask3.getId()));
List<String> externalIds = new ArrayList<>(Arrays.asList(createdTask1.getExternalId(), createdTask2.getExternalId(), createdTask3.getExternalId()));
List<String> taskIds = new ArrayList<>(
Arrays.asList(createdTask1.getId(), createdTask2.getId(), createdTask3.getId()));
List<String> externalIds = new ArrayList<>(
Arrays.asList(createdTask1.getExternalId(), createdTask2.getExternalId(), createdTask3.getExternalId()));
// delete should fail because callback_state = CALLBACK_PROCESSING_REQUIRED
BulkOperationResults<String, TaskanaException> bulkResult1 = taskService.deleteTasks(taskIds);
@ -146,7 +153,8 @@ class CallbackStateAccTest extends AbstractAccTest {
}
// now enable deletion by setting callback state to CALLBACK_PROCESSING_COMPLETED
BulkOperationResults<String, TaskanaException> bulkResult2 = taskService.setCallbackStateForTasks(externalIds, CallbackState.CALLBACK_PROCESSING_COMPLETED);
BulkOperationResults<String, TaskanaException> 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<String> externalIds = new ArrayList<>(
Arrays.asList(createdTask1.getExternalId(), createdTask2.getExternalId(), createdTask3.getExternalId()));
//try to set CallbackState to NONE
BulkOperationResults<String, TaskanaException> bulkResult = taskService.setCallbackStateForTasks(externalIds,
CallbackState.NONE);
//It's never allowed to set CallbackState to NONE over public API
assertTrue(bulkResult.containsErrors());
List<String> 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<String> 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<String, TaskanaException> bulkResult = taskService.setCallbackStateForTasks(externalIds,
CallbackState.CALLBACK_PROCESSING_COMPLETED);
assertTrue(bulkResult.containsErrors());
List<String> 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<String> 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<String, TaskanaException> bulkResult = taskService.setCallbackStateForTasks(externalIds,
CallbackState.CLAIMED);
assertTrue(bulkResult.containsErrors());
List<String> 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<String> 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<String, TaskanaException> bulkResult = taskService.setCallbackStateForTasks(externalIds,
CallbackState.CALLBACK_PROCESSING_REQUIRED);
assertTrue(bulkResult.containsErrors());
List<String> 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<String> externalIds = completedTasks.stream().map(TaskSummary::getExternalId).collect(Collectors.toList());
BulkOperationResults<String, TaskanaException> bulkResultCompleted = taskService.setCallbackStateForTasks(externalIds, CallbackState.CALLBACK_PROCESSING_REQUIRED);
BulkOperationResults<String, TaskanaException> 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<String, TaskanaException> bulkResult = taskService.setCallbackStateForTasks(externalIds, CallbackState.CALLBACK_PROCESSING_COMPLETED);
BulkOperationResults<String, TaskanaException> 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"));