This commit is contained in:
jamesrdi 2024-09-29 18:16:50 +02:00 committed by GitHub
commit 8b69d8076f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 1009 additions and 1 deletions

View File

@ -37,6 +37,12 @@
</dependency>
<!-- test dependencies -->
<dependency>
<groupId>pro.taskana</groupId>
<artifactId>taskana-test-api</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>pro.taskana</groupId>
<artifactId>taskana-common-data</artifactId>

View File

@ -0,0 +1,244 @@
package acceptance.events.task;
import static org.assertj.core.api.Assertions.assertThat;
import acceptance.events.task.CreateHistoryEventOnTaskRerouteAccTest.TaskRoutingProviderForDomainA;
import java.lang.reflect.Field;
import java.util.List;
import org.apache.ibatis.session.SqlSessionManager;
import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import pro.taskana.classification.api.ClassificationService;
import pro.taskana.classification.api.models.ClassificationSummary;
import pro.taskana.common.api.TaskanaEngine;
import pro.taskana.common.test.security.JaasExtension;
import pro.taskana.common.test.security.WithAccessId;
import pro.taskana.simplehistory.impl.SimpleHistoryServiceImpl;
import pro.taskana.simplehistory.impl.TaskHistoryQueryImpl;
import pro.taskana.simplehistory.impl.TaskanaHistoryEngineImpl;
import pro.taskana.simplehistory.impl.task.TaskHistoryQueryMapper;
import pro.taskana.spi.history.api.TaskanaHistory;
import pro.taskana.spi.history.api.events.task.TaskHistoryEvent;
import pro.taskana.spi.history.api.events.task.TaskHistoryEventType;
import pro.taskana.spi.routing.api.TaskRoutingProvider;
import pro.taskana.task.api.TaskService;
import pro.taskana.task.api.models.Task;
import pro.taskana.testapi.DefaultTestEntities;
import pro.taskana.testapi.TaskanaInject;
import pro.taskana.testapi.TaskanaIntegrationTest;
import pro.taskana.testapi.WithServiceProvider;
import pro.taskana.testapi.builder.TaskBuilder;
import pro.taskana.testapi.builder.WorkbasketAccessItemBuilder;
import pro.taskana.workbasket.api.WorkbasketPermission;
import pro.taskana.workbasket.api.WorkbasketService;
import pro.taskana.workbasket.api.models.Workbasket;
import pro.taskana.workbasket.api.models.WorkbasketSummary;
@WithServiceProvider(
serviceProviderInterface = TaskRoutingProvider.class,
serviceProviders = TaskRoutingProviderForDomainA.class)
@WithServiceProvider(
serviceProviderInterface = TaskanaHistory.class,
serviceProviders = SimpleHistoryServiceImpl.class)
@TaskanaIntegrationTest
@ExtendWith(JaasExtension.class)
class CreateHistoryEventOnTaskRerouteAccTest {
@TaskanaInject TaskanaEngine taskanaEngine;
@TaskanaInject TaskService taskService;
@TaskanaInject WorkbasketService workbasketService;
@TaskanaInject ClassificationService classificationService;
ClassificationSummary classificationSummary;
WorkbasketSummary domainAWorkbasketSummary;
WorkbasketSummary domainBWorkbasketSummary;
Task task1;
Task task2;
Task task3;
Task task4;
SimpleHistoryServiceImpl historyService;
TaskanaHistoryEngineImpl taskanaHistoryEngine;
@WithAccessId(user = "admin")
@BeforeAll
void setup() throws Exception {
historyService = new SimpleHistoryServiceImpl();
taskanaHistoryEngine = TaskanaHistoryEngineImpl.createTaskanaEngine(taskanaEngine);
historyService.initialize(taskanaEngine);
classificationSummary =
DefaultTestEntities.defaultTestClassification()
.buildAndStoreAsSummary(classificationService);
domainAWorkbasketSummary =
DefaultTestEntities.defaultTestWorkbasket()
.domain("DOMAIN_A")
.buildAndStoreAsSummary(workbasketService);
domainBWorkbasketSummary =
DefaultTestEntities.defaultTestWorkbasket()
.domain("DOMAIN_B")
.buildAndStoreAsSummary(workbasketService);
task1 =
TaskBuilder.newTask()
.classificationSummary(classificationSummary)
.workbasketSummary(domainAWorkbasketSummary)
.primaryObjRef(DefaultTestEntities.defaultTestObjectReference().build())
.buildAndStore(taskService);
task2 =
TaskBuilder.newTask()
.classificationSummary(classificationSummary)
.workbasketSummary(domainAWorkbasketSummary)
.primaryObjRef(DefaultTestEntities.defaultTestObjectReference().build())
.buildAndStore(taskService);
task3 =
TaskBuilder.newTask()
.classificationSummary(classificationSummary)
.workbasketSummary(domainBWorkbasketSummary)
.primaryObjRef(DefaultTestEntities.defaultTestObjectReference().build())
.buildAndStore(taskService);
task4 =
TaskBuilder.newTask()
.classificationSummary(classificationSummary)
.workbasketSummary(domainAWorkbasketSummary)
.primaryObjRef(DefaultTestEntities.defaultTestObjectReference().build())
.buildAndStore(taskService);
WorkbasketAccessItemBuilder.newWorkbasketAccessItem()
.workbasketId(domainAWorkbasketSummary.getId())
.accessId("user-1-1")
.permission(WorkbasketPermission.OPEN)
.permission(WorkbasketPermission.READ)
.permission(WorkbasketPermission.APPEND)
.buildAndStore(workbasketService);
WorkbasketAccessItemBuilder.newWorkbasketAccessItem()
.workbasketId(domainBWorkbasketSummary.getId())
.accessId("user-1-1")
.permission(WorkbasketPermission.OPEN)
.permission(WorkbasketPermission.READ)
.permission(WorkbasketPermission.APPEND)
.buildAndStore(workbasketService);
}
@WithAccessId(user = "admin")
@Test
void should_CreateRerouteHistoryEvent_When_TaskIsRerouted() throws Exception {
historyService.deleteHistoryEventsByTaskIds(List.of(task4.getId()));
TaskHistoryQueryMapper taskHistoryQueryMapper = getHistoryQueryMapper();
taskService.rerouteTask(task4.getId());
List<TaskHistoryEvent> events =
taskHistoryQueryMapper.queryHistoryEvents(
(TaskHistoryQueryImpl) historyService.createTaskHistoryQuery().taskIdIn(task4.getId()));
assertThat(events).hasSize(1);
String eventType = events.get(0).getEventType();
assertThat(eventType).isEqualTo(TaskHistoryEventType.REROUTED.getName());
assertRerouteHistoryEvent(
events.get(0).getId(),
domainAWorkbasketSummary.getId(),
domainBWorkbasketSummary.getId(),
"admin");
historyService.deleteHistoryEventsByTaskIds(List.of(task4.getId()));
}
@WithAccessId(user = "admin")
@Test
void should_CreateRerouteHistoryEvent_When_MultipleTasksAreRerouted() throws Exception {
List<String> taskIds = List.of(task1.getId(), task2.getId(), task3.getId());
historyService.deleteHistoryEventsByTaskIds(taskIds);
TaskHistoryQueryMapper taskHistoryQueryMapper = getHistoryQueryMapper();
taskService.rerouteTasks(taskIds);
List<TaskHistoryEvent> events =
taskHistoryQueryMapper.queryHistoryEvents(
(TaskHistoryQueryImpl)
historyService.createTaskHistoryQuery().taskIdIn(taskIds.toArray(new String[0])));
assertThat(events)
.extracting(TaskHistoryEvent::getTaskId)
.containsExactlyInAnyOrderElementsOf(taskIds);
for (TaskHistoryEvent event : events) {
if (event.getTaskId().equals(task1.getId())) {
assertRerouteHistoryEvent(
event.getId(),
domainAWorkbasketSummary.getId(),
domainBWorkbasketSummary.getId(),
"admin");
} else if (event.getTaskId().equals(task2.getId())) {
assertRerouteHistoryEvent(
event.getId(),
domainAWorkbasketSummary.getId(),
domainBWorkbasketSummary.getId(),
"admin");
} else {
assertRerouteHistoryEvent(
event.getId(),
domainBWorkbasketSummary.getId(),
domainAWorkbasketSummary.getId(),
"admin");
}
}
}
private TaskHistoryQueryMapper getHistoryQueryMapper()
throws NoSuchFieldException, IllegalAccessException {
Field sessionManagerField = TaskanaHistoryEngineImpl.class.getDeclaredField("sessionManager");
sessionManagerField.setAccessible(true);
SqlSessionManager sqlSessionManager =
(SqlSessionManager) sessionManagerField.get(taskanaHistoryEngine);
return sqlSessionManager.getMapper(TaskHistoryQueryMapper.class);
}
private void assertRerouteHistoryEvent(
String eventId, String expectedOldValue, String expectedNewValue, String expectedUser)
throws Exception {
TaskHistoryEvent event = historyService.getTaskHistoryEvent(eventId);
assertThat(event.getDetails()).isNotNull();
JSONArray changes = new JSONObject(event.getDetails()).getJSONArray("changes");
assertThat(changes.length()).isPositive();
boolean foundField = false;
for (int i = 0; i < changes.length() && !foundField; i++) {
JSONObject change = changes.getJSONObject(i);
if (change.get("fieldName").equals("workbasketSummary")) {
foundField = true;
String oldWorkbasketStr = change.get("oldValue").toString();
String newWorkbasketStr = change.get("newValue").toString();
Workbasket oldWorkbasket = workbasketService.getWorkbasket(expectedOldValue);
assertThat(oldWorkbasketStr)
.isEqualTo(JSONObject.wrap(oldWorkbasket.asSummary()).toString());
Workbasket newWorkbasket = workbasketService.getWorkbasket(expectedNewValue);
assertThat(newWorkbasketStr)
.isEqualTo(JSONObject.wrap(newWorkbasket.asSummary()).toString());
}
}
assertThat(foundField).describedAs("changes do not contain field 'workbasketSummary'").isTrue();
assertThat(event.getId()).startsWith("THI:");
assertThat(event.getOldValue()).isEqualTo(expectedOldValue);
assertThat(event.getNewValue()).isEqualTo(expectedNewValue);
assertThat(event.getUserId()).isEqualTo(expectedUser);
assertThat(event.getEventType()).isEqualTo(TaskHistoryEventType.REROUTED.getName());
}
class TaskRoutingProviderForDomainA implements TaskRoutingProvider {
@Override
public void initialize(TaskanaEngine taskanaEngine) {}
@Override
public String determineWorkbasketId(Task task) {
if ("DOMAIN_A".equals(task.getDomain())) {
return domainBWorkbasketSummary.getId();
} else if ("DOMAIN_B".equals(task.getDomain())) {
return domainAWorkbasketSummary.getId();
}
return null;
}
}
}

View File

@ -0,0 +1,368 @@
package acceptance.taskrouting;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowableOfType;
import static pro.taskana.testapi.DefaultTestEntities.defaultTestClassification;
import static pro.taskana.testapi.DefaultTestEntities.defaultTestObjectReference;
import static pro.taskana.testapi.DefaultTestEntities.defaultTestWorkbasket;
import acceptance.taskrouting.TaskReroutingAccTest.CustomTaskRoutingProvider;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import pro.taskana.classification.api.ClassificationService;
import pro.taskana.classification.api.models.ClassificationSummary;
import pro.taskana.common.api.BulkOperationResults;
import pro.taskana.common.api.TaskanaEngine;
import pro.taskana.common.api.exceptions.TaskanaException;
import pro.taskana.spi.routing.api.TaskRoutingProvider;
import pro.taskana.task.api.TaskService;
import pro.taskana.task.api.TaskState;
import pro.taskana.task.api.exceptions.InvalidTaskStateException;
import pro.taskana.task.api.exceptions.TaskNotFoundException;
import pro.taskana.task.api.models.ObjectReference;
import pro.taskana.task.api.models.Task;
import pro.taskana.task.api.models.TaskSummary;
import pro.taskana.testapi.TaskanaInject;
import pro.taskana.testapi.TaskanaIntegrationTest;
import pro.taskana.testapi.WithServiceProvider;
import pro.taskana.testapi.builder.ObjectReferenceBuilder;
import pro.taskana.testapi.builder.TaskBuilder;
import pro.taskana.testapi.builder.WorkbasketAccessItemBuilder;
import pro.taskana.testapi.security.WithAccessId;
import pro.taskana.workbasket.api.WorkbasketPermission;
import pro.taskana.workbasket.api.WorkbasketService;
import pro.taskana.workbasket.api.exceptions.NotAuthorizedOnWorkbasketException;
import pro.taskana.workbasket.api.models.WorkbasketSummary;
@WithServiceProvider(
serviceProviderInterface = TaskRoutingProvider.class,
serviceProviders = CustomTaskRoutingProvider.class)
@TaskanaIntegrationTest
class TaskReroutingAccTest {
@TaskanaInject TaskService taskService;
@TaskanaInject ClassificationService classificationService;
@TaskanaInject WorkbasketService workbasketService;
ClassificationSummary defaultClassificationSummary;
WorkbasketSummary defaultWorkbasketSummary;
WorkbasketSummary workbasketSummary1;
WorkbasketSummary workbasketSummary2;
WorkbasketSummary workbasketSummary3;
ObjectReference defaultObjectReference;
@WithAccessId(user = "businessadmin")
@BeforeAll
void setup() throws Exception {
defaultClassificationSummary =
defaultTestClassification().buildAndStoreAsSummary(classificationService);
defaultWorkbasketSummary = defaultTestWorkbasket().buildAndStoreAsSummary(workbasketService);
workbasketSummary1 =
defaultTestWorkbasket().key("key_1").buildAndStoreAsSummary(workbasketService);
workbasketSummary2 =
defaultTestWorkbasket().key("key_2").buildAndStoreAsSummary(workbasketService);
workbasketSummary3 =
defaultTestWorkbasket().key("key_3").buildAndStoreAsSummary(workbasketService);
defaultObjectReference = defaultTestObjectReference().build();
WorkbasketAccessItemBuilder.newWorkbasketAccessItem()
.workbasketId(defaultWorkbasketSummary.getId())
.accessId("user-1-2")
.permission(WorkbasketPermission.OPEN)
.permission(WorkbasketPermission.READ)
.permission(WorkbasketPermission.READTASKS)
.permission(WorkbasketPermission.EDITTASKS)
.permission(WorkbasketPermission.APPEND)
.permission(WorkbasketPermission.TRANSFER)
.buildAndStore(workbasketService);
WorkbasketAccessItemBuilder.newWorkbasketAccessItem()
.workbasketId(workbasketSummary1.getId())
.accessId("user-1-2")
.permission(WorkbasketPermission.OPEN)
.permission(WorkbasketPermission.READ)
.permission(WorkbasketPermission.READTASKS)
.permission(WorkbasketPermission.EDITTASKS)
.permission(WorkbasketPermission.APPEND)
.buildAndStore(workbasketService);
WorkbasketAccessItemBuilder.newWorkbasketAccessItem()
.workbasketId(workbasketSummary2.getId())
.accessId("user-1-2")
.permission(WorkbasketPermission.OPEN)
.permission(WorkbasketPermission.READ)
.permission(WorkbasketPermission.READTASKS)
.permission(WorkbasketPermission.EDITTASKS)
.buildAndStore(workbasketService);
WorkbasketAccessItemBuilder.newWorkbasketAccessItem()
.workbasketId(workbasketSummary3.getId())
.accessId("user-1-2")
.permission(WorkbasketPermission.OPEN)
.permission(WorkbasketPermission.READ)
.permission(WorkbasketPermission.READTASKS)
.permission(WorkbasketPermission.EDITTASKS)
.permission(WorkbasketPermission.APPEND)
.buildAndStore(workbasketService);
}
@WithAccessId(user = "taskadmin")
@Test
void should_RerouteTask_When_PorValueIsChanged() throws Exception {
Task task = createDefaultTask().buildAndStore(taskService, "admin");
Instant before = Instant.now().truncatedTo(ChronoUnit.MILLIS);
task.setPrimaryObjRef(createObjectReference("company", null, null, "MyType1", "Value1"));
taskService.updateTask(task);
Task reroutedTask = taskService.rerouteTask(task.getId());
assertTaskIsRerouted(before, reroutedTask.asSummary(), task.getState(), workbasketSummary1);
}
@WithAccessId(user = "user-1-2")
@Test
void should_NotRerouteTask_When_UserHasNoAppendPermissionToDestinationWb() throws Exception {
Task task = createDefaultTask().buildAndStore(taskService, "admin");
task.setPrimaryObjRef(createObjectReference("company", null, null, "MyType1", "Value2"));
taskService.updateTask(task);
ThrowingCallable call = () -> taskService.rerouteTask(task.getId());
NotAuthorizedOnWorkbasketException e =
catchThrowableOfType(call, NotAuthorizedOnWorkbasketException.class);
assertThat(e.getWorkbasketId()).isEqualTo(workbasketSummary2.getId());
assertThat(e.getCurrentUserId()).isEqualTo("user-1-2");
assertThat(e.getRequiredPermissions()).containsExactlyInAnyOrder(WorkbasketPermission.APPEND);
Task readTask = taskService.getTask(task.getId());
assertThat(readTask.getWorkbasketSummary().getId())
.isEqualTo(task.getWorkbasketSummary().getId());
}
@WithAccessId(user = "user-1-2")
@Test
void should_NotRerouteTask_When_UserHasNoTransferPermissionToOriginWb() throws Exception {
Task task =
createDefaultTask()
.workbasketSummary(workbasketSummary1)
.buildAndStore(taskService, "admin");
task.setPrimaryObjRef(createObjectReference("company", null, null, "MyType1", "Value2"));
taskService.updateTask(task);
ThrowingCallable call = () -> taskService.rerouteTask(task.getId());
NotAuthorizedOnWorkbasketException e =
catchThrowableOfType(call, NotAuthorizedOnWorkbasketException.class);
assertThat(e.getWorkbasketId()).isEqualTo(workbasketSummary1.getId());
assertThat(e.getCurrentUserId()).isEqualTo("user-1-2");
assertThat(e.getRequiredPermissions()).containsExactlyInAnyOrder(WorkbasketPermission.TRANSFER);
Task readTask = taskService.getTask(task.getId());
assertThat(readTask.getWorkbasketSummary().getId())
.isEqualTo(task.getWorkbasketSummary().getId());
}
@WithAccessId(user = "taskadmin")
@Test
void should_RerouteTasksToSameWb_When_PorValueIsChanged() throws Exception {
Task task1 = createDefaultTask().buildAndStore(taskService, "admin");
Task task2 = createDefaultTask().buildAndStore(taskService, "admin");
Task task3 = createDefaultTask().buildAndStore(taskService, "admin");
List<Task> tasks = Arrays.asList(task1, task2, task3);
Instant before = Instant.now().truncatedTo(ChronoUnit.MILLIS);
for (Task task : tasks) {
task.setPrimaryObjRef(createObjectReference("company", null, null, "MyType1", "Value1"));
taskService.updateTask(task);
}
List<String> taskIds = tasks.stream().map(Task::getId).collect(Collectors.toList());
BulkOperationResults<String, TaskanaException> results = taskService.rerouteTasks(taskIds);
assertThat(results.containsErrors()).isFalse();
List<TaskSummary> reroutedTasks =
taskService.createTaskQuery().idIn(taskIds.toArray(new String[0])).list();
assertThat(reroutedTasks).isNotEmpty();
for (int i = 0; i < reroutedTasks.size(); i++) {
assertTaskIsRerouted(
before, reroutedTasks.get(i), tasks.get(i).getState(), workbasketSummary1);
}
}
@WithAccessId(user = "taskadmin")
@Test
void should_RerouteTasksToMultipleWorkbaskets_When_PorValueIsChanged() throws Exception {
final Task task1 = createDefaultTask().buildAndStore(taskService, "admin");
final Task task2 = createDefaultTask().buildAndStore(taskService, "admin");
final Task task3 = createDefaultTask().buildAndStore(taskService, "admin");
Instant before = Instant.now().truncatedTo(ChronoUnit.MILLIS);
task1.setPrimaryObjRef(createObjectReference("company", null, null, "MyType1", "Value1"));
taskService.updateTask(task1);
task2.setPrimaryObjRef(createObjectReference("company", null, null, "MyType1", "Value2"));
taskService.updateTask(task2);
task3.setPrimaryObjRef(createObjectReference("company", null, null, "MyType1", "Value3"));
taskService.updateTask(task3);
List<Task> tasks = Arrays.asList(task1, task2, task3);
List<String> taskIds = tasks.stream().map(Task::getId).collect(Collectors.toList());
BulkOperationResults<String, TaskanaException> results = taskService.rerouteTasks(taskIds);
assertThat(results.containsErrors()).isFalse();
List<TaskSummary> reroutedTasks =
taskService.createTaskQuery().idIn(taskIds.toArray(new String[0])).list();
assertThat(reroutedTasks).isNotEmpty();
for (TaskSummary reroutedTask : reroutedTasks) {
if (reroutedTask.getId().equals(task1.getId())) {
assertTaskIsRerouted(before, reroutedTask, task1.getState(), workbasketSummary1);
} else if (reroutedTask.getId().equals(task2.getId())) {
assertTaskIsRerouted(before, reroutedTask, task2.getState(), workbasketSummary2);
} else {
assertTaskIsRerouted(before, reroutedTask, task3.getState(), workbasketSummary3);
}
}
}
@WithAccessId(user = "user-1-2")
@Test
void should_RerouteValidTasksEvenIfErrorsExist_When_PorValueIsChanged() throws Exception {
final Task taskToBeRerouted1 = createDefaultTask().buildAndStore(taskService, "admin");
final Task taskToBeRerouted3 = createDefaultTask().buildAndStore(taskService, "admin");
final Task taskNotNeededToReroute =
createDefaultTask()
.workbasketSummary(workbasketSummary1)
.buildAndStore(taskService, "admin");
final Task taskWithFinalState =
createDefaultTask().state(TaskState.COMPLETED).buildAndStore(taskService, "admin");
final Task taskWithNoTransferPerm =
createDefaultTask()
.workbasketSummary(workbasketSummary1)
.buildAndStore(taskService, "admin");
final Task taskWithNoAppendDestPerm = createDefaultTask().buildAndStore(taskService, "admin");
Instant before = Instant.now().truncatedTo(ChronoUnit.MILLIS);
taskToBeRerouted1.setPrimaryObjRef(
createObjectReference("company", null, null, "MyType1", "Value1"));
taskService.updateTask(taskToBeRerouted1);
taskToBeRerouted3.setPrimaryObjRef(
createObjectReference("company", null, null, "MyType1", "Value3"));
taskService.updateTask(taskToBeRerouted3);
taskNotNeededToReroute.setPrimaryObjRef("company", null, null, "MyType1", "Value1");
taskService.updateTask(taskNotNeededToReroute);
taskWithFinalState.setPrimaryObjRef("company", null, null, "MyType1", "Value1");
taskService.updateTask(taskWithFinalState);
taskWithNoTransferPerm.setPrimaryObjRef("company", null, null, "MyType1", "Value3");
taskService.updateTask(taskWithNoTransferPerm);
taskWithNoAppendDestPerm.setPrimaryObjRef("company", null, null, "MyType1", "Value2");
taskService.updateTask(taskWithNoAppendDestPerm);
List<Task> tasks =
Arrays.asList(
taskToBeRerouted1,
taskToBeRerouted3,
taskNotNeededToReroute,
taskWithFinalState,
taskWithNoTransferPerm,
taskWithNoAppendDestPerm);
List<String> taskIds = tasks.stream().map(Task::getId).collect(Collectors.toList());
taskIds.add("invalid-id");
BulkOperationResults<String, TaskanaException> results = taskService.rerouteTasks(taskIds);
final List<TaskSummary> reroutedTasks =
taskService.createTaskQuery().idIn(taskIds.toArray(new String[0])).list();
assertThat(results.containsErrors()).isTrue();
assertThat(results.getErrorMap()).hasSize(4);
assertThat(results.getErrorForId("invalid-id")).isOfAnyClassIn(TaskNotFoundException.class);
assertThat(results.getErrorForId(taskWithFinalState.getId()))
.isOfAnyClassIn(InvalidTaskStateException.class);
assertThat(results.getErrorForId(taskWithNoTransferPerm.getId()))
.isOfAnyClassIn(NotAuthorizedOnWorkbasketException.class);
assertThat(results.getErrorForId(taskWithNoAppendDestPerm.getId()))
.isOfAnyClassIn(NotAuthorizedOnWorkbasketException.class);
for (TaskSummary reroutedTask : reroutedTasks) {
if (reroutedTask.getId().equals(taskToBeRerouted1.getId())) {
assertTaskIsRerouted(
before, reroutedTask, taskToBeRerouted1.getState(), workbasketSummary1);
} else if (reroutedTask.getId().equals(taskToBeRerouted3.getId())) {
assertTaskIsRerouted(
before, reroutedTask, taskToBeRerouted3.getState(), workbasketSummary3);
} else if (reroutedTask.getId().equals(taskNotNeededToReroute.getId())) {
assertThat(reroutedTask).isEqualTo(taskNotNeededToReroute.asSummary());
} else if (reroutedTask.getId().equals(taskWithFinalState.getId())) {
assertThat(reroutedTask).isEqualTo(taskWithFinalState.asSummary());
} else if (reroutedTask.getId().equals(taskWithNoTransferPerm.getId())) {
assertThat(reroutedTask).isEqualTo(taskWithNoTransferPerm.asSummary());
} else if (reroutedTask.getId().equals(taskWithNoAppendDestPerm.getId())) {
assertThat(reroutedTask).isEqualTo(taskWithNoAppendDestPerm.asSummary());
}
}
}
ObjectReference createObjectReference(
String company, String system, String systemInstance, String type, String value) {
return ObjectReferenceBuilder.newObjectReference()
.company(company)
.system(system)
.systemInstance(systemInstance)
.type(type)
.value(value)
.build();
}
private void assertTaskIsRerouted(
Instant before,
TaskSummary reroutedTask,
TaskState stateBeforeTransfer,
WorkbasketSummary wbAfterReroute) {
assertThat(reroutedTask).isNotNull();
assertThat(reroutedTask.isRead()).isFalse();
assertThat(reroutedTask.isTransferred()).isTrue();
assertThat(reroutedTask.getState()).isEqualTo(getStateAfterTransfer(stateBeforeTransfer));
assertThat(reroutedTask.getOwner()).isNull();
assertThat(reroutedTask.getWorkbasketSummary().getId()).isEqualTo(wbAfterReroute.getId());
assertThat(reroutedTask.getDomain()).isEqualTo(wbAfterReroute.getDomain());
assertThat(reroutedTask.getModified()).isAfterOrEqualTo(before);
}
private TaskBuilder createDefaultTask() {
return (TaskBuilder.newTask()
.workbasketSummary(defaultWorkbasketSummary)
.primaryObjRef(defaultObjectReference))
.classificationSummary(defaultClassificationSummary);
}
private TaskState getStateAfterTransfer(TaskState stateBeforeTransfer) {
if (stateBeforeTransfer.equals(TaskState.CLAIMED)) {
return TaskState.READY;
}
if (stateBeforeTransfer.equals(TaskState.IN_REVIEW)) {
return TaskState.READY_FOR_REVIEW;
} else {
return stateBeforeTransfer;
}
}
class CustomTaskRoutingProvider implements TaskRoutingProvider {
@Override
public void initialize(TaskanaEngine taskanaEngine) {}
@Override
public String determineWorkbasketId(Task task) {
if (task.getPrimaryObjRef().getValue().equals("Value1")) {
return workbasketSummary1.getId();
} else if (task.getPrimaryObjRef().getValue().equals("Value2")) {
return workbasketSummary2.getId();
} else if (task.getPrimaryObjRef().getValue().equals("Value3")) {
return workbasketSummary3.getId();
}
return null;
}
}
}

View File

@ -11,7 +11,8 @@ public enum TaskHistoryEventType {
CANCELLED("CANCELLED"),
TERMINATED("TERMINATED"),
TRANSFERRED("TRANSFERRED"),
DELETED("DELETED");
DELETED("DELETED"),
REROUTED("REROUTED");
private String name;

View File

@ -0,0 +1,19 @@
package pro.taskana.spi.history.api.events.task;
import pro.taskana.task.api.models.TaskSummary;
public class TaskReroutedEvent extends TaskHistoryEvent {
public TaskReroutedEvent(
String id,
TaskSummary task,
String oldWorkbasketId,
String newWorkbasketId,
String userId,
String details) {
super(id, task, userId, details);
eventType = TaskHistoryEventType.REROUTED.getName();
this.oldValue = oldWorkbasketId;
this.newValue = newWorkbasketId;
}
}

View File

@ -1255,4 +1255,12 @@ public interface TaskService {
* @return a {@linkplain TaskCommentQuery}
*/
TaskCommentQuery createTaskCommentQuery();
Task rerouteTask(String taskId)
throws NotAuthorizedOnWorkbasketException,
TaskNotFoundException,
WorkbasketNotFoundException,
InvalidTaskStateException;
BulkOperationResults<String, TaskanaException> rerouteTasks(List<String> taskIds);
}

View File

@ -315,4 +315,6 @@ public interface TaskSummary {
* @return a copy of this TaskSummary
*/
TaskSummary copy();
Task asTask();
}

View File

@ -168,6 +168,47 @@ public interface TaskMapper {
void updateTransfered(
@Param("taskIds") Set<String> taskIds, @Param("referencetask") TaskImpl referencetask);
@Update(
"<script>"
+ " UPDATE TASK SET MODIFIED = CASE ID"
+ "<foreach item='task' index='index' separator='' collection='referenceTasks'>WHEN #{task.id, jdbcType=VARCHAR} THEN CAST(#{task.modified, jdbcType=TIMESTAMP} AS TIMESTAMP)"
+ "</foreach> END, "
+ " STATE = CASE ID "
+ "<foreach item='task' collection='referenceTasks' separator=' '>WHEN #{task.id, jdbcType=VARCHAR} THEN CAST(#{task.state} AS VARCHAR(20))"
+ "</foreach> END, "
+ " WORKBASKET_KEY = CASE ID "
+ "<foreach item='task' collection='referenceTasks' separator=' '>WHEN #{task.id, jdbcType=VARCHAR} THEN CAST(#{task.workbasketSummary.key, jdbcType=VARCHAR} AS VARCHAR(64))"
+ "</foreach> END, "
+ " WORKBASKET_ID = CASE ID "
+ "<foreach item='task' collection='referenceTasks' separator=' '>WHEN #{task.id, jdbcType=VARCHAR} THEN CAST(#{task.workbasketSummary.id, jdbcType=VARCHAR} AS VARCHAR(40))"
+ "</foreach> END, "
+ " DOMAIN = CASE ID "
+ "<foreach item='task' collection='referenceTasks' separator=' '>WHEN #{task.id, jdbcType=VARCHAR} THEN CAST(#{task.workbasketSummary.domain, jdbcType=VARCHAR} AS VARCHAR(32))"
+ "</foreach> END, "
+ " OWNER = NULL,"
+ " IS_READ = "
+ "<choose>"
+ "<when test =\"_databaseId == 'postgres'\">FALSE, </when><otherwise>0, </otherwise>"
+ "</choose>"
+ "IS_TRANSFERRED = CASE ID "
+ "<foreach item='task' collection='referenceTasks' separator=' '>WHEN #{task.id, jdbcType=VARCHAR} THEN "
+ "<choose>"
+ "<when test =\"_databaseId == 'postgres'\">#{task.isTransferred}</when>"
+ "<otherwise>"
+ "<choose>"
+ "<when test='task.isTransferred'>1</when>"
+ "<otherwise>0</otherwise>"
+ "</choose>"
+ "</otherwise>"
+ "</choose>"
+ "</foreach> END"
+ " WHERE ID IN "
+ "<foreach item='taskId' index='index' separator=',' open='(' close=')' collection='taskIds'> #{taskId, jdbcType=VARCHAR} </foreach>"
+ "</script>")
void updateTransferMultipleWorkbaskets(
@Param("taskIds") Set<String> taskIds,
@Param("referenceTasks") List<TaskSummary> referenceTasks);
@Update(
"<script>"
+ " UPDATE TASK SET COMPLETED = #{referenceTask.completed}, MODIFIED = #{referenceTask.modified}, STATE = #{referenceTask.state}, OWNER = #{referenceTask.owner}"

View File

@ -1088,6 +1088,19 @@ public class TaskServiceImpl implements TaskService {
return terminatedTask;
}
@Override
public Task rerouteTask(String taskId) throws NotAuthorizedOnWorkbasketException,
TaskNotFoundException,
WorkbasketNotFoundException,
InvalidTaskStateException {
return taskTransferrer.rerouteTask(taskId);
}
@Override
public BulkOperationResults<String, TaskanaException> rerouteTasks(List<String> taskIds) {
return taskTransferrer.rerouteTasks(taskIds);
}
public List<String> findTasksIdsAffectedByClassificationChange(String classificationId) {
// tasks directly affected
List<TaskSummary> tasksAffectedDirectly =

View File

@ -16,9 +16,12 @@ import pro.taskana.common.api.BulkOperationResults;
import pro.taskana.common.api.exceptions.InvalidArgumentException;
import pro.taskana.common.api.exceptions.TaskanaException;
import pro.taskana.common.internal.InternalTaskanaEngine;
import pro.taskana.common.internal.configuration.DB;
import pro.taskana.common.internal.util.EnumUtil;
import pro.taskana.common.internal.util.IdGenerator;
import pro.taskana.common.internal.util.ObjectAttributeChangeDetector;
import pro.taskana.common.internal.util.Pair;
import pro.taskana.spi.history.api.events.task.TaskReroutedEvent;
import pro.taskana.spi.history.api.events.task.TaskTransferredEvent;
import pro.taskana.spi.history.internal.HistoryEventManager;
import pro.taskana.task.api.TaskState;
@ -158,6 +161,69 @@ final class TaskTransferrer {
return transferSingleTask(taskId, destinationWorkbasket, owner, setTransferFlag);
}
Task rerouteTask(String taskId)
throws NotAuthorizedOnWorkbasketException,
TaskNotFoundException,
WorkbasketNotFoundException,
InvalidTaskStateException {
TaskImpl task = (TaskImpl) taskService.getTask(taskId);
WorkbasketSummary originWorkbasket = task.getWorkbasketSummary();
String newWorkbasketId = taskanaEngine.getTaskRoutingManager().determineWorkbasketId(task);
if (!originWorkbasket.getId().equals(newWorkbasketId)) {
try {
taskanaEngine.openConnection();
TaskImpl oldTask = task.copy();
oldTask.setId(task.getId());
oldTask.setExternalId(task.getExternalId());
WorkbasketSummary destinationWorkbasket =
workbasketService.getWorkbasket(newWorkbasketId).asSummary();
checkPreconditionsForTransferTask(task, destinationWorkbasket, originWorkbasket);
applyTransferValuesForTask(task, destinationWorkbasket, true);
taskMapper.update(task);
if (historyEventManager.isEnabled()) {
createReroutedEvent(
oldTask, task, originWorkbasket.getId(), destinationWorkbasket.getId());
}
return task;
} finally {
taskanaEngine.returnConnection();
}
}
return task;
}
BulkOperationResults<String, TaskanaException> rerouteTasks(List<String> taskIds) {
BulkOperationResults<String, TaskanaException> bulkLog = new BulkOperationResults<>();
List<TaskSummary> taskSummaries = filterNotExistingTaskIds(taskIds, bulkLog);
List<String> workbasketIds =
taskSummaries.stream()
.map(TaskSummary::asTask)
.map(task -> taskanaEngine.getTaskRoutingManager().determineWorkbasketId(task))
.collect(Collectors.toList());
filterOutTasksWhichNotNeededToBeTransferred(taskSummaries, workbasketIds);
List<String> taskIdsToBeTransferred =
taskSummaries.stream().map(TaskSummary::getId).collect(Collectors.toList());
filterOutTasksWhichDoNotMatchRerouteCriteria(
taskIdsToBeTransferred, taskSummaries, workbasketIds, bulkLog);
List<WorkbasketSummary> destinationWorkbaskets =
filterOutDestinationWbsWhichDoNotMatchTransferCriteria(
taskSummaries, workbasketIds, bulkLog);
try {
taskanaEngine.openConnection();
updateReroutableTasksWithDifferentWorkbaskets(taskSummaries, destinationWorkbaskets, true);
return bulkLog;
} finally {
taskanaEngine.returnConnection();
}
}
private Task transferSingleTask(
String taskId, WorkbasketSummary destinationWorkbasket, String owner, boolean setTransferFlag)
throws TaskNotFoundException,
@ -265,6 +331,33 @@ final class TaskTransferrer {
return filteredOutTasks;
}
private void filterOutTasksWhichDoNotMatchRerouteCriteria(
List<String> taskIds,
List<TaskSummary> taskSummaries,
List<String> workbasketIds,
BulkOperationResults<String, TaskanaException> bulkLog) {
Map<String, TaskSummary> taskIdToTaskSummary =
taskSummaries.stream().collect(Collectors.toMap(TaskSummary::getId, Function.identity()));
Set<String> sourceWorkbasketIds = getSourceWorkbasketIdsWithTransferPermission(taskSummaries);
for (String taskId : new HashSet<>(taskIds)) {
TaskSummary taskSummary = taskIdToTaskSummary.get(taskId);
Optional<TaskanaException> error =
checkTaskForTransferCriteria(sourceWorkbasketIds, taskId, taskSummary);
if (error.isPresent()) {
bulkLog.addError(taskId, error.get());
for (int i = 0; i < taskSummaries.size(); i++) {
if (taskSummaries.get(i).getId().equals(taskId)) {
taskSummaries.remove(i);
workbasketIds.remove(i);
break;
}
}
}
}
}
private Optional<TaskanaException> checkTaskForTransferCriteria(
Set<String> sourceWorkbasketIds, String taskId, TaskSummary taskSummary) {
TaskanaException error = null;
@ -372,6 +465,22 @@ final class TaskTransferrer {
details));
}
private void createReroutedEvent(
TaskSummary oldTask,
TaskSummary newTask,
String originWorkbasketId,
String destinationWorkbasketId) {
String details = ObjectAttributeChangeDetector.determineChangesInAttributes(oldTask, newTask);
historyEventManager.createEvent(
new TaskReroutedEvent(
IdGenerator.generateWithPrefix(IdGenerator.ID_PREFIX_TASK_HISTORY_EVENT),
newTask,
originWorkbasketId,
destinationWorkbasketId,
taskanaEngine.getEngine().getCurrentUserContext().getUserid(),
details));
}
private TaskState getStateAfterTransfer(TaskSummary taskSummary) {
TaskState stateBeforeTransfer = taskSummary.getState();
if (stateBeforeTransfer.equals(TaskState.CLAIMED)) {
@ -395,4 +504,127 @@ final class TaskTransferrer {
}
return result;
}
private void filterOutTasksWhichNotNeededToBeTransferred(
List<TaskSummary> taskSummaries, List<String> workbasketIds) {
for (int i = taskSummaries.size() - 1; i >= 0; i--) {
TaskSummary taskSummary = taskSummaries.get(i);
String workbasketId = workbasketIds.get(i);
if (taskSummary.getWorkbasketSummary().getId().equals(workbasketId)) {
taskSummaries.remove(i);
workbasketIds.remove(i);
}
}
}
private List<WorkbasketSummary> filterOutDestinationWbsWhichDoNotMatchTransferCriteria(
List<TaskSummary> taskSummaries,
List<String> workbasketIds,
BulkOperationResults<String, TaskanaException> bulkLog) {
List<WorkbasketSummary> wbsWithAppendPerm =
workbasketService
.createWorkbasketQuery()
.callerHasPermissions(WorkbasketPermission.APPEND)
.idIn(workbasketIds.toArray(new String[0]))
.list();
List<WorkbasketSummary> destinationWbSummaries = new ArrayList<>();
for (int i = taskSummaries.size() - 1; i >= 0; i--) {
String workbasketId = workbasketIds.get(i);
WorkbasketSummary matchingSummary =
wbsWithAppendPerm.stream()
.filter(summary -> workbasketId.equals(summary.getId()))
.findFirst()
.orElse(null);
if (matchingSummary == null) {
bulkLog.addError(
taskSummaries.get(i).getId(),
new NotAuthorizedOnWorkbasketException(
taskanaEngine.getEngine().getCurrentUserContext().getUserid(),
workbasketId,
WorkbasketPermission.APPEND));
workbasketIds.remove(i);
taskSummaries.remove(i);
} else if (matchingSummary.isMarkedForDeletion()) {
bulkLog.addError(
taskSummaries.get(i).getId(), new WorkbasketNotFoundException(matchingSummary.getId()));
workbasketIds.remove(i);
taskSummaries.remove(i);
} else {
destinationWbSummaries.add(matchingSummary);
}
}
Collections.reverse(destinationWbSummaries);
return destinationWbSummaries;
}
private void updateReroutableTasksWithDifferentWorkbaskets(
List<TaskSummary> taskSummaries,
List<WorkbasketSummary> workbasketSummaries,
boolean setTransferFlag) {
List<TaskSummary> oldTaskSummaries = new ArrayList<>();
for (TaskSummary taskSummary : taskSummaries) {
TaskSummaryImpl copy = new TaskSummaryImpl();
copy.setRead(taskSummary.isRead());
copy.setTransferred(taskSummary.isTransferred());
copy.setState(taskSummary.getState());
copy.setOwner(taskSummary.getOwner());
copy.setWorkbasketSummary(taskSummary.getWorkbasketSummary());
copy.setDomain(taskSummary.getDomain());
copy.setModified(taskSummary.getModified());
copy.setId(taskSummary.getId());
oldTaskSummaries.add(copy);
}
for (int i = 0; i < taskSummaries.size(); i++) {
applyTransferValuesForTask(
(TaskSummaryImpl) taskSummaries.get(i), workbasketSummaries.get(i), setTransferFlag);
}
Set<String> taskIds =
taskSummaries.stream().map(TaskSummary::getId).collect(Collectors.toSet());
taskMapper.updateTransferMultipleWorkbaskets(taskIds, taskSummaries);
if (historyEventManager.isEnabled()) {
for (int i = 0; i < taskSummaries.size(); i++) {
TaskSummary oldSummary = oldTaskSummaries.get(i);
TaskSummary newSummary = taskSummaries.get(i);
createReroutedEvent(
oldSummary,
newSummary,
oldSummary.getWorkbasketSummary().getId(),
newSummary.getWorkbasketSummary().getId());
}
}
}
private DB getDB() {
return DB.getDB(this.taskanaEngine.getSqlSession().getConfiguration().getDatabaseId());
}
private List<TaskSummary> filterNotExistingTaskIds(
List<String> taskIds, BulkOperationResults<String, TaskanaException> bulkLog) {
Map<String, TaskSummaryImpl> taskSummaryMap =
getTasksToChange(taskIds).stream()
.collect(Collectors.toMap(TaskSummary::getId, TaskSummaryImpl.class::cast));
return taskIds.stream()
.map(id -> Pair.of(id, taskSummaryMap.get(id)))
.filter(
pair -> {
if (pair.getRight() != null) {
return true;
}
String taskId = pair.getLeft();
bulkLog.addError(taskId, new TaskNotFoundException(taskId));
return false;
})
.map(Pair::getRight)
.collect(Collectors.toList());
}
private List<TaskSummary> getTasksToChange(List<String> taskIds) {
return taskanaEngine
.getEngine()
.runAsAdmin(
() -> taskService.createTaskQuery().idIn(taskIds.toArray(new String[0])).list());
}
}

View File

@ -12,8 +12,10 @@ import pro.taskana.common.api.exceptions.SystemException;
import pro.taskana.task.api.TaskCustomField;
import pro.taskana.task.api.TaskCustomIntField;
import pro.taskana.task.api.TaskState;
import pro.taskana.task.api.models.Attachment;
import pro.taskana.task.api.models.AttachmentSummary;
import pro.taskana.task.api.models.ObjectReference;
import pro.taskana.task.api.models.Task;
import pro.taskana.task.api.models.TaskSummary;
import pro.taskana.workbasket.api.models.WorkbasketSummary;
import pro.taskana.workbasket.internal.models.WorkbasketSummaryImpl;
@ -787,6 +789,78 @@ public class TaskSummaryImpl implements TaskSummary {
this.customInt8 = customInt8;
}
@Override
public Task asTask() {
TaskImpl task = new TaskImpl();
List<Attachment> attachments = new ArrayList<>();
for (AttachmentSummary attachmentSummary : attachmentSummaries) {
AttachmentImpl attachment = new AttachmentImpl();
attachment.setId(attachmentSummary.getId());
attachment.setTaskId(attachmentSummary.getTaskId());
attachment.setCreated(attachmentSummary.getCreated());
attachment.setModified(attachmentSummary.getModified());
attachment.setClassificationSummary(attachmentSummary.getClassificationSummary());
attachment.setObjectReference(attachmentSummary.getObjectReference());
attachment.setChannel(attachmentSummary.getChannel());
attachment.setReceived(attachmentSummary.getReceived());
attachments.add(attachment);
}
task.setAttachments(attachments);
task.setSecondaryObjectReferences(secondaryObjectReferences);
task.setBusinessProcessId(this.businessProcessId);
task.setClaimed(claimed);
if (classificationSummary != null) {
task.setClassificationSummary(classificationSummary);
}
task.setExternalId(externalId);
task.setCompleted(completed);
task.setCreated(created);
task.setCustom1(custom1);
task.setCustom2(custom2);
task.setCustom3(custom3);
task.setCustom4(custom4);
task.setCustom5(custom5);
task.setCustom6(custom6);
task.setCustom7(custom7);
task.setCustom8(custom8);
task.setCustom9(custom9);
task.setCustom10(custom10);
task.setCustom11(custom11);
task.setCustom12(custom12);
task.setCustom13(custom13);
task.setCustom14(custom14);
task.setCustom15(custom15);
task.setCustom16(custom16);
task.setCustomInt1(customInt1);
task.setCustomInt2(customInt2);
task.setCustomInt3(customInt3);
task.setCustomInt4(customInt4);
task.setCustomInt5(customInt5);
task.setCustomInt6(customInt6);
task.setCustomInt7(customInt7);
task.setCustomInt8(customInt8);
task.setDue(due);
task.setId(id);
task.setModified(modified);
task.setName(name);
task.setCreator(creator);
task.setNote(note);
task.setDescription(description);
task.setOwner(owner);
task.setOwnerLongName(ownerLongName);
task.setParentBusinessProcessId(parentBusinessProcessId);
task.setPlanned(planned);
task.setReceived(received);
task.setPrimaryObjRef(primaryObjRef);
task.setPriority(priority);
task.setManualPriority(manualPriority);
task.setRead(isRead);
task.setState(state);
task.setTransferred(isTransferred);
task.setWorkbasketSummary(workbasketSummary);
return task;
}
protected boolean canEqual(Object other) {
return (other instanceof TaskSummaryImpl);
}