Closes #2364 - Add Rerouting of Tasks in JAVA-API
This commit is contained in:
parent
5af380e27d
commit
421a607ead
|
@ -37,6 +37,12 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- test dependencies -->
|
<!-- test dependencies -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>pro.taskana</groupId>
|
||||||
|
<artifactId>taskana-test-api</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>pro.taskana</groupId>
|
<groupId>pro.taskana</groupId>
|
||||||
<artifactId>taskana-common-data</artifactId>
|
<artifactId>taskana-common-data</artifactId>
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,8 @@ public enum TaskHistoryEventType {
|
||||||
CANCELLED("CANCELLED"),
|
CANCELLED("CANCELLED"),
|
||||||
TERMINATED("TERMINATED"),
|
TERMINATED("TERMINATED"),
|
||||||
TRANSFERRED("TRANSFERRED"),
|
TRANSFERRED("TRANSFERRED"),
|
||||||
DELETED("DELETED");
|
DELETED("DELETED"),
|
||||||
|
REROUTED("REROUTED");
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1019,4 +1019,12 @@ public interface TaskService {
|
||||||
* @return a {@linkplain TaskCommentQuery}
|
* @return a {@linkplain TaskCommentQuery}
|
||||||
*/
|
*/
|
||||||
TaskCommentQuery createTaskCommentQuery();
|
TaskCommentQuery createTaskCommentQuery();
|
||||||
|
|
||||||
|
Task rerouteTask(String taskId)
|
||||||
|
throws NotAuthorizedOnWorkbasketException,
|
||||||
|
TaskNotFoundException,
|
||||||
|
WorkbasketNotFoundException,
|
||||||
|
InvalidTaskStateException;
|
||||||
|
|
||||||
|
BulkOperationResults<String, TaskanaException> rerouteTasks(List<String> taskIds);
|
||||||
}
|
}
|
||||||
|
|
|
@ -308,4 +308,6 @@ public interface TaskSummary {
|
||||||
* @return a copy of this TaskSummary
|
* @return a copy of this TaskSummary
|
||||||
*/
|
*/
|
||||||
TaskSummary copy();
|
TaskSummary copy();
|
||||||
|
|
||||||
|
Task asTask();
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,6 +167,47 @@ public interface TaskMapper {
|
||||||
void updateTransfered(
|
void updateTransfered(
|
||||||
@Param("taskIds") Set<String> taskIds, @Param("referencetask") TaskImpl referencetask);
|
@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(
|
@Update(
|
||||||
"<script>"
|
"<script>"
|
||||||
+ " UPDATE TASK SET COMPLETED = #{referenceTask.completed}, MODIFIED = #{referenceTask.modified}, STATE = #{referenceTask.state}, OWNER = #{referenceTask.owner}"
|
+ " UPDATE TASK SET COMPLETED = #{referenceTask.completed}, MODIFIED = #{referenceTask.modified}, STATE = #{referenceTask.state}, OWNER = #{referenceTask.owner}"
|
||||||
|
|
|
@ -1040,6 +1040,19 @@ public class TaskServiceImpl implements TaskService {
|
||||||
}
|
}
|
||||||
return terminatedTask;
|
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) {
|
public List<String> findTasksIdsAffectedByClassificationChange(String classificationId) {
|
||||||
// tasks directly affected
|
// tasks directly affected
|
||||||
|
|
|
@ -16,9 +16,12 @@ import pro.taskana.common.api.BulkOperationResults;
|
||||||
import pro.taskana.common.api.exceptions.InvalidArgumentException;
|
import pro.taskana.common.api.exceptions.InvalidArgumentException;
|
||||||
import pro.taskana.common.api.exceptions.TaskanaException;
|
import pro.taskana.common.api.exceptions.TaskanaException;
|
||||||
import pro.taskana.common.internal.InternalTaskanaEngine;
|
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.EnumUtil;
|
||||||
import pro.taskana.common.internal.util.IdGenerator;
|
import pro.taskana.common.internal.util.IdGenerator;
|
||||||
import pro.taskana.common.internal.util.ObjectAttributeChangeDetector;
|
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.api.events.task.TaskTransferredEvent;
|
||||||
import pro.taskana.spi.history.internal.HistoryEventManager;
|
import pro.taskana.spi.history.internal.HistoryEventManager;
|
||||||
import pro.taskana.task.api.TaskState;
|
import pro.taskana.task.api.TaskState;
|
||||||
|
@ -104,6 +107,69 @@ final class TaskTransferrer {
|
||||||
return transferMultipleTasks(taskIds, destinationWorkbasket, setTransferFlag);
|
return transferMultipleTasks(taskIds, destinationWorkbasket, 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(
|
private Task transferSingleTask(
|
||||||
String taskId, WorkbasketSummary destinationWorkbasket, boolean setTransferFlag)
|
String taskId, WorkbasketSummary destinationWorkbasket, boolean setTransferFlag)
|
||||||
throws TaskNotFoundException,
|
throws TaskNotFoundException,
|
||||||
|
@ -210,6 +276,33 @@ final class TaskTransferrer {
|
||||||
return filteredOutTasks;
|
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(
|
private Optional<TaskanaException> checkTaskForTransferCriteria(
|
||||||
Set<String> sourceWorkbasketIds, String taskId, TaskSummary taskSummary) {
|
Set<String> sourceWorkbasketIds, String taskId, TaskSummary taskSummary) {
|
||||||
TaskanaException error = null;
|
TaskanaException error = null;
|
||||||
|
@ -315,6 +408,22 @@ final class TaskTransferrer {
|
||||||
details));
|
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) {
|
private TaskState getStateAfterTransfer(TaskSummary taskSummary) {
|
||||||
TaskState stateBeforeTransfer = taskSummary.getState();
|
TaskState stateBeforeTransfer = taskSummary.getState();
|
||||||
if (stateBeforeTransfer.equals(TaskState.CLAIMED)) {
|
if (stateBeforeTransfer.equals(TaskState.CLAIMED)) {
|
||||||
|
@ -338,4 +447,127 @@ final class TaskTransferrer {
|
||||||
}
|
}
|
||||||
return result;
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,10 @@ import pro.taskana.common.api.exceptions.SystemException;
|
||||||
import pro.taskana.task.api.TaskCustomField;
|
import pro.taskana.task.api.TaskCustomField;
|
||||||
import pro.taskana.task.api.TaskCustomIntField;
|
import pro.taskana.task.api.TaskCustomIntField;
|
||||||
import pro.taskana.task.api.TaskState;
|
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.AttachmentSummary;
|
||||||
import pro.taskana.task.api.models.ObjectReference;
|
import pro.taskana.task.api.models.ObjectReference;
|
||||||
|
import pro.taskana.task.api.models.Task;
|
||||||
import pro.taskana.task.api.models.TaskSummary;
|
import pro.taskana.task.api.models.TaskSummary;
|
||||||
import pro.taskana.workbasket.api.models.WorkbasketSummary;
|
import pro.taskana.workbasket.api.models.WorkbasketSummary;
|
||||||
import pro.taskana.workbasket.internal.models.WorkbasketSummaryImpl;
|
import pro.taskana.workbasket.internal.models.WorkbasketSummaryImpl;
|
||||||
|
@ -776,6 +778,78 @@ public class TaskSummaryImpl implements TaskSummary {
|
||||||
this.customInt8 = customInt8;
|
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) {
|
protected boolean canEqual(Object other) {
|
||||||
return (other instanceof TaskSummaryImpl);
|
return (other instanceof TaskSummaryImpl);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue