diff --git a/history/taskana-simplehistory-provider/pom.xml b/history/taskana-simplehistory-provider/pom.xml index 97d54044d..9029e084b 100644 --- a/history/taskana-simplehistory-provider/pom.xml +++ b/history/taskana-simplehistory-provider/pom.xml @@ -32,6 +32,12 @@ + + pro.taskana + taskana-data + ${project.version} + test + org.assertj assertj-core diff --git a/history/taskana-simplehistory-provider/src/main/java/pro/taskana/simplehistory/impl/HistoryEventImpl.java b/history/taskana-simplehistory-provider/src/main/java/pro/taskana/simplehistory/impl/HistoryEventImpl.java index d1fe4e7f2..15e3b4de2 100644 --- a/history/taskana-simplehistory-provider/src/main/java/pro/taskana/simplehistory/impl/HistoryEventImpl.java +++ b/history/taskana-simplehistory-provider/src/main/java/pro/taskana/simplehistory/impl/HistoryEventImpl.java @@ -7,7 +7,7 @@ public class HistoryEventImpl extends TaskanaHistoryEvent { public HistoryEventImpl() {} - public HistoryEventImpl(String userId, String details) { - super(userId, details); + public HistoryEventImpl(String id, String userId, String details) { + super(id, userId, details); } } diff --git a/history/taskana-simplehistory-provider/src/main/java/pro/taskana/simplehistory/impl/SimpleHistoryServiceImpl.java b/history/taskana-simplehistory-provider/src/main/java/pro/taskana/simplehistory/impl/SimpleHistoryServiceImpl.java index 920d8ed81..73f7a5a95 100644 --- a/history/taskana-simplehistory-provider/src/main/java/pro/taskana/simplehistory/impl/SimpleHistoryServiceImpl.java +++ b/history/taskana-simplehistory-provider/src/main/java/pro/taskana/simplehistory/impl/SimpleHistoryServiceImpl.java @@ -2,10 +2,14 @@ package pro.taskana.simplehistory.impl; import java.sql.SQLException; import java.time.Instant; +import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import pro.taskana.TaskanaEngineConfiguration; +import pro.taskana.common.api.TaskanaRole; +import pro.taskana.common.api.exceptions.InvalidArgumentException; +import pro.taskana.common.api.exceptions.NotAuthorizedException; import pro.taskana.simplehistory.impl.mappings.HistoryEventMapper; import pro.taskana.simplehistory.impl.mappings.HistoryQueryMapper; import pro.taskana.simplehistory.query.HistoryQuery; @@ -54,6 +58,32 @@ public class SimpleHistoryServiceImpl implements TaskanaHistory { } } + @Override + public void deleteHistoryEventsByTaskIds(List taskIds) + throws InvalidArgumentException, NotAuthorizedException { + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("entry to deleteHistoryEventsByTaskIds(taskIds = {})", taskIds); + } + + taskanaHistoryEngine.checkRoleMembership(TaskanaRole.ADMIN); + + try { + taskanaHistoryEngine.openConnection(); + if (taskIds == null) { + throw new InvalidArgumentException("List of taskIds must not be null."); + } + + historyEventMapper.deleteMultipleByTaskIds(taskIds); + + } catch (SQLException e) { + LOGGER.error("Caught exception while trying to delete history events", e); + } finally { + LOGGER.debug("exit from deleteHistoryEventsByTaskIds()"); + taskanaHistoryEngine.returnConnection(); + } + } + public TaskanaHistoryEvent getHistoryEvent(String historyEventId) throws TaskanaHistoryEventNotFoundException { LOGGER.debug("entry to getHistoryEvent (id = {})", historyEventId); diff --git a/history/taskana-simplehistory-provider/src/main/java/pro/taskana/simplehistory/impl/TaskanaHistoryEngineImpl.java b/history/taskana-simplehistory-provider/src/main/java/pro/taskana/simplehistory/impl/TaskanaHistoryEngineImpl.java index 74669eb1d..f4f3979d1 100644 --- a/history/taskana-simplehistory-provider/src/main/java/pro/taskana/simplehistory/impl/TaskanaHistoryEngineImpl.java +++ b/history/taskana-simplehistory-provider/src/main/java/pro/taskana/simplehistory/impl/TaskanaHistoryEngineImpl.java @@ -2,7 +2,10 @@ package pro.taskana.simplehistory.impl; import java.sql.SQLException; import java.util.ArrayDeque; +import java.util.Arrays; import java.util.Deque; +import java.util.HashSet; +import java.util.Set; import org.apache.ibatis.mapping.Environment; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.SqlSession; @@ -12,8 +15,13 @@ import org.apache.ibatis.session.SqlSessionManager; import org.apache.ibatis.transaction.TransactionFactory; import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; import org.apache.ibatis.transaction.managed.ManagedTransactionFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import pro.taskana.TaskanaEngineConfiguration; +import pro.taskana.common.api.TaskanaRole; +import pro.taskana.common.api.exceptions.NotAuthorizedException; +import pro.taskana.common.internal.security.CurrentUserContext; import pro.taskana.simplehistory.TaskanaHistoryEngine; import pro.taskana.simplehistory.impl.mappings.HistoryEventMapper; import pro.taskana.simplehistory.impl.mappings.HistoryQueryMapper; @@ -23,7 +31,7 @@ import pro.taskana.spi.history.api.TaskanaHistory; public class TaskanaHistoryEngineImpl implements TaskanaHistoryEngine { protected static final ThreadLocal> SESSION_STACK = new ThreadLocal<>(); - + private static final Logger LOGGER = LoggerFactory.getLogger(TaskanaHistoryEngineImpl.class); private static final String DEFAULT = "default"; protected SqlSessionManager sessionManager; protected TransactionFactory transactionFactory; @@ -52,6 +60,38 @@ public class TaskanaHistoryEngineImpl implements TaskanaHistoryEngine { return this.taskanaHistoryService; } + public boolean isUserInRole(TaskanaRole... roles) { + if (!getConfiguration().isSecurityEnabled()) { + return true; + } + + Set rolesMembers = + Arrays.stream(roles) + .map(role -> getConfiguration().getRoleMap().get(role)) + .collect(HashSet::new, Set::addAll, Set::addAll); + + return CurrentUserContext.getAccessIds().stream() + .anyMatch(rolesMembers::contains); + } + + public void checkRoleMembership(TaskanaRole... roles) throws NotAuthorizedException { + if (!isUserInRole(roles)) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug( + "Throwing NotAuthorizedException because accessIds {} are not member of roles {}", + CurrentUserContext.getAccessIds(), + Arrays.toString(roles)); + } + throw new NotAuthorizedException( + "current user is not member of role(s) " + Arrays.toString(roles), + CurrentUserContext.getUserid()); + } + } + + public TaskanaEngineConfiguration getConfiguration() { + return this.taskanaEngineConfiguration; + } + protected SqlSessionManager createSqlSessionManager() { Environment environment = new Environment( diff --git a/history/taskana-simplehistory-provider/src/main/java/pro/taskana/simplehistory/impl/mappings/HistoryEventMapper.java b/history/taskana-simplehistory-provider/src/main/java/pro/taskana/simplehistory/impl/mappings/HistoryEventMapper.java index a63ba7b0a..e9c35b9a7 100644 --- a/history/taskana-simplehistory-provider/src/main/java/pro/taskana/simplehistory/impl/mappings/HistoryEventMapper.java +++ b/history/taskana-simplehistory-provider/src/main/java/pro/taskana/simplehistory/impl/mappings/HistoryEventMapper.java @@ -1,5 +1,7 @@ package pro.taskana.simplehistory.impl.mappings; +import java.util.List; +import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Result; @@ -13,11 +15,11 @@ import pro.taskana.spi.history.api.events.TaskanaHistoryEvent; public interface HistoryEventMapper { @Insert( - "") + void deleteMultipleByTaskIds(@Param("taskIds") List taskIds); + } diff --git a/history/taskana-simplehistory-provider/src/test/java/acceptance/AbstractAccTest.java b/history/taskana-simplehistory-provider/src/test/java/acceptance/AbstractAccTest.java index e000772cc..df080866e 100644 --- a/history/taskana-simplehistory-provider/src/test/java/acceptance/AbstractAccTest.java +++ b/history/taskana-simplehistory-provider/src/test/java/acceptance/AbstractAccTest.java @@ -1,27 +1,39 @@ package acceptance; -import configuration.DbWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.Field; import java.util.Objects; import java.util.Properties; import javax.sql.DataSource; import org.apache.ibatis.datasource.pooled.PooledDataSource; +import org.apache.ibatis.session.SqlSessionManager; import org.junit.jupiter.api.BeforeAll; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import pro.taskana.TaskanaEngineConfiguration; +import pro.taskana.common.api.TaskanaEngine; +import pro.taskana.common.api.TaskanaEngine.ConnectionManagementMode; +import pro.taskana.common.internal.util.IdGenerator; +import pro.taskana.sampledata.SampleDataGenerator; import pro.taskana.simplehistory.impl.HistoryEventImpl; import pro.taskana.simplehistory.impl.SimpleHistoryServiceImpl; +import pro.taskana.simplehistory.impl.TaskanaHistoryEngineImpl; +import pro.taskana.simplehistory.impl.mappings.HistoryQueryMapper; /** Set up database for tests. */ public abstract class AbstractAccTest { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractAccTest.class); + private static final String ID_PREFIX_HISTORY_EVENT = "HEI"; + + protected static TaskanaEngineConfiguration taskanaEngineConfiguration; + protected static TaskanaHistoryEngineImpl taskanaHistoryEngine; + protected static TaskanaEngine taskanaEngine; private static final String USER_HOME_DIRECTORY = System.getProperty("user.home"); private static final int POOL_TIME_TO_WAIT = 50; @@ -72,17 +84,20 @@ public abstract class AbstractAccTest { protected static void resetDb(String schemaName) throws Exception { DataSource dataSource = getDataSource(); - TaskanaEngineConfiguration taskanaEngineConfiguration = + taskanaEngineConfiguration = new TaskanaEngineConfiguration( dataSource, false, schemaName != null && !schemaName.isEmpty() ? schemaName : getSchemaName()); + taskanaHistoryEngine = TaskanaHistoryEngineImpl.createTaskanaEngine(taskanaEngineConfiguration); + taskanaEngine = taskanaEngineConfiguration.buildTaskanaEngine(); + taskanaEngine.setConnectionManagementMode(ConnectionManagementMode.AUTOCOMMIT); historyService = new SimpleHistoryServiceImpl(); historyService.initialize(taskanaEngineConfiguration); - DbWriter writer = new DbWriter(); - writer.clearDB(dataSource); - writer.generateTestData(dataSource); + SampleDataGenerator sampleDataGenerator = new SampleDataGenerator(dataSource, getSchemaName()); + sampleDataGenerator.clearDb(); + sampleDataGenerator.generateTestData(); } protected static DataSource getDataSource() { @@ -117,6 +132,12 @@ public abstract class AbstractAccTest { protected static SimpleHistoryServiceImpl getHistoryService() { return historyService; + * @param id the id of the event + String id, + HistoryEventImpl historyEvent = + new HistoryEventImpl( + IdGenerator.generateWithPrefix(ID_PREFIX_HISTORY_EVENT), userid, details); + historyEvent.setId(id); } @BeforeAll @@ -236,4 +257,15 @@ public abstract class AbstractAccTest { return schemaName; } + + protected HistoryQueryMapper getHistoryQueryMapper() + throws NoSuchFieldException, IllegalAccessException { + + Field sessionManagerField = TaskanaHistoryEngineImpl.class.getDeclaredField("sessionManager"); + sessionManagerField.setAccessible(true); + SqlSessionManager sqlSessionManager = + (SqlSessionManager) sessionManagerField.get(taskanaHistoryEngine); + + return sqlSessionManager.getMapper(HistoryQueryMapper.class); + } } diff --git a/history/taskana-simplehistory-provider/src/test/java/acceptance/events/CreateHistoryEventOnCancelClaimAccTest.java b/history/taskana-simplehistory-provider/src/test/java/acceptance/events/CreateHistoryEventOnCancelClaimAccTest.java new file mode 100644 index 000000000..499db154a --- /dev/null +++ b/history/taskana-simplehistory-provider/src/test/java/acceptance/events/CreateHistoryEventOnCancelClaimAccTest.java @@ -0,0 +1,60 @@ +package acceptance.events; + +import static org.assertj.core.api.Assertions.assertThat; + +import acceptance.AbstractAccTest; +import acceptance.security.JaasExtension; +import acceptance.security.WithAccessId; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import pro.taskana.simplehistory.impl.HistoryEventImpl; +import pro.taskana.simplehistory.impl.HistoryQueryImpl; +import pro.taskana.simplehistory.impl.SimpleHistoryServiceImpl; +import pro.taskana.simplehistory.impl.mappings.HistoryQueryMapper; +import pro.taskana.task.api.TaskService; +import pro.taskana.task.api.TaskState; +import pro.taskana.task.api.models.Task; + +@ExtendWith(JaasExtension.class) +class CreateHistoryEventOnCancelClaimAccTest extends AbstractAccTest { + + private TaskService taskService; + private SimpleHistoryServiceImpl historyService; + + @BeforeEach + public void setUp() { + + taskService = taskanaEngine.getTaskService(); + historyService = getHistoryService(); + } + + @WithAccessId(user = "admin") + @Test + void should_CreateCancelClaimedHistoryEvent_When_TaskIsCancelClaimed() throws Exception { + + final String taskId = "TKI:000000000000000000000000000000000043"; + + HistoryQueryMapper historyQueryMapper = getHistoryQueryMapper(); + + List listEvents = + historyQueryMapper.queryHistoryEvent( + (HistoryQueryImpl) historyService.createHistoryQuery().taskIdIn(taskId)); + + assertThat(listEvents).hasSize(0); + + assertThat(taskService.getTask(taskId).getState()).isEqualTo(TaskState.CLAIMED); + Task task = taskService.forceCancelClaim(taskId); + assertThat(task.getState()).isEqualTo(TaskState.READY); + + listEvents = + historyQueryMapper.queryHistoryEvent( + (HistoryQueryImpl) historyService.createHistoryQuery().taskIdIn(taskId)); + + assertThat(listEvents).hasSize(1); + assertThat(historyService.getHistoryEvent(listEvents.get(0).getId()).getEventType()) + .isEqualTo("TASK_CLAIM_CANCELLED"); + } +} diff --git a/history/taskana-simplehistory-provider/src/test/java/acceptance/events/CreateHistoryEventOnClaimAccTest.java b/history/taskana-simplehistory-provider/src/test/java/acceptance/events/CreateHistoryEventOnClaimAccTest.java new file mode 100644 index 000000000..bc3912a1c --- /dev/null +++ b/history/taskana-simplehistory-provider/src/test/java/acceptance/events/CreateHistoryEventOnClaimAccTest.java @@ -0,0 +1,60 @@ +package acceptance.events; + +import static org.assertj.core.api.Assertions.assertThat; + +import acceptance.AbstractAccTest; +import acceptance.security.JaasExtension; +import acceptance.security.WithAccessId; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import pro.taskana.simplehistory.impl.HistoryEventImpl; +import pro.taskana.simplehistory.impl.HistoryQueryImpl; +import pro.taskana.simplehistory.impl.SimpleHistoryServiceImpl; +import pro.taskana.simplehistory.impl.mappings.HistoryQueryMapper; +import pro.taskana.task.api.TaskService; +import pro.taskana.task.api.TaskState; +import pro.taskana.task.api.models.Task; + +@ExtendWith(JaasExtension.class) +class CreateHistoryEventOnClaimAccTest extends AbstractAccTest { + + private TaskService taskService; + private SimpleHistoryServiceImpl historyService; + + @BeforeEach + public void setUp() { + + taskService = taskanaEngine.getTaskService(); + historyService = getHistoryService(); + } + + @WithAccessId(user = "admin") + @Test + void should_CreateClaimedHistoryEvent_When_TaskIsClaimed() throws Exception { + + final String taskId = "TKI:000000000000000000000000000000000047"; + + HistoryQueryMapper historyQueryMapper = getHistoryQueryMapper(); + + List listEvents = + historyQueryMapper.queryHistoryEvent( + (HistoryQueryImpl) historyService.createHistoryQuery().taskIdIn(taskId)); + + assertThat(listEvents).hasSize(0); + + assertThat(taskService.getTask(taskId).getState()).isEqualTo(TaskState.READY); + Task task = taskService.claim(taskId); + assertThat(task.getState()).isEqualTo(TaskState.CLAIMED); + + listEvents = + historyQueryMapper.queryHistoryEvent( + (HistoryQueryImpl) historyService.createHistoryQuery().taskIdIn(taskId)); + + assertThat(listEvents).hasSize(1); + assertThat(historyService.getHistoryEvent(listEvents.get(0).getId()).getEventType()) + .isEqualTo("TASK_CLAIMED"); + } +} diff --git a/history/taskana-simplehistory-provider/src/test/java/acceptance/events/CreateHistoryEventOnCompletionAccTest.java b/history/taskana-simplehistory-provider/src/test/java/acceptance/events/CreateHistoryEventOnCompletionAccTest.java new file mode 100644 index 000000000..17e789c35 --- /dev/null +++ b/history/taskana-simplehistory-provider/src/test/java/acceptance/events/CreateHistoryEventOnCompletionAccTest.java @@ -0,0 +1,58 @@ +package acceptance.events; + +import static org.assertj.core.api.Assertions.assertThat; + +import acceptance.AbstractAccTest; +import acceptance.security.JaasExtension; +import acceptance.security.WithAccessId; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import pro.taskana.simplehistory.impl.HistoryEventImpl; +import pro.taskana.simplehistory.impl.HistoryQueryImpl; +import pro.taskana.simplehistory.impl.SimpleHistoryServiceImpl; +import pro.taskana.simplehistory.impl.mappings.HistoryQueryMapper; +import pro.taskana.task.api.TaskService; +import pro.taskana.task.api.TaskState; + +@ExtendWith(JaasExtension.class) +class CreateHistoryEventOnCompletionAccTest extends AbstractAccTest { + + private TaskService taskService; + private SimpleHistoryServiceImpl historyService; + + @BeforeEach + public void setUp() { + + taskService = taskanaEngine.getTaskService(); + historyService = getHistoryService(); + } + + @WithAccessId(user = "admin") + @Test + void should_CreateCompletedHistoryEvent_When_TaskIsCompleted() throws Exception { + + final String taskId = "TKI:000000000000000000000000000000000001"; + + HistoryQueryMapper historyQueryMapper = getHistoryQueryMapper(); + + List listEvents = + historyQueryMapper.queryHistoryEvent( + (HistoryQueryImpl) historyService.createHistoryQuery().taskIdIn(taskId)); + + assertThat(listEvents).hasSize(0); + + assertThat(taskService.getTask(taskId).getState()).isEqualTo(TaskState.CLAIMED); + taskService.forceCompleteTask(taskId); + + listEvents = + historyQueryMapper.queryHistoryEvent( + (HistoryQueryImpl) historyService.createHistoryQuery().taskIdIn(taskId)); + + assertThat(listEvents).hasSize(1); + assertThat(historyService.getHistoryEvent(listEvents.get(0).getId()).getEventType()) + .isEqualTo("TASK_COMPLETED"); + } +} diff --git a/history/taskana-simplehistory-provider/src/test/java/acceptance/events/CreateHistoryEventOnTaskCreationAccTest.java b/history/taskana-simplehistory-provider/src/test/java/acceptance/events/CreateHistoryEventOnTaskCreationAccTest.java new file mode 100644 index 000000000..8ff788960 --- /dev/null +++ b/history/taskana-simplehistory-provider/src/test/java/acceptance/events/CreateHistoryEventOnTaskCreationAccTest.java @@ -0,0 +1,65 @@ +package acceptance.events; + +import static org.assertj.core.api.Assertions.assertThat; + +import acceptance.AbstractAccTest; +import acceptance.security.JaasExtension; +import acceptance.security.WithAccessId; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import pro.taskana.simplehistory.impl.HistoryEventImpl; +import pro.taskana.simplehistory.impl.HistoryQueryImpl; +import pro.taskana.simplehistory.impl.SimpleHistoryServiceImpl; +import pro.taskana.simplehistory.impl.mappings.HistoryQueryMapper; +import pro.taskana.task.api.TaskService; +import pro.taskana.task.api.models.ObjectReference; +import pro.taskana.task.internal.models.TaskImpl; + +@ExtendWith(JaasExtension.class) +class CreateHistoryEventOnTaskCreationAccTest extends AbstractAccTest { + + private TaskService taskService; + private SimpleHistoryServiceImpl historyService; + + @BeforeEach + public void setUp() { + + taskService = taskanaEngine.getTaskService(); + historyService = getHistoryService(); + } + + protected ObjectReference createObjectRef( + String company, String system, String systemInstance, String type, String value) { + ObjectReference objectRef = new ObjectReference(); + objectRef.setCompany(company); + objectRef.setSystem(system); + objectRef.setSystemInstance(systemInstance); + objectRef.setType(type); + objectRef.setValue(value); + return objectRef; + } + + @Test + @WithAccessId(user = "admin") + void should_CreateCreatedHistoryEvent_When_TaskIsCreated() throws Exception { + + TaskImpl newTask = (TaskImpl) taskService.newTask("WBI:100000000000000000000000000000000006"); + newTask.setClassificationKey("T2100"); + ObjectReference objectReference = + createObjectRef("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"); + newTask.setPrimaryObjRef(objectReference); + taskService.createTask(newTask); + + HistoryQueryMapper historyQueryMapper = getHistoryQueryMapper(); + + List listEvents = + historyQueryMapper.queryHistoryEvent( + (HistoryQueryImpl) historyService.createHistoryQuery().taskIdIn(newTask.getId())); + + assertThat(listEvents).hasSize(1); + assertThat(listEvents.get(0).getEventType()).isEqualTo("TASK_CREATED"); + } +} diff --git a/history/taskana-simplehistory-provider/src/test/java/acceptance/events/CreateHistoryEventOnTaskUpdateAccTest.java b/history/taskana-simplehistory-provider/src/test/java/acceptance/events/CreateHistoryEventOnTaskUpdateAccTest.java new file mode 100644 index 000000000..752d0cdf3 --- /dev/null +++ b/history/taskana-simplehistory-provider/src/test/java/acceptance/events/CreateHistoryEventOnTaskUpdateAccTest.java @@ -0,0 +1,60 @@ +package acceptance.events; + +import static org.assertj.core.api.Assertions.assertThat; + +import acceptance.AbstractAccTest; +import acceptance.security.JaasExtension; +import acceptance.security.WithAccessId; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import pro.taskana.simplehistory.impl.HistoryEventImpl; +import pro.taskana.simplehistory.impl.HistoryQueryImpl; +import pro.taskana.simplehistory.impl.SimpleHistoryServiceImpl; +import pro.taskana.simplehistory.impl.mappings.HistoryQueryMapper; +import pro.taskana.task.api.TaskService; +import pro.taskana.task.api.models.Task; + +@ExtendWith(JaasExtension.class) +class CreateHistoryEventOnTaskUpdateAccTest extends AbstractAccTest { + + private TaskService taskService; + private SimpleHistoryServiceImpl historyService; + + @BeforeEach + public void setUp() { + + taskService = taskanaEngine.getTaskService(); + historyService = getHistoryService(); + } + + @Test + @WithAccessId(user = "admin") + void should_CreateUpdatedHistoryEvent_When_TaskIsCreated() throws Exception { + + final String taskId = "TKI:000000000000000000000000000000000000"; + HistoryQueryMapper historyQueryMapper = getHistoryQueryMapper(); + + List listEvents = + historyQueryMapper.queryHistoryEvent( + (HistoryQueryImpl) historyService.createHistoryQuery().taskIdIn(taskId)); + + assertThat(listEvents).hasSize(2); + + Task task = taskService.getTask(taskId); + task.setName("someUpdatedName"); + taskService.updateTask(task); + + listEvents = + historyQueryMapper.queryHistoryEvent( + (HistoryQueryImpl) historyService.createHistoryQuery().taskIdIn(taskId)); + + assertThat(listEvents).hasSize(3); + assertThat(listEvents.get(2).getEventType()).isEqualTo("TASK_UPDATED"); + + assertThat(historyService.getHistoryEvent(listEvents.get(2).getId()).getDetails()) + .contains("someUpdatedName"); + } +} diff --git a/history/taskana-simplehistory-provider/src/test/java/acceptance/events/CreateHistoryEventOnTransferAccTest.java b/history/taskana-simplehistory-provider/src/test/java/acceptance/events/CreateHistoryEventOnTransferAccTest.java new file mode 100644 index 000000000..2aa91065b --- /dev/null +++ b/history/taskana-simplehistory-provider/src/test/java/acceptance/events/CreateHistoryEventOnTransferAccTest.java @@ -0,0 +1,56 @@ +package acceptance.events; + +import static org.assertj.core.api.Assertions.assertThat; + +import acceptance.AbstractAccTest; +import acceptance.security.JaasExtension; +import acceptance.security.WithAccessId; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import pro.taskana.simplehistory.impl.HistoryEventImpl; +import pro.taskana.simplehistory.impl.HistoryQueryImpl; +import pro.taskana.simplehistory.impl.SimpleHistoryServiceImpl; +import pro.taskana.simplehistory.impl.mappings.HistoryQueryMapper; +import pro.taskana.task.api.TaskService; + +@ExtendWith(JaasExtension.class) +class CreateHistoryEventOnTransferAccTest extends AbstractAccTest { + + private TaskService taskService; + private SimpleHistoryServiceImpl historyService; + + @BeforeEach + public void setUp() { + + taskService = taskanaEngine.getTaskService(); + historyService = getHistoryService(); + } + + @WithAccessId(user = "admin") + @Test + void should_CreateTransferredHistoryEvent_When_TaskIstransferred() throws Exception { + + final String taskId = "TKI:000000000000000000000000000000000003"; + + HistoryQueryMapper historyQueryMapper = getHistoryQueryMapper(); + + List listEvents = + historyQueryMapper.queryHistoryEvent( + (HistoryQueryImpl) historyService.createHistoryQuery().taskIdIn(taskId)); + + assertThat(listEvents).hasSize(0); + + taskService.transfer(taskId, "WBI:100000000000000000000000000000000006"); + + listEvents = + historyQueryMapper.queryHistoryEvent( + (HistoryQueryImpl) historyService.createHistoryQuery().taskIdIn(taskId)); + + assertThat(listEvents).hasSize(1); + assertThat(historyService.getHistoryEvent(listEvents.get(0).getId()).getEventType()) + .isEqualTo("TASK_TRANSFERRED"); + } +} diff --git a/history/taskana-simplehistory-provider/src/test/java/acceptance/events/DeleteHistoryEventsOnTaskDeletionAccTest.java b/history/taskana-simplehistory-provider/src/test/java/acceptance/events/DeleteHistoryEventsOnTaskDeletionAccTest.java new file mode 100644 index 000000000..a5a36a8b4 --- /dev/null +++ b/history/taskana-simplehistory-provider/src/test/java/acceptance/events/DeleteHistoryEventsOnTaskDeletionAccTest.java @@ -0,0 +1,173 @@ +package acceptance.events; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import acceptance.AbstractAccTest; +import acceptance.security.JaasExtension; +import acceptance.security.WithAccessId; +import java.util.Arrays; +import java.util.List; +import org.assertj.core.api.ThrowableAssert.ThrowingCallable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import pro.taskana.simplehistory.impl.HistoryEventImpl; +import pro.taskana.simplehistory.impl.HistoryQueryImpl; +import pro.taskana.simplehistory.impl.SimpleHistoryServiceImpl; +import pro.taskana.simplehistory.impl.mappings.HistoryQueryMapper; +import pro.taskana.task.api.TaskService; +import pro.taskana.task.api.exceptions.TaskNotFoundException; + +@ExtendWith(JaasExtension.class) +class DeleteHistoryEventsOnTaskDeletionAccTest extends AbstractAccTest { + + private TaskService taskService; + private SimpleHistoryServiceImpl historyService; + + @BeforeEach + public void setUp() { + + taskService = taskanaEngine.getTaskService(); + historyService = getHistoryService(); + } + + @Test + @WithAccessId(user = "admin") + void should_deleteHistoryEvents_When_TaskIsDeleted_With_HistoryDeletionEnabled() + throws Exception { + + final String taskid = "TKI:000000000000000000000000000000000036"; + taskanaEngineConfiguration.setDeleteHistoryOnTaskDeletionEnabled(true); + + HistoryQueryMapper historyQueryMapper = getHistoryQueryMapper(); + + List listEvents = + historyQueryMapper.queryHistoryEvent( + (HistoryQueryImpl) historyService.createHistoryQuery().taskIdIn(taskid)); + assertThat(listEvents).hasSize(2); + + taskService.deleteTask(taskid); + + // make sure the task got deleted + ThrowingCallable getDeletedTaskCall = + () -> { + taskService.getTask(taskid); + }; + + assertThatThrownBy(getDeletedTaskCall).isInstanceOf(TaskNotFoundException.class); + + listEvents = + historyQueryMapper.queryHistoryEvent( + (HistoryQueryImpl) historyService.createHistoryQuery().taskIdIn(taskid)); + assertThat(listEvents).hasSize(0); + } + + @Test + @WithAccessId(user = "admin") + void should_deleteHistoryEvents_When_TasksAreDeleted_With_HistoryDeletionEnabled() + throws Exception { + + final String taskId_1 = "TKI:000000000000000000000000000000000037"; + final String taskId_2 = "TKI:000000000000000000000000000000000038"; + + taskanaEngineConfiguration.setDeleteHistoryOnTaskDeletionEnabled(true); + + HistoryQueryMapper historyQueryMapper = getHistoryQueryMapper(); + + List listEvents = + historyQueryMapper.queryHistoryEvent( + (HistoryQueryImpl) historyService.createHistoryQuery().taskIdIn(taskId_1, taskId_2)); + assertThat(listEvents).hasSize(3); + + taskService.deleteTasks(Arrays.asList(taskId_1, taskId_2)); + + // make sure the tasks got deleted + ThrowingCallable getDeletedTaskCall = + () -> { + taskService.getTask(taskId_1); + }; + ThrowingCallable getDeletedTaskCall2 = + () -> { + taskService.getTask(taskId_2); + }; + + assertThatThrownBy(getDeletedTaskCall).isInstanceOf(TaskNotFoundException.class); + assertThatThrownBy(getDeletedTaskCall2).isInstanceOf(TaskNotFoundException.class); + + listEvents = + historyQueryMapper.queryHistoryEvent( + (HistoryQueryImpl) historyService.createHistoryQuery().taskIdIn(taskId_1, taskId_2)); + assertThat(listEvents).hasSize(0); + } + + @Test + @WithAccessId(user = "admin") + void should_notDeleteHistoryEvents_When_TaskIsDeleted_With_HistoryDeletionDisabled() + throws Exception { + + final String taskId = "TKI:000000000000000000000000000000000039"; + + taskanaEngineConfiguration.setDeleteHistoryOnTaskDeletionEnabled(false); + + HistoryQueryMapper historyQueryMapper = getHistoryQueryMapper(); + + List listEvents = + historyQueryMapper.queryHistoryEvent( + (HistoryQueryImpl) historyService.createHistoryQuery().taskIdIn(taskId)); + assertThat(listEvents).hasSize(2); + + taskService.deleteTask(taskId); + + // make sure the task got deleted + ThrowingCallable getDeletedTaskCall = + () -> { + taskService.getTask(taskId); + }; + + assertThatThrownBy(getDeletedTaskCall).isInstanceOf(TaskNotFoundException.class); + + listEvents = + historyQueryMapper.queryHistoryEvent( + (HistoryQueryImpl) historyService.createHistoryQuery().taskIdIn(taskId)); + assertThat(listEvents).hasSize(2); + } + + @Test + @WithAccessId(user = "admin") + void should_notDeleteHistoryEvents_When_TasksAreDeleted_With_HistoryDeletionDisabled() + throws Exception { + final String taskId_1 = "TKI:000000000000000000000000000000000040"; + final String taskId_2 = "TKI:000000000000000000000000000000000068"; + + taskanaEngineConfiguration.setDeleteHistoryOnTaskDeletionEnabled(false); + + HistoryQueryMapper historyQueryMapper = getHistoryQueryMapper(); + + List listEvents = + historyQueryMapper.queryHistoryEvent( + (HistoryQueryImpl) historyService.createHistoryQuery().taskIdIn(taskId_1, taskId_2)); + assertThat(listEvents).hasSize(2); + + taskService.deleteTasks(Arrays.asList(taskId_1, taskId_2)); + + // make sure the tasks got deleted + ThrowingCallable getDeletedTaskCall = + () -> { + taskService.getTask(taskId_1); + }; + ThrowingCallable getDeletedTaskCall2 = + () -> { + taskService.getTask(taskId_2); + }; + + assertThatThrownBy(getDeletedTaskCall).isInstanceOf(TaskNotFoundException.class); + assertThatThrownBy(getDeletedTaskCall2).isInstanceOf(TaskNotFoundException.class); + + listEvents = + historyQueryMapper.queryHistoryEvent( + (HistoryQueryImpl) historyService.createHistoryQuery().taskIdIn(taskId_1, taskId_2)); + assertThat(listEvents).hasSize(2); + } +} diff --git a/history/taskana-simplehistory-provider/src/test/java/acceptance/events/GetHistoryEventAccTest.java b/history/taskana-simplehistory-provider/src/test/java/acceptance/events/GetHistoryEventAccTest.java index 50bba6c5c..fbaa6fe8f 100644 --- a/history/taskana-simplehistory-provider/src/test/java/acceptance/events/GetHistoryEventAccTest.java +++ b/history/taskana-simplehistory-provider/src/test/java/acceptance/events/GetHistoryEventAccTest.java @@ -7,10 +7,11 @@ import org.junit.jupiter.api.Test; import pro.taskana.spi.history.api.events.TaskanaHistoryEvent; + class GetHistoryEventAccTest extends AbstractAccTest { @Test - void should_ReturnSpecificTaskHistoryEventWithDetails_For_HistoryEventId() throws Exception { + void should_ReturnSpecificTaskHistoryEventWithDetails_For_HistoryEventId() String detailsJson = "{\"changes\":[{" @@ -21,7 +22,8 @@ class GetHistoryEventAccTest extends AbstractAccTest { + "\"fieldName\":\"owner\"," + "\"oldValue\":\"owner1\"}]}"; - TaskanaHistoryEvent taskHistoryEvent = getHistoryService().getHistoryEvent("4"); + TaskanaHistoryEvent taskHistoryEvent = + getHistoryService().getHistoryEvent("HEI:000000000000000000000000000000000000"); assertThat(taskHistoryEvent.getBusinessProcessId()).isEqualTo("BPI:01"); assertThat(taskHistoryEvent.getUserId()).isEqualTo("admin"); assertThat(taskHistoryEvent.getEventType()).isEqualTo("TASK_UPDATED"); diff --git a/history/taskana-simplehistory-provider/src/test/java/acceptance/query/QueryHistoryAccTest.java b/history/taskana-simplehistory-provider/src/test/java/acceptance/query/QueryHistoryAccTest.java index 2a359f95b..7d95b5dd5 100644 --- a/history/taskana-simplehistory-provider/src/test/java/acceptance/query/QueryHistoryAccTest.java +++ b/history/taskana-simplehistory-provider/src/test/java/acceptance/query/QueryHistoryAccTest.java @@ -31,7 +31,7 @@ class QueryHistoryAccTest extends AbstractAccTest { .createHistoryQuery() .listValues(HistoryQueryColumnName.CREATED, SortDirection.ASCENDING); - assertThat(ascendingList).hasSize(2); + assertThat(ascendingList).hasSize(13); assertThat(ascendingList).isEqualTo(defaultList); List descendingList = @@ -55,12 +55,12 @@ class QueryHistoryAccTest extends AbstractAccTest { List results = query.list(); assertThat(results) .extracting(TaskanaHistoryEvent::getUserId) - .containsExactly("admin", "peter"); + .containsOnly("admin", "peter"); results = query.orderByUserId(SortDirection.DESCENDING).list(); assertThat(results) .extracting(TaskanaHistoryEvent::getUserId) - .containsExactly("admin", "peter"); - assertThat(query.domainLike().count()).isEqualTo(3); + .containsOnly("admin", "peter"); + assertThat(query.domainLike().count()).isEqualTo(13); } @Test @@ -76,7 +76,7 @@ class QueryHistoryAccTest extends AbstractAccTest { @Test void should_ReturnEmptyList_When_ProvidingWrongContraints() { List result = getHistoryService().createHistoryQuery().list(1, 1000); - assertThat(result).hasSize(2); + assertThat(result).hasSize(12); result = getHistoryService().createHistoryQuery().list(100, 1000); assertThat(result).isEmpty(); @@ -94,10 +94,10 @@ class QueryHistoryAccTest extends AbstractAccTest { @Test void should_ReturnCountOfEvents_When_UsingCountMethod() { long count = getHistoryService().createHistoryQuery().userIdIn("peter").count(); - assertThat(count).isOne(); + assertThat(count).isEqualTo(6); count = getHistoryService().createHistoryQuery().count(); - assertThat(count).isEqualTo(3); + assertThat(count).isEqualTo(13); count = getHistoryService().createHistoryQuery().userIdIn("klaus", "arnold", "benni").count(); assertThat(count).isZero(); @@ -107,11 +107,11 @@ class QueryHistoryAccTest extends AbstractAccTest { void should_ReturnHistoryEvents_For_DifferentInAttributes() { List returnValues = getHistoryService().createHistoryQuery().businessProcessIdIn("BPI:01", "BPI:02").list(); - assertThat(returnValues).hasSize(2); + assertThat(returnValues).hasSize(7); returnValues = getHistoryService().createHistoryQuery().parentBusinessProcessIdIn("BPI:01").list(); - assertThat(returnValues).hasSize(1); + assertThat(returnValues).hasSize(6); returnValues = getHistoryService() @@ -121,63 +121,63 @@ class QueryHistoryAccTest extends AbstractAccTest { assertThat(returnValues).hasSize(2); returnValues = getHistoryService().createHistoryQuery().eventTypeIn("TASK_CREATED").list(); - assertThat(returnValues).hasSize(2); + assertThat(returnValues).hasSize(12); TimeInterval timeInterval = new TimeInterval(Instant.now().minusSeconds(10), Instant.now()); returnValues = getHistoryService().createHistoryQuery().createdWithin(timeInterval).list(); - assertThat(returnValues).hasSize(2); + assertThat(returnValues).hasSize(0); returnValues = getHistoryService().createHistoryQuery().userIdIn("admin").list(); - assertThat(returnValues).hasSize(2); + assertThat(returnValues).hasSize(7); returnValues = getHistoryService().createHistoryQuery().domainIn("DOMAIN_A").list(); - assertThat(returnValues).hasSize(2); + assertThat(returnValues).hasSize(12); returnValues = getHistoryService() .createHistoryQuery() .workbasketKeyIn("WBI:100000000000000000000000000000000001") .list(); - assertThat(returnValues).hasSize(2); + assertThat(returnValues).hasSize(7); returnValues = getHistoryService().createHistoryQuery().porCompanyIn("00").list(); - assertThat(returnValues).hasSize(2); + assertThat(returnValues).hasSize(7); returnValues = getHistoryService().createHistoryQuery().porSystemIn("PASystem").list(); - assertThat(returnValues).hasSize(2); + assertThat(returnValues).hasSize(7); returnValues = getHistoryService().createHistoryQuery().porInstanceIn("22").list(); - assertThat(returnValues).hasSize(1); + assertThat(returnValues).hasSize(6); returnValues = getHistoryService().createHistoryQuery().porTypeIn("VN").list(); assertThat(returnValues).isEmpty(); returnValues = getHistoryService().createHistoryQuery().porValueIn("11223344").list(); - assertThat(returnValues).hasSize(2); + assertThat(returnValues).hasSize(7); returnValues = getHistoryService().createHistoryQuery().taskClassificationKeyIn("L140101").list(); - assertThat(returnValues).hasSize(2); + assertThat(returnValues).hasSize(7); returnValues = getHistoryService().createHistoryQuery().taskClassificationCategoryIn("TASK").list(); - assertThat(returnValues).hasSize(2); + assertThat(returnValues).hasSize(7); returnValues = getHistoryService() .createHistoryQuery() .attachmentClassificationKeyIn("DOCTYPE_DEFAULT") .list(); - assertThat(returnValues).hasSize(1); + assertThat(returnValues).hasSize(6); returnValues = getHistoryService().createHistoryQuery().custom1In("custom1").list(); - assertThat(returnValues).hasSize(3); + assertThat(returnValues).hasSize(13); returnValues = getHistoryService().createHistoryQuery().custom2In("custom2").list(); assertThat(returnValues).hasSize(1); returnValues = getHistoryService().createHistoryQuery().custom3In("custom3").list(); - assertThat(returnValues).hasSize(2); + assertThat(returnValues).hasSize(7); returnValues = getHistoryService().createHistoryQuery().custom4In("custom4").list(); assertThat(returnValues).hasSize(1); @@ -192,35 +192,35 @@ class QueryHistoryAccTest extends AbstractAccTest { assertThat(returnValues).hasSize(1); returnValues = getHistoryService().createHistoryQuery().newValueLike("new_%").list(); - assertThat(returnValues).hasSize(2); + assertThat(returnValues).hasSize(7); } @Test void should_ReturnHistoryEvents_For_DifferentLikeAttributes() { List returnValues = getHistoryService().createHistoryQuery().businessProcessIdLike("BPI:0%").list(); - assertThat(returnValues).hasSize(3); + assertThat(returnValues).hasSize(13); returnValues = getHistoryService().createHistoryQuery().parentBusinessProcessIdLike("BPI:01", " %").list(); - assertThat(returnValues).hasSize(1); + assertThat(returnValues).hasSize(6); returnValues = getHistoryService().createHistoryQuery().taskIdLike("TKI:000000000000000%").list(); - assertThat(returnValues).hasSize(3); + assertThat(returnValues).hasSize(13); returnValues = getHistoryService().createHistoryQuery().oldValueLike("old%").list(); assertThat(returnValues).hasSize(1); returnValues = getHistoryService().createHistoryQuery().newValueLike("new_%").list(); - assertThat(returnValues).hasSize(2); + assertThat(returnValues).hasSize(7); } @Test void should_ReturnHistoryEvents_When_ProvidingListValues() { List returnedList = getHistoryService().createHistoryQuery().listValues(HistoryQueryColumnName.ID, null); - assertThat(returnedList).hasSize(3); + assertThat(returnedList).hasSize(13); returnedList = getHistoryService() @@ -236,7 +236,7 @@ class QueryHistoryAccTest extends AbstractAccTest { returnedList = getHistoryService().createHistoryQuery().listValues(HistoryQueryColumnName.TASK_ID, null); - assertThat(returnedList).hasSize(2); + assertThat(returnedList).hasSize(7); returnedList = getHistoryService() @@ -246,7 +246,7 @@ class QueryHistoryAccTest extends AbstractAccTest { returnedList = getHistoryService().createHistoryQuery().listValues(HistoryQueryColumnName.CREATED, null); - assertThat(returnedList).hasSize(2); + assertThat(returnedList).hasSize(13); returnedList = getHistoryService().createHistoryQuery().listValues(HistoryQueryColumnName.USER_ID, null); diff --git a/history/taskana-simplehistory-provider/src/test/java/acceptance/security/JaasExtension.java b/history/taskana-simplehistory-provider/src/test/java/acceptance/security/JaasExtension.java new file mode 100644 index 000000000..7a4ec81db --- /dev/null +++ b/history/taskana-simplehistory-provider/src/test/java/acceptance/security/JaasExtension.java @@ -0,0 +1,329 @@ +package acceptance.security; + +import static org.junit.platform.commons.support.AnnotationSupport.isAnnotated; +import static pro.taskana.common.internal.util.CheckedFunction.wrap; + +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.security.Principal; +import java.security.PrivilegedAction; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; +import javax.security.auth.Subject; +import org.junit.jupiter.api.DynamicContainer; +import org.junit.jupiter.api.DynamicNode; +import org.junit.jupiter.api.extension.Extension; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ExtensionContext.Namespace; +import org.junit.jupiter.api.extension.ExtensionContext.Store; +import org.junit.jupiter.api.extension.InvocationInterceptor; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolver; +import org.junit.jupiter.api.extension.ReflectiveInvocationContext; +import org.junit.jupiter.api.extension.TestTemplateInvocationContext; +import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider; +import org.junit.platform.commons.JUnitException; +import org.junit.platform.commons.support.AnnotationSupport; + +import pro.taskana.common.api.exceptions.SystemException; +import pro.taskana.common.internal.security.GroupPrincipal; +import pro.taskana.common.internal.security.UserPrincipal; + +/** Runner for integration tests that enables JAAS subject. */ +public class JaasExtension implements InvocationInterceptor, TestTemplateInvocationContextProvider { + + private static final String ACCESS_IDS_STORE_KEY = "accessIds"; + + // region InvocationInterceptor + + @Override + public T interceptTestClassConstructor( + Invocation invocation, + ReflectiveInvocationContext> invocationContext, + ExtensionContext extensionContext) { + return extractAccessIdAndPerformInvocation(invocation, invocationContext.getExecutable()); + } + + @Override + public void interceptBeforeAllMethod( + Invocation invocation, + ReflectiveInvocationContext invocationContext, + ExtensionContext extensionContext) { + extractAccessIdAndPerformInvocation(invocation, invocationContext.getExecutable()); + } + + @Override + public void interceptBeforeEachMethod( + Invocation invocation, + ReflectiveInvocationContext invocationContext, + ExtensionContext extensionContext) { + extractAccessIdAndPerformInvocation(invocation, invocationContext.getExecutable()); + } + + @Override + public void interceptTestMethod( + Invocation invocation, + ReflectiveInvocationContext invocationContext, + ExtensionContext extensionContext) { + if (isAnnotated(invocationContext.getExecutable(), WithAccessIds.class)) { + throw new JUnitException("Please use @TestTemplate instead of @Test for multiple accessIds"); + } + extractAccessIdAndPerformInvocation(invocation, invocationContext.getExecutable()); + } + + @Override + @SuppressWarnings("unchecked") + public T interceptTestFactoryMethod( + Invocation invocation, + ReflectiveInvocationContext invocationContext, + ExtensionContext extensionContext) { + WithAccessIds annotation = invocationContext.getExecutable().getAnnotation( + WithAccessIds.class); + if (annotation != null) { + // our goal is to run each test returned from the test factory X times. X is the amount of + // WithAccessId annotations. In order to achieve this we are wrapping the result from the + // factory (the returning tests) in a dynamicContainer for each accessId. Since we don't know + // what the factory will return we have to check for every possible return type. All possible + // return types can be found here: + // https://junit.org/junit5/docs/current/user-guide/#writing-tests-dynamic-tests + // After checking each return type we abuse the return type of T and hardly change it to + // Stream no matter what the factory returns. This return type is allowed + // per definition (See link above), but is not the type T. Hence we have an unchecked cast at + // the end to keep the compiler happy... + + // we are using the first annotation to run the factory method with. + T factoryResult = performInvocationWithAccessId(invocation, annotation.value()[0]); + + Iterable newChildrenForDynamicContainer; + // TestFactory must have one of the following return types. See link above for further details + if (factoryResult instanceof DynamicNode) { + newChildrenForDynamicContainer = Collections.singleton((DynamicNode) factoryResult); + } else if (factoryResult instanceof Stream) { + Stream nodes = (Stream) factoryResult; + newChildrenForDynamicContainer = nodes.collect(Collectors.toList()); + } else if (factoryResult instanceof Iterable) { + newChildrenForDynamicContainer = (Iterable) factoryResult; + } else if (factoryResult instanceof Iterator) { + newChildrenForDynamicContainer = () -> (Iterator) factoryResult; + } else if (factoryResult instanceof DynamicNode[]) { + newChildrenForDynamicContainer = Arrays.asList((DynamicNode[]) factoryResult); + } else { + throw new SystemException( + String.format( + "Testfactory '%s' did not return a proper type", + invocationContext.getExecutable().getName())); + } + + // Currently a DynamicContainer has children from this type: Stream + // Because of this the children can only be extracted once (Streams can only be operated + // once). This is obviously not ok since we want to execute each node X times. So we have to + // manually persist all children recursively to extract them X times... + Map> childrenMap = new HashMap<>(); + persistDynamicContainerChildren(newChildrenForDynamicContainer, childrenMap); + + Function wrapTestsInDynamicContainer = + accessId -> + DynamicContainer.dynamicContainer( + getDisplayNameForAccessId(accessId), + StreamSupport.stream(newChildrenForDynamicContainer.spliterator(), false) + .map(x -> duplicateDynamicNode(x, childrenMap))); + + Store store = getStore(extensionContext); + return (T) + Stream.of(annotation.value()) + .peek(a -> store.put(ACCESS_IDS_STORE_KEY, a)) + .map(wrapTestsInDynamicContainer); + } + + return extractAccessIdAndPerformInvocation(invocation, invocationContext.getExecutable()); + } + + @Override + public void interceptTestTemplateMethod( + Invocation invocation, + ReflectiveInvocationContext invocationContext, + ExtensionContext extensionContext) { + WithAccessId accessId = + getStore(extensionContext).get(ACCESS_IDS_STORE_KEY, WithAccessId.class); + performInvocationWithAccessId(invocation, accessId); + } + + @Override + public void interceptDynamicTest(Invocation invocation, ExtensionContext extensionContext) { + ExtensionContext testContext = getParentMethodExtensionContent(extensionContext); + // Check if the test factory provided an access Id for this dynamic test. + WithAccessId o = getStore(testContext).get(ACCESS_IDS_STORE_KEY, WithAccessId.class); + if (o != null) { + performInvocationWithAccessId(invocation, o); + } else { + extractAccessIdAndPerformInvocation(invocation, testContext.getRequiredTestMethod()); + } + } + + @Override + public void interceptAfterEachMethod( + Invocation invocation, + ReflectiveInvocationContext invocationContext, + ExtensionContext extensionContext) { + extractAccessIdAndPerformInvocation(invocation, invocationContext.getExecutable()); + } + + @Override + public void interceptAfterAllMethod( + Invocation invocation, + ReflectiveInvocationContext invocationContext, + ExtensionContext extensionContext) { + extractAccessIdAndPerformInvocation(invocation, invocationContext.getExecutable()); + } + + // endregion + + // region TestTemplateInvocationContextProvider + + @Override + public boolean supportsTestTemplate(ExtensionContext context) { + return isAnnotated(context.getElement(), WithAccessIds.class) + || isAnnotated(context.getElement(), WithAccessId.class); + } + + @Override + public Stream provideTestTemplateInvocationContexts( + ExtensionContext context) { + List accessIds = + AnnotationSupport.findRepeatableAnnotations(context.getElement(), WithAccessId.class); + Store store = getStore(context); + return accessIds.stream() + .peek(a -> store.put(ACCESS_IDS_STORE_KEY, a)) + .map(JaasExtensionInvocationContext::new); + } + + // endregion + + private static void persistDynamicContainerChildren( + Iterable nodes, Map> childrenMap) { + nodes.forEach( + node -> { + if (node instanceof DynamicContainer) { + DynamicContainer container = (DynamicContainer) node; + List children = container.getChildren().collect(Collectors.toList()); + childrenMap.put(container.hashCode() + container.getDisplayName(), children); + persistDynamicContainerChildren(children, childrenMap); + } + }); + } + + private static DynamicNode duplicateDynamicNode( + DynamicNode node, Map> lookupMap) { + if (node instanceof DynamicContainer) { + DynamicContainer container = (DynamicContainer) node; + Stream children = + lookupMap.get(node.hashCode() + node.getDisplayName()).stream() + .map(x -> duplicateDynamicNode(x, lookupMap)); + return DynamicContainer.dynamicContainer(container.getDisplayName(), children); + } + return node; + } + + private static T extractAccessIdAndPerformInvocation( + Invocation invocation, AnnotatedElement executable) { + return performInvocationWithAccessId(invocation, executable.getAnnotation( + WithAccessId.class)); + } + + private static T performInvocationWithAccessId( + Invocation invocation, WithAccessId withAccessId) { + Subject subject = new Subject(); + subject.getPrincipals().addAll(getPrincipals(withAccessId)); + + Function, T> proceedInvocation = wrap(Invocation::proceed); + PrivilegedAction performInvocation = () -> proceedInvocation.apply(invocation); + return Subject.doAs(subject, performInvocation); + } + + private static List getPrincipals( + WithAccessId withAccessId) { + if (withAccessId != null) { + return Stream.concat( + Stream.of(withAccessId.user()).map(UserPrincipal::new), + Arrays.stream(withAccessId.groups()).map(GroupPrincipal::new)) + .collect(Collectors.toList()); + } + return Collections.emptyList(); + } + + private ExtensionContext getParentMethodExtensionContent(ExtensionContext extensionContext) { + Optional parent = extensionContext.getParent(); + // the class MethodExtensionContext is part of junit-jupiter-engine and has only a + // package-private visibility thus this workaround is needed. + while (!parent + .map(Object::getClass) + .map(Class::getName) + .filter(s -> s.endsWith("MethodExtensionContext")) + .isPresent()) { + parent = parent.flatMap(ExtensionContext::getParent); + } + return parent.orElseThrow( + () -> + new JUnitException( + String.format( + "Test '%s' does not have a parent method", extensionContext.getUniqueId()))); + } + + /** + * Gets the store with a method-level scope. + * + * @param context context for current extension + * @return The store + */ + private Store getStore(ExtensionContext context) { + return context.getStore(Namespace.create(getClass(), context.getRequiredTestMethod())); + } + + private static String getDisplayNameForAccessId( + WithAccessId withAccessId) { + return String.format("for user '%s'", withAccessId.user()); + } + + private static class JaasExtensionInvocationContext implements TestTemplateInvocationContext { + private final WithAccessId withAccessId; + + private JaasExtensionInvocationContext( + WithAccessId withAccessId) { + this.withAccessId = withAccessId; + } + + @Override + public String getDisplayName(int invocationIndex) { + return getDisplayNameForAccessId(withAccessId); + } + + @Override + public List getAdditionalExtensions() { + return Collections.singletonList(new WithAccessIdParameterResolver()); + } + + private class WithAccessIdParameterResolver implements ParameterResolver { + @Override + public boolean supportsParameter( + ParameterContext parameterContext, ExtensionContext extensionContext) { + return parameterContext.getParameter().getType().equals(WithAccessId.class); + } + + @Override + public Object resolveParameter( + ParameterContext parameterContext, ExtensionContext extensionContext) { + return withAccessId; + } + } + } +} diff --git a/history/taskana-simplehistory-provider/src/test/java/acceptance/security/WithAccessId.java b/history/taskana-simplehistory-provider/src/test/java/acceptance/security/WithAccessId.java new file mode 100644 index 000000000..9370896f6 --- /dev/null +++ b/history/taskana-simplehistory-provider/src/test/java/acceptance/security/WithAccessId.java @@ -0,0 +1,17 @@ +package acceptance.security; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) +@Repeatable(WithAccessIds.class) +public @interface WithAccessId { + + String user(); + + String[] groups() default {}; +} diff --git a/history/taskana-simplehistory-provider/src/test/java/acceptance/security/WithAccessIds.java b/history/taskana-simplehistory-provider/src/test/java/acceptance/security/WithAccessIds.java new file mode 100644 index 000000000..65fd66bbf --- /dev/null +++ b/history/taskana-simplehistory-provider/src/test/java/acceptance/security/WithAccessIds.java @@ -0,0 +1,12 @@ +package acceptance.security; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface WithAccessIds { + WithAccessId[] value(); +} diff --git a/history/taskana-simplehistory-provider/src/test/java/configuration/DbWriter.java b/history/taskana-simplehistory-provider/src/test/java/configuration/DbWriter.java deleted file mode 100644 index dbd8f35c7..000000000 --- a/history/taskana-simplehistory-provider/src/test/java/configuration/DbWriter.java +++ /dev/null @@ -1,70 +0,0 @@ -package configuration; - -import java.io.InputStreamReader; -import java.io.PrintWriter; -import java.io.StringReader; -import java.io.StringWriter; -import java.nio.charset.StandardCharsets; -import java.sql.Connection; -import java.sql.SQLException; -import javax.sql.DataSource; -import org.apache.ibatis.jdbc.ScriptRunner; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** Set up the database's writer and generates data for tests. */ -public class DbWriter { - - private static final Logger LOGGER = LoggerFactory.getLogger(DbWriter.class); - private static final String INSERTVALUES = "/sql/history-events.sql"; - private StringWriter outWriter = new StringWriter(); - private PrintWriter logWriter; - private StringWriter errorWriter; - private PrintWriter errorLogWriter; - - public DbWriter() { - this.logWriter = new PrintWriter(this.outWriter); - this.errorWriter = new StringWriter(); - this.errorLogWriter = new PrintWriter(this.errorWriter); - } - - public void generateTestData(DataSource dataSource) throws SQLException { - ScriptRunner runner = null; - try (Connection connection = dataSource.getConnection()) { - runner = configScriptRunner(connection); - runner.runScript( - new InputStreamReader( - DbWriter.class.getResourceAsStream(INSERTVALUES), StandardCharsets.UTF_8)); - } finally { - LOGGER.debug(outWriter.toString()); - if (!errorWriter.toString().trim().isEmpty()) { - LOGGER.error(errorWriter.toString()); - } - } - } - - public void clearDB(DataSource dataSource) throws SQLException { - ScriptRunner runner = null; - try (Connection connection = dataSource.getConnection()) { - runner = configScriptRunner(connection); - runner.runScript(new StringReader("DELETE FROM HISTORY_EVENTS;")); - } finally { - LOGGER.debug(outWriter.toString()); - if (!errorWriter.toString().trim().isEmpty()) { - LOGGER.error(errorWriter.toString()); - } - } - } - - private ScriptRunner configScriptRunner(Connection connection) throws SQLException { - LOGGER.debug(connection.getMetaData().toString()); - ScriptRunner runner = new ScriptRunner(connection); - runner.setStopOnError(true); - runner.setLogWriter(this.logWriter); - runner.setErrorLogWriter(this.errorLogWriter); - runner.setStopOnError(true); - runner.setLogWriter(this.logWriter); - runner.setErrorLogWriter(this.errorLogWriter); - return runner; - } -} diff --git a/history/taskana-simplehistory-provider/src/test/java/configuration/TaskanaEngineConfigurationTest.java b/history/taskana-simplehistory-provider/src/test/java/configuration/TaskanaEngineConfigurationTest.java index 5cb834bb2..67274eea4 100644 --- a/history/taskana-simplehistory-provider/src/test/java/configuration/TaskanaEngineConfigurationTest.java +++ b/history/taskana-simplehistory-provider/src/test/java/configuration/TaskanaEngineConfigurationTest.java @@ -20,7 +20,7 @@ class TaskanaEngineConfigurationTest extends AbstractAccTest { void testCreateTaskanaEngine() throws Exception { DataSource ds = getDataSource(); TaskanaEngineConfiguration taskEngineConfiguration = - new TaskanaEngineConfiguration(ds, false, getSchemaName()); + new TaskanaEngineConfiguration(ds, false, false, getSchemaName()); TaskanaEngine te = taskEngineConfiguration.buildTaskanaEngine(); @@ -35,7 +35,13 @@ class TaskanaEngineConfigurationTest extends AbstractAccTest { getHistoryService() .create( AbstractAccTest.createHistoryEvent( - "wbKey1", "taskId1", "type1", "Some comment", "wbKey2", "someUserId")); + "HEI:000000000000000000000000000000000000", + "wbKey1", + "taskId1", + "type1", + "Some comment", + "wbKey2", + "someUserId")); count = getHistoryService().createHistoryQuery().workbasketKeyIn("wbKey1").count(); assertThat(count).isOne(); } diff --git a/history/taskana-simplehistory-provider/src/test/java/pro/taskana/simplehistory/impl/HistoryQueryImplTest.java b/history/taskana-simplehistory-provider/src/test/java/pro/taskana/simplehistory/impl/HistoryQueryImplTest.java index c366002f1..2b1b466f1 100644 --- a/history/taskana-simplehistory-provider/src/test/java/pro/taskana/simplehistory/impl/HistoryQueryImplTest.java +++ b/history/taskana-simplehistory-provider/src/test/java/pro/taskana/simplehistory/impl/HistoryQueryImplTest.java @@ -15,6 +15,7 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import pro.taskana.common.api.TimeInterval; +import pro.taskana.common.internal.util.IdGenerator; import pro.taskana.simplehistory.impl.mappings.HistoryQueryMapper; /** @@ -25,6 +26,8 @@ import pro.taskana.simplehistory.impl.mappings.HistoryQueryMapper; @ExtendWith(MockitoExtension.class) class HistoryQueryImplTest { + private static final String ID_PREFIX_HISTORY_EVENT = "HEI"; + private HistoryQueryImpl historyQueryImpl; @Mock private TaskanaHistoryEngineImpl taskanaHistoryEngineMock; @@ -66,7 +69,9 @@ class HistoryQueryImplTest { String userId, String details, Instant created) { - HistoryEventImpl he = new HistoryEventImpl(userId, details); + HistoryEventImpl he = + new HistoryEventImpl( + IdGenerator.generateWithPrefix(ID_PREFIX_HISTORY_EVENT), userId, details); he.setTaskId(taskId); he.setWorkbasketKey(workbasketKey); he.setEventType(type); diff --git a/history/taskana-simplehistory-provider/src/test/java/pro/taskana/simplehistory/impl/SimpleHistoryServiceImplTest.java b/history/taskana-simplehistory-provider/src/test/java/pro/taskana/simplehistory/impl/SimpleHistoryServiceImplTest.java index bd4b43001..08178aa54 100644 --- a/history/taskana-simplehistory-provider/src/test/java/pro/taskana/simplehistory/impl/SimpleHistoryServiceImplTest.java +++ b/history/taskana-simplehistory-provider/src/test/java/pro/taskana/simplehistory/impl/SimpleHistoryServiceImplTest.java @@ -56,7 +56,13 @@ class SimpleHistoryServiceImplTest { void testCreateEvent() throws Exception { HistoryEventImpl expectedWb = AbstractAccTest.createHistoryEvent( - "wbKey1", "taskId1", "type1", "wbKey2", "someUserId", "someDetails"); + "HEI:000000000000000000000000000000000000", + "wbKey1", + "taskId1", + "type1", + "wbKey2", + "someUserId", + "someDetails"); cutSpy.create(expectedWb); verify(taskanaHistoryEngineMock, times(1)).openConnection(); @@ -70,7 +76,13 @@ class SimpleHistoryServiceImplTest { List returnList = new ArrayList<>(); returnList.add( AbstractAccTest.createHistoryEvent( - "wbKey1", "taskId1", "type1", "wbKey2", "someUserId", "someDetails")); + "HEI:000000000000000000000000000000000000", + "wbKey1", + "taskId1", + "type1", + "wbKey2", + "someUserId", + "someDetails")); when(historyQueryMapperMock.queryHistoryEvent(any())).thenReturn(returnList); final List result = cutSpy.createHistoryQuery().taskIdIn("taskId1").list(); diff --git a/history/taskana-simplehistory-provider/src/test/resources/sql/history-events.sql b/history/taskana-simplehistory-provider/src/test/resources/sql/history-events.sql deleted file mode 100644 index 4201a2df2..000000000 --- a/history/taskana-simplehistory-provider/src/test/resources/sql/history-events.sql +++ /dev/null @@ -1,7 +0,0 @@ -INSERT INTO HISTORY_EVENTS (BUSINESS_PROCESS_ID, PARENT_BUSINESS_PROCESS_ID, TASK_ID, EVENT_TYPE, CREATED, USER_ID, DOMAIN, WORKBASKET_KEY, POR_COMPANY, POR_SYSTEM, POR_INSTANCE, POR_TYPE, POR_VALUE, TASK_CLASSIFICATION_KEY, - TASK_CLASSIFICATION_CATEGORY, ATTACHMENT_CLASSIFICATION_KEY, OLD_VALUE, NEW_VALUE, CUSTOM_1, CUSTOM_2, CUSTOM_3, CUSTOM_4, DETAILS) VALUES --- BUSINESS_PROCESS_ID, PARENT_BUSINESS_PROCESS_ID, TASK_ID, EVENT_TYPE, CREATED, USER_ID, DOMAIN, WORKBASKET_KEY, POR_COMPANY , POR_SYSTEM, POR_INSTANCE , POR_TYPE , POR_VALUE , TASK_CLASSIFICATION_KEY, TASK_CLASSIFICATION_CATEGORY , ATTACHMENT_CLASSIFICATION_KEY , OLD_VALUE , NEW_VALUE , CUSTOM_1 , CUSTOM_2 , CUSTOM_3 , CUSTOM_4, details -('BPI:01' ,'' ,'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', CURRENT_TIMESTAMP , 'admin', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344', 'L140101' , 'TASK' ,'' ,'old_val' ,'new_val' ,'custom1' ,'custom2' , 'custom3' ,'custom4', '{"changes":[{"newValue":"BPI:01","fieldName":"businessProcessId","oldValue":"BPI:02"},{"newValue":"admin","fieldName":"owner","oldValue":"owner1"}]}' ), -('BPI:02' ,'' ,'TKI:000000000000000000000000000000000000', 'TASK_CREATED', DATEADD('DAY', -2, CURRENT_TIMESTAMP),'peter', 'DOMAIN_A', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344', '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom2' ,'' ,'someDetails' ), -('BPI:03' ,'BPI:01','TKI:000000000000000000000000000000000001','TASK_CREATED', CURRENT_TIMESTAMP , 'admin', 'DOMAIN_A', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'' ,'someDetails' ) -; diff --git a/history/taskana-simplehistory-provider/src/test/resources/taskana.properties b/history/taskana-simplehistory-provider/src/test/resources/taskana.properties new file mode 100644 index 000000000..413d7e278 --- /dev/null +++ b/history/taskana-simplehistory-provider/src/test/resources/taskana.properties @@ -0,0 +1,19 @@ +taskana.roles.user=group1 | group2|teamlead_1 | teamlead_2 |user_1_1| user_1_1| user_1_2| user_2_1| user_2_2| max|elena|simone +taskana.roles.Admin=name=konrad,Organisation=novatec|admin +taskana.roles.businessadmin=max|Moritz|businessadmin +taskana.roles.monitor=john|teamlead_2 | monitor +taskana.roles.taskadmin=peter | taskadmin +taskana.domains=Domain_A , DOMAIN_B +taskana.classification.types=TASK , document +taskana.classification.categories.task=EXTERNAL, manual, autoMAtic, Process +taskana.classification.categories.document=EXTERNAL +taskana.jobs.maxRetries=3 +taskana.jobs.batchSize=50 +taskana.jobs.cleanup.runEvery=P1D +taskana.jobs.cleanup.firstRunAt=2018-07-25T08:00:00Z +taskana.jobs.cleanup.minimumAge=P14D +taskana.german.holidays.enabled=true +taskana.german.holidays.corpus-christi.enabled=false +taskana.history.deletion.on.task.deletion.enabled=true + + diff --git a/history/taskana-simplehistory-rest-spring/src/main/resources/sql.sample-data/history-event.sql b/history/taskana-simplehistory-rest-spring/src/main/resources/sql.sample-data/history-event.sql index d57493d20..af73b57fe 100644 --- a/history/taskana-simplehistory-rest-spring/src/main/resources/sql.sample-data/history-event.sql +++ b/history/taskana-simplehistory-rest-spring/src/main/resources/sql.sample-data/history-event.sql @@ -1,47 +1,47 @@ -INSERT INTO HISTORY_EVENTS (BUSINESS_PROCESS_ID, PARENT_BUSINESS_PROCESS_ID, TASK_ID, EVENT_TYPE, CREATED, USER_ID, DOMAIN, WORKBASKET_KEY, POR_COMPANY, POR_SYSTEM, POR_INSTANCE, POR_TYPE, POR_VALUE, TASK_CLASSIFICATION_KEY, TASK_CLASSIFICATION_CATEGORY, ATTACHMENT_CLASSIFICATION_KEY, OLD_VALUE, NEW_VALUE, CUSTOM_1, CUSTOM_2, CUSTOM_3, CUSTOM_4, DETAILS) VALUES +INSERT INTO HISTORY_EVENTS (ID, BUSINESS_PROCESS_ID, PARENT_BUSINESS_PROCESS_ID, TASK_ID, EVENT_TYPE, CREATED, USER_ID, DOMAIN, WORKBASKET_KEY, POR_COMPANY, POR_SYSTEM, POR_INSTANCE, POR_TYPE, POR_VALUE, TASK_CLASSIFICATION_KEY, TASK_CLASSIFICATION_CATEGORY, ATTACHMENT_CLASSIFICATION_KEY, OLD_VALUE, NEW_VALUE, CUSTOM_1, CUSTOM_2, CUSTOM_3, CUSTOM_4, DETAILS) VALUES -- BUSINESS_PROCESS_ID, PARENT_BUSINESS_PROCESS_ID, TASK_ID, EVENT_TYPE, TASK_CREATEDD, USER_ID, DOMAIN, WORKBASKET_KEY, POR_COMPANY , POR_SYSTEM, POR_INSTANCE , POR_TYPE , POR_VALUE , TASK_CLASSIFICATION_KEY, TASK_CLASSIFICATION_CATEGORY , ATTACHMENT_CLASSIFICATION_KEY , OLD_VALUE , NEW_VALUE , CUSTOM_1 , CUSTOM_2 , CUSTOM_3 , CUSTOM_4 -('BPI:01' ,'', 'TKI:000000000000000000000000000000000000', 'TASK_CREATED', CURRENT_TIMESTAMP , 'USER-2-2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' ,' L140101' , 'TASK' ,'' ,'old_val12' ,'new_val12' ,'custom1' ,'custom2' , 'custom3' ,'custom4', 'some Details'), -('BPI:02' ,'', 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -2, CURRENT_TIMESTAMP ), 'USER-1-1', 'DOMAIN_A', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '65464564' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'','some Details'), -('BPI:03' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER-2-1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), -('BPI:04' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER-1-2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), -('BPI:03' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER-2-1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), -('BPI:02' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_CREATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER-1-2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), -('BPI:06' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER-2-1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), -('BPI:02' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER-1-2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), -('BPI:04' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_CREATED', CURRENT_TIMESTAMP , 'USER-2-1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), -('BPI:02' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER-1-2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), -('BPI:02' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER-2-1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), -('BPI:03' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER-1-2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), -('BPI:02' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER-2-1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), -('BPI:03' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER-1-2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), -('BPI:02' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER-2-1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), -('BPI:05' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER-1-2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), -('BPI:02' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_CREATED', CURRENT_TIMESTAMP , 'USER-2-1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), -('BPI:04' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER-1-2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), -('BPI:02' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER-2-1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), -('BPI:03' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER-1-2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), -('BPI:02' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER-2-1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), -('BPI:05' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER-1-2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), -('BPI:02' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER-2-1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), -('BPI:05' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_CREATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER-1-2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), -('BPI:02' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER-2-1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), -('BPI:03' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER-1-2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), -('BPI:02' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_CREATED', CURRENT_TIMESTAMP , 'USER-2-1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), -('BPI:04' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER-1-2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), -('BPI:02' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER-2-1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), -('BPI:03' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER-1-2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), -('BPI:02' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER-2-1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), -('BPI:05' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_CREATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER-1-2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), -('BPI:02' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER-2-1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), -('BPI:06' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER-1-2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), -('BPI:02' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_CREATED', CURRENT_TIMESTAMP , 'USER-2-1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), -('BPI:04' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER-1-2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), -('BPI:03' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER-2-1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), -('BPI:02' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER-1-2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), -('BPI:03' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER-2-1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), -('BPI:02' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_CREATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER-1-2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), -('BPI:06' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER-2-1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), -('BPI:02' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER-1-2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), -('BPI:03' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER-2-1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), -('BPI:04' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_CREATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER-1-2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), -('BPI:03' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER-2-1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'); \ No newline at end of file +('HEI:000000000000000000000000000000000000','BPI:01' ,'', 'TKI:000000000000000000000000000000000000', 'TASK_CREATED', CURRENT_TIMESTAMP , 'USER_2_2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' ,' L140101' , 'TASK' ,'' ,'old_val12' ,'new_val12' ,'custom1' ,'custom2' , 'custom3' ,'custom4', 'some Details'), +('HEI:000000000000000000000000000000000001','BPI:02' ,'', 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -2, CURRENT_TIMESTAMP ), 'USER_1_1', 'DOMAIN_A', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '65464564' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'','some Details'), +('HEI:000000000000000000000000000000000002','BPI:03' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER_2_1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), +('HEI:000000000000000000000000000000000003','BPI:04' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER_1_2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), +('HEI:000000000000000000000000000000000004','BPI:03' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER_2_1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), +('HEI:000000000000000000000000000000000005','BPI:02' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_CREATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER_1_2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), +('HEI:000000000000000000000000000000000006','BPI:06' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER_2_1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), +('HEI:000000000000000000000000000000000007','BPI:02' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER_1_2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), +('HEI:000000000000000000000000000000000008','BPI:04' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_CREATED', CURRENT_TIMESTAMP , 'USER_2_1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), +('HEI:000000000000000000000000000000000009','BPI:02' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER_1_2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), +('HEI:000000000000000000000000000000000010','BPI:02' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER_2_1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), +('HEI:000000000000000000000000000000000011','BPI:03' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER_1_2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), +('HEI:000000000000000000000000000000000012','BPI:02' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER_2_1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), +('HEI:000000000000000000000000000000000013','BPI:03' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER_1_2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), +('HEI:000000000000000000000000000000000014','BPI:02' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER_2_1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), +('HEI:000000000000000000000000000000000015','BPI:05' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER_1_2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), +('HEI:000000000000000000000000000000000016','BPI:02' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_CREATED', CURRENT_TIMESTAMP , 'USER_2_1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), +('HEI:000000000000000000000000000000000017','BPI:04' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER_1_2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), +('HEI:000000000000000000000000000000000018','BPI:02' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER_2_1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), +('HEI:000000000000000000000000000000000019','BPI:03' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER_1_2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), +('HEI:000000000000000000000000000000000020','BPI:02' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER_2_1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), +('HEI:000000000000000000000000000000000021','BPI:05' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER_1_2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), +('HEI:000000000000000000000000000000000022','BPI:02' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER_2_1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), +('HEI:000000000000000000000000000000000023','BPI:05' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_CREATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER_1_2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), +('HEI:000000000000000000000000000000000024','BPI:02' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER_2_1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), +('HEI:000000000000000000000000000000000025','BPI:03' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER_1_2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), +('HEI:000000000000000000000000000000000026','BPI:02' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_CREATED', CURRENT_TIMESTAMP , 'USER_2_1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), +('HEI:000000000000000000000000000000000027','BPI:04' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER_1_2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), +('HEI:000000000000000000000000000000000028','BPI:02' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER_2_1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), +('HEI:000000000000000000000000000000000029','BPI:03' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER_1_2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), +('HEI:000000000000000000000000000000000030','BPI:02' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER_2_1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), +('HEI:000000000000000000000000000000000031','BPI:05' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_CREATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER_1_2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), +('HEI:000000000000000000000000000000000032','BPI:02' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER_2_1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), +('HEI:000000000000000000000000000000000033','BPI:06' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER_1_2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), +('HEI:000000000000000000000000000000000034','BPI:02' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_CREATED', CURRENT_TIMESTAMP , 'USER_2_1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), +('HEI:000000000000000000000000000000000035','BPI:04' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER_1_2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), +('HEI:000000000000000000000000000000000036','BPI:03' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER_2_1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), +('HEI:000000000000000000000000000000000037','BPI:02' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER_1_2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), +('HEI:000000000000000000000000000000000038','BPI:03' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER_2_1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), +('HEI:000000000000000000000000000000000039','BPI:02' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_CREATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER_1_2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), +('HEI:000000000000000000000000000000000040','BPI:06' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER_2_1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), +('HEI:000000000000000000000000000000000041','BPI:02' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER_1_2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), +('HEI:000000000000000000000000000000000042','BPI:03' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER_2_1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'), +('HEI:000000000000000000000000000000000043','BPI:04' ,'' , 'TKI:000000000000000000000000000000000000', 'TASK_CREATED', DATEADD('DAY', -1, CURRENT_TIMESTAMP ), 'USER_1_2', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344' , '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom3' ,'custom4','some Details'), +('HEI:000000000000000000000000000000000044','BPI:03' ,'BPI:01', 'TKI:000000000000000000000000000000000001', 'TASK_DELETED', CURRENT_TIMESTAMP , 'USER_2_1', 'DOMAIN_B', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'','some Details'); \ No newline at end of file diff --git a/history/taskana-simplehistory-rest-spring/src/test/java/pro/taskana/TaskHistoryEventControllerIntTest.java b/history/taskana-simplehistory-rest-spring/src/test/java/pro/taskana/TaskHistoryEventControllerIntTest.java index 748ae6ec2..35cea5377 100644 --- a/history/taskana-simplehistory-rest-spring/src/test/java/pro/taskana/TaskHistoryEventControllerIntTest.java +++ b/history/taskana-simplehistory-rest-spring/src/test/java/pro/taskana/TaskHistoryEventControllerIntTest.java @@ -104,7 +104,7 @@ class TaskHistoryEventControllerIntTest { void should_ReturnSpecificTaskHistoryEventWithDetails_When_SingleEventIsQueried() { ResponseEntity response = template.exchange( - server + port + "/api/v1/task-history-event/45", + server + port + "/api/v1/task-history-event/HEI:000000000000000000000000000000000000", HttpMethod.GET, request, ParameterizedTypeReference.forType(TaskHistoryEventResource.class)); diff --git a/history/taskana-simplehistory-rest-spring/src/test/java/pro/taskana/TaskHistoryEventResourceAssemblerTest.java b/history/taskana-simplehistory-rest-spring/src/test/java/pro/taskana/TaskHistoryEventResourceAssemblerTest.java index be864a00f..8910004da 100644 --- a/history/taskana-simplehistory-rest-spring/src/test/java/pro/taskana/TaskHistoryEventResourceAssemblerTest.java +++ b/history/taskana-simplehistory-rest-spring/src/test/java/pro/taskana/TaskHistoryEventResourceAssemblerTest.java @@ -33,7 +33,8 @@ class TaskHistoryEventResourceAssemblerTest { @Test void taskHistoryEventModelToResource() { - HistoryEventImpl historyEvent = new HistoryEventImpl("user1", "someDetails"); + HistoryEventImpl historyEvent = + new HistoryEventImpl("HEI:000000000000000000000000000000000000", "user1", "someDetails"); historyEvent.setEventType("TASK_CREATED"); historyEvent.setBusinessProcessId("BPI:01"); diff --git a/history/taskana-simplehistory-rest-spring/src/test/java/pro/taskana/doc/api/TaskHistoryEventControllerRestDocumentation.java b/history/taskana-simplehistory-rest-spring/src/test/java/pro/taskana/doc/api/TaskHistoryEventControllerRestDocumentation.java index 92a7dba13..153d41db2 100644 --- a/history/taskana-simplehistory-rest-spring/src/test/java/pro/taskana/doc/api/TaskHistoryEventControllerRestDocumentation.java +++ b/history/taskana-simplehistory-rest-spring/src/test/java/pro/taskana/doc/api/TaskHistoryEventControllerRestDocumentation.java @@ -173,7 +173,7 @@ public class TaskHistoryEventControllerRestDocumentation { this.mockMvc .perform( RestDocumentationRequestBuilders.get( - "http://127.0.0.1:" + port + "/api/v1/task-history-event/1") + "http://127.0.0.1:" + port + "/api/v1/task-history-event/HEI:000000000000000000000000000000000000") .accept("application/hal+json") .header("Authorization", "Basic dGVhbWxlYWRfMTp0ZWFtbGVhZF8x")) .andExpect(MockMvcResultMatchers.status().isOk()) diff --git a/lib/taskana-core/src/main/java/pro/taskana/TaskanaEngineConfiguration.java b/lib/taskana-core/src/main/java/pro/taskana/TaskanaEngineConfiguration.java index ea3f458c3..90e9d1e85 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/TaskanaEngineConfiguration.java +++ b/lib/taskana-core/src/main/java/pro/taskana/TaskanaEngineConfiguration.java @@ -68,6 +68,8 @@ public class TaskanaEngineConfiguration { "taskana.german.holidays.corpus-christi.enabled"; private static final String TASKANA_CUSTOM_HOLIDAY = "taskana.custom.holidays"; private static final String TASKANA_CUSTOM_HOLIDAY_DAY_MONTH_SEPARATOR = "."; + private static final String TASKANA_HISTORY_DELETION_ON_TASK_DELETION_ENABLED = + "taskana.history.deletion.on.task.deletion.enabled"; // TASKANA_SCHEMA_VERSION private static final String DEFAULT_SCHEMA_NAME = "TASKANA"; @@ -92,6 +94,7 @@ public class TaskanaEngineConfiguration { protected List classificationTypes = new ArrayList<>(); protected Map> classificationCategoriesByTypeMap = new HashMap<>(); // Properties for the monitor + private boolean deleteHistoryOnTaskDeletionEnabled; private boolean germanPublicHolidaysEnabled; private boolean corpusChristiEnabled; // Properties for general job execution @@ -171,8 +174,9 @@ public class TaskanaEngineConfiguration { initDomains(props); initClassificationTypes(props); initClassificationCategories(props); - initGermanHolidaysEnabled(props); - initCorpusChristiEnabled(props); + initBooleanFlag(props, TASKANA_GERMAN_HOLIDAYS_ENABLED); + initBooleanFlag(props, TASKANA_GERMAN_HOLIDAYS_CORPUS_CHRISTI_ENABLED); + initBooleanFlag(props, TASKANA_HISTORY_DELETION_ON_TASK_DELETION_ENABLED); initCustomHolidays(props); } @@ -268,6 +272,14 @@ public class TaskanaEngineConfiguration { this.germanPublicHolidaysEnabled = germanPublicHolidaysEnabled; } + public boolean isDeleteHistoryOnTaskDeletionEnabled() { + return deleteHistoryOnTaskDeletionEnabled; + } + + public void setDeleteHistoryOnTaskDeletionEnabled(boolean deleteHistoryOnTaskDeletionEnabled) { + this.deleteHistoryOnTaskDeletionEnabled = deleteHistoryOnTaskDeletionEnabled; + } + public List getCustomHolidays() { return customHolidays; } @@ -362,24 +374,21 @@ public class TaskanaEngineConfiguration { return true; } - private void initGermanHolidaysEnabled(Properties props) { - String enabled = props.getProperty(TASKANA_GERMAN_HOLIDAYS_ENABLED); + private void initBooleanFlag(Properties props, String propName) { + String enabled = props.getProperty(propName); if (enabled != null && !enabled.isEmpty()) { - germanPublicHolidaysEnabled = Boolean.parseBoolean(enabled); - } else { - germanPublicHolidaysEnabled = false; + boolean isEnabled = Boolean.parseBoolean(enabled); + if (propName.equals(TASKANA_GERMAN_HOLIDAYS_ENABLED)) { + germanPublicHolidaysEnabled = isEnabled; + } else if (propName.equals(TASKANA_GERMAN_HOLIDAYS_CORPUS_CHRISTI_ENABLED)) { + corpusChristiEnabled = isEnabled; + } else if (propName.equals(TASKANA_HISTORY_DELETION_ON_TASK_DELETION_ENABLED)) { + deleteHistoryOnTaskDeletionEnabled = isEnabled; + } } - LOGGER.debug("GermanPublicHolidaysEnabled = {}", germanPublicHolidaysEnabled); - } - - private void initCorpusChristiEnabled(Properties props) { - String enabled = props.getProperty(TASKANA_GERMAN_HOLIDAYS_CORPUS_CHRISTI_ENABLED); - if (enabled != null && !enabled.isEmpty()) { - corpusChristiEnabled = Boolean.parseBoolean(enabled); - } else { - corpusChristiEnabled = false; + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(String.format("%s = %b", propName, Boolean.parseBoolean(enabled))); } - LOGGER.debug("CorpusChristiEnabled = {}", corpusChristiEnabled); } private void initJobParameters(Properties props) { diff --git a/lib/taskana-core/src/main/java/pro/taskana/common/internal/InternalTaskanaEngine.java b/lib/taskana-core/src/main/java/pro/taskana/common/internal/InternalTaskanaEngine.java index 4c0c93cbc..d5f03f9c4 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/common/internal/InternalTaskanaEngine.java +++ b/lib/taskana-core/src/main/java/pro/taskana/common/internal/InternalTaskanaEngine.java @@ -4,7 +4,7 @@ import java.util.function.Supplier; import org.apache.ibatis.session.SqlSession; import pro.taskana.common.api.TaskanaEngine; -import pro.taskana.spi.history.internal.HistoryEventProducer; +import pro.taskana.spi.history.internal.HistoryEventManager; import pro.taskana.task.internal.TaskRoutingManager; /** @@ -67,7 +67,7 @@ public interface InternalTaskanaEngine { * * @return the HistoryEventProducer instance. */ - HistoryEventProducer getHistoryEventProducer(); + HistoryEventManager getHistoryEventManager(); /** * Retrieve TaskRoutingProducer. diff --git a/lib/taskana-core/src/main/java/pro/taskana/common/internal/TaskanaEngineImpl.java b/lib/taskana-core/src/main/java/pro/taskana/common/internal/TaskanaEngineImpl.java index 892c71b65..06d493dca 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/common/internal/TaskanaEngineImpl.java +++ b/lib/taskana-core/src/main/java/pro/taskana/common/internal/TaskanaEngineImpl.java @@ -49,7 +49,7 @@ import pro.taskana.common.internal.security.GroupPrincipal; import pro.taskana.monitor.api.MonitorService; import pro.taskana.monitor.internal.MonitorMapper; import pro.taskana.monitor.internal.MonitorServiceImpl; -import pro.taskana.spi.history.internal.HistoryEventProducer; +import pro.taskana.spi.history.internal.HistoryEventManager; import pro.taskana.task.api.TaskService; import pro.taskana.task.internal.AttachmentMapper; import pro.taskana.task.internal.ObjectReferenceMapper; @@ -80,12 +80,13 @@ public class TaskanaEngineImpl implements TaskanaEngine { protected SqlSessionManager sessionManager; protected ConnectionManagementMode mode = ConnectionManagementMode.PARTICIPATE; protected Connection connection = null; + private HistoryEventManager historyEventManager; protected TaskanaEngineImpl(TaskanaEngineConfiguration taskanaEngineConfiguration) { this.taskanaEngineConfiguration = taskanaEngineConfiguration; createTransactionFactory(taskanaEngineConfiguration.getUseManagedTransactions()); this.sessionManager = createSqlSessionManager(); - historyEventProducer = HistoryEventProducer.getInstance(taskanaEngineConfiguration); + historyEventManager = HistoryEventManager.getInstance(taskanaEngineConfiguration); taskRoutingManager = TaskRoutingManager.getInstance(this); this.internalTaskanaEngineImpl = new InternalTaskanaEngineImpl(); workingDaysToDaysConverter = @@ -154,7 +155,7 @@ public class TaskanaEngineImpl implements TaskanaEngine { @Override public boolean isHistoryEnabled() { - return HistoryEventProducer.isHistoryEnabled(); + return HistoryEventManager.isHistoryEnabled(); } @Override @@ -410,8 +411,8 @@ public class TaskanaEngineImpl implements TaskanaEngine { } @Override - public HistoryEventProducer getHistoryEventProducer() { - return historyEventProducer; + public HistoryEventManager getHistoryEventManager() { + return historyEventManager; } @Override diff --git a/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/TaskanaHistory.java b/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/TaskanaHistory.java index 0443e148f..1f2c6fd58 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/TaskanaHistory.java +++ b/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/TaskanaHistory.java @@ -1,6 +1,10 @@ package pro.taskana.spi.history.api; +import java.util.List; + import pro.taskana.TaskanaEngineConfiguration; +import pro.taskana.common.api.exceptions.InvalidArgumentException; +import pro.taskana.common.api.exceptions.NotAuthorizedException; import pro.taskana.spi.history.api.events.TaskanaHistoryEvent; /** Interface for TASKANA History Service Provider. */ @@ -20,4 +24,14 @@ public interface TaskanaHistory { * @param event {@link TaskanaHistoryEvent} The event to be created. */ void create(TaskanaHistoryEvent event); + + /** + * Delete history events by taskIds. + * + * @param taskIds the task ids for which all history events must be deleted + * @throws InvalidArgumentException If the list of taskIds is null + * @throws NotAuthorizedException If the user has no permission to delete events + */ + void deleteHistoryEventsByTaskIds(List taskIds) + throws InvalidArgumentException, NotAuthorizedException; } diff --git a/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/TaskanaHistoryEvent.java b/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/TaskanaHistoryEvent.java index a6baf4cba..d544d397e 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/TaskanaHistoryEvent.java +++ b/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/TaskanaHistoryEvent.java @@ -5,7 +5,7 @@ import java.time.Instant; /** Super class for all specific events from the TASKANA engine. */ public class TaskanaHistoryEvent { - protected long id; + protected String id; protected String businessProcessId; protected String parentBusinessProcessId; protected String taskId; @@ -32,15 +32,20 @@ public class TaskanaHistoryEvent { public TaskanaHistoryEvent() {} - public TaskanaHistoryEvent(String userId, String details) { + public TaskanaHistoryEvent(String id, String userId, String details) { + this.id = id; this.userId = userId; this.details = details; } - public long getId() { + public String getId() { return id; } + public void setId(String id) { + this.id = id; + } + public String getBusinessProcessId() { return businessProcessId; } diff --git a/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/task/ClaimCancelledEvent.java b/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/task/ClaimCancelledEvent.java index 5e58339c9..13635fdba 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/task/ClaimCancelledEvent.java +++ b/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/task/ClaimCancelledEvent.java @@ -5,8 +5,8 @@ import pro.taskana.task.api.models.Task; /** Event fired if a task is cancelled to be claimed. */ public class ClaimCancelledEvent extends TaskEvent { - public ClaimCancelledEvent(Task task, String userId) { - super(task, userId, null); + public ClaimCancelledEvent(String id, Task task, String userId) { + super(id, task, userId, null); eventType = "TASK_CLAIM_CANCELLED"; created = task.getModified(); } diff --git a/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/task/ClaimedEvent.java b/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/task/ClaimedEvent.java index 130490a59..8ceaf3267 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/task/ClaimedEvent.java +++ b/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/task/ClaimedEvent.java @@ -5,8 +5,8 @@ import pro.taskana.task.api.models.Task; /** Event fired if a task is claimed. */ public class ClaimedEvent extends TaskEvent { - public ClaimedEvent(Task task, String userId) { - super(task, userId, null); + public ClaimedEvent(String id, Task task, String userId) { + super(id, task, userId, null); setEventType("TASK_CLAIMED"); created = task.getClaimed(); } diff --git a/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/task/CompletedEvent.java b/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/task/CompletedEvent.java index 0f521b387..bd065014b 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/task/CompletedEvent.java +++ b/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/task/CompletedEvent.java @@ -6,14 +6,14 @@ import pro.taskana.task.api.models.TaskSummary; /** Event fired if a task is completed. */ public class CompletedEvent extends TaskEvent { - public CompletedEvent(Task completedTask, String userId) { - super(completedTask, userId, null); + public CompletedEvent(String id, Task completedTask, String userId) { + super(id, completedTask, userId, null); eventType = "TASK_COMPLETED"; created = completedTask.getCompleted(); } - public CompletedEvent(TaskSummary completedTask, String userId) { - super(completedTask, userId, null); + public CompletedEvent(String id, TaskSummary completedTask, String userId) { + super(id, completedTask, userId, null); eventType = "TASK_COMPLETED"; created = completedTask.getCompleted(); } diff --git a/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/task/CreatedEvent.java b/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/task/CreatedEvent.java index 42208db5b..a98e22f49 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/task/CreatedEvent.java +++ b/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/task/CreatedEvent.java @@ -5,8 +5,8 @@ import pro.taskana.task.api.models.Task; /** Event fired if a task is created. */ public class CreatedEvent extends TaskEvent { - public CreatedEvent(Task task, String userId, String details) { - super(task, userId, details); + public CreatedEvent(String id, Task task, String userId, String details) { + super(id, task, userId, details); eventType = "TASK_CREATED"; created = task.getCreated(); } diff --git a/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/task/TaskEvent.java b/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/task/TaskEvent.java index 405df55f9..b89555fa0 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/task/TaskEvent.java +++ b/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/task/TaskEvent.java @@ -7,8 +7,8 @@ import pro.taskana.task.api.models.TaskSummary; /** Super class for all task related events. */ public class TaskEvent extends TaskanaHistoryEvent { - public TaskEvent(Task task, String userId, String details) { - super(userId, details); + public TaskEvent(String id, Task task, String userId, String details) { + super(id, userId, details); taskId = task.getId(); businessProcessId = task.getBusinessProcessId(); parentBusinessProcessId = task.getParentBusinessProcessId(); @@ -31,8 +31,8 @@ public class TaskEvent extends TaskanaHistoryEvent { } } - public TaskEvent(TaskSummary task, String userId, String details) { - super(userId, details); + public TaskEvent(String id, TaskSummary task, String userId, String details) { + super(id, userId, details); taskId = task.getId(); businessProcessId = task.getBusinessProcessId(); parentBusinessProcessId = task.getParentBusinessProcessId(); diff --git a/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/task/TransferredEvent.java b/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/task/TransferredEvent.java index 8009705f7..bacd0fe38 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/task/TransferredEvent.java +++ b/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/task/TransferredEvent.java @@ -12,8 +12,12 @@ public class TransferredEvent extends TaskEvent { private static final Logger LOGGER = LoggerFactory.getLogger(TransferredEvent.class); public TransferredEvent( - Task task, WorkbasketSummary oldWorkbasket, WorkbasketSummary newWorkbasket, String userId) { - super(task, userId, null); + String id, + Task task, + WorkbasketSummary oldWorkbasket, + WorkbasketSummary newWorkbasket, + String userId) { + super(id, task, userId, null); eventType = "TASK_TRANSFERRED"; created = task.getModified(); this.oldValue = oldWorkbasket.getId(); diff --git a/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/task/UpdatedEvent.java b/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/task/UpdatedEvent.java index fc52ae995..5553bebe2 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/task/UpdatedEvent.java +++ b/lib/taskana-core/src/main/java/pro/taskana/spi/history/api/events/task/UpdatedEvent.java @@ -4,8 +4,8 @@ import pro.taskana.task.api.models.Task; public class UpdatedEvent extends TaskEvent { - public UpdatedEvent(Task updatedTask, String userId, String details) { - super(updatedTask, userId, details); + public UpdatedEvent(String id, Task updatedTask, String userId, String details) { + super(id, updatedTask, userId, details); eventType = "TASK_UPDATED"; created = updatedTask.getModified(); } diff --git a/lib/taskana-core/src/main/java/pro/taskana/spi/history/internal/HistoryEventProducer.java b/lib/taskana-core/src/main/java/pro/taskana/spi/history/internal/HistoryEventManager.java similarity index 56% rename from lib/taskana-core/src/main/java/pro/taskana/spi/history/internal/HistoryEventProducer.java rename to lib/taskana-core/src/main/java/pro/taskana/spi/history/internal/HistoryEventManager.java index 45eb74924..b4912b8f6 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/spi/history/internal/HistoryEventProducer.java +++ b/lib/taskana-core/src/main/java/pro/taskana/spi/history/internal/HistoryEventManager.java @@ -1,23 +1,28 @@ package pro.taskana.spi.history.internal; +import java.util.List; import java.util.Objects; import java.util.ServiceLoader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import pro.taskana.TaskanaEngineConfiguration; +import pro.taskana.common.api.exceptions.InvalidArgumentException; +import pro.taskana.common.api.exceptions.NotAuthorizedException; import pro.taskana.spi.history.api.TaskanaHistory; import pro.taskana.spi.history.api.events.TaskanaHistoryEvent; -/** Creates events and emits them to the registered history service providers. */ -public final class HistoryEventProducer { +/** + * Creates and deletes events and emits them to the registered history service providers. + */ +public final class HistoryEventManager { - private static final Logger LOGGER = LoggerFactory.getLogger(HistoryEventProducer.class); - private static HistoryEventProducer singleton; + private static final Logger LOGGER = LoggerFactory.getLogger(HistoryEventManager.class); + private static HistoryEventManager singleton; private boolean enabled = false; private ServiceLoader serviceLoader; - private HistoryEventProducer(TaskanaEngineConfiguration taskanaEngineConfiguration) { + private HistoryEventManager(TaskanaEngineConfiguration taskanaEngineConfiguration) { serviceLoader = ServiceLoader.load(TaskanaHistory.class); for (TaskanaHistory history : serviceLoader) { history.initialize(taskanaEngineConfiguration); @@ -29,10 +34,10 @@ public final class HistoryEventProducer { } } - public static synchronized HistoryEventProducer getInstance( + public static synchronized HistoryEventManager getInstance( TaskanaEngineConfiguration taskanaEngineConfiguration) { if (singleton == null) { - singleton = new HistoryEventProducer(taskanaEngineConfiguration); + singleton = new HistoryEventManager(taskanaEngineConfiguration); } return singleton; } @@ -45,4 +50,15 @@ public final class HistoryEventProducer { LOGGER.debug("Sending event to history service providers: {}", event); serviceLoader.forEach(historyProvider -> historyProvider.create(event)); } + + public void deleteEvents(List taskIds) { + LOGGER.debug("Sending taskIds to history service providers: {}", taskIds); + serviceLoader.forEach(historyProvider -> { + try { + historyProvider.deleteHistoryEventsByTaskIds(taskIds); + } catch (InvalidArgumentException | NotAuthorizedException e) { + LOGGER.warn("Caught an exception while trying to delete HistoryEvents", e); + } + }); + } } diff --git a/lib/taskana-core/src/main/java/pro/taskana/task/internal/AttachmentHandler.java b/lib/taskana-core/src/main/java/pro/taskana/task/internal/AttachmentHandler.java index 5b4e29fab..c7f04ee62 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/task/internal/AttachmentHandler.java +++ b/lib/taskana-core/src/main/java/pro/taskana/task/internal/AttachmentHandler.java @@ -177,7 +177,7 @@ public class AttachmentHandler { oldAttachments.forEach( a -> { if (!newAttIds.contains(a.getId())) { - attachmentMapper.deleteAttachment(a.getId()); + attachmentMapper.delete(a.getId()); LOGGER.debug( "TaskService.updateTask() for TaskId={} DELETED an Attachment={}.", newTaskImpl.getId(), diff --git a/lib/taskana-core/src/main/java/pro/taskana/task/internal/AttachmentMapper.java b/lib/taskana-core/src/main/java/pro/taskana/task/internal/AttachmentMapper.java index f9887dcf7..8978f3d8d 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/task/internal/AttachmentMapper.java +++ b/lib/taskana-core/src/main/java/pro/taskana/task/internal/AttachmentMapper.java @@ -91,7 +91,11 @@ public interface AttachmentMapper { @Param("taskIds") List taskIds); @Delete("DELETE FROM ATTACHMENT WHERE ID=#{attachmentId}") - void deleteAttachment(@Param("attachmentId") String attachmentId); + void delete(@Param("attachmentId") String attachmentId); + + @Delete( + "") + void deleteMultipleByTaskIds(@Param("taskIds") List taskIds); @Update( "UPDATE ATTACHMENT SET TASK_ID = #{taskId}, CREATED = #{created}, MODIFIED = #{modified}," diff --git a/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskServiceImpl.java b/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskServiceImpl.java index 442074ec6..f6299c27e 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskServiceImpl.java +++ b/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskServiceImpl.java @@ -46,7 +46,7 @@ import pro.taskana.spi.history.api.events.task.ClaimedEvent; import pro.taskana.spi.history.api.events.task.CompletedEvent; import pro.taskana.spi.history.api.events.task.CreatedEvent; import pro.taskana.spi.history.api.events.task.UpdatedEvent; -import pro.taskana.spi.history.internal.HistoryEventProducer; +import pro.taskana.spi.history.internal.HistoryEventManager; import pro.taskana.task.api.CallbackState; import pro.taskana.task.api.TaskQuery; import pro.taskana.task.api.TaskService; @@ -101,18 +101,19 @@ public class TaskServiceImpl implements TaskService { private static final String ID_PREFIX_BUSINESS_PROCESS = "BPI"; private static final Set ALLOWED_CUSTOM_KEYS = IntStream.rangeClosed(1, 16).mapToObj(String::valueOf).collect(Collectors.toSet()); + private static final String ID_PREFIX_HISTORY_EVENT = "HEI"; private static final String TASK_WITH_ID_IS_ALREADY_IN_END_STATE = "Task with Id %s is already in an end state."; private final InternalTaskanaEngine taskanaEngine; private final WorkbasketService workbasketService; private final ClassificationService classificationService; private final TaskMapper taskMapper; - private final AttachmentMapper attachmentMapper; - private final HistoryEventProducer historyEventProducer; private final TaskTransferrer taskTransferrer; private final TaskCommentServiceImpl taskCommentService; private final ServiceLevelHandler serviceLevelHandler; private final AttachmentHandler attachmentHandler; + private AttachmentMapper attachmentMapper; + private HistoryEventManager historyEventManager; public TaskServiceImpl( InternalTaskanaEngine taskanaEngine, @@ -124,7 +125,7 @@ public class TaskServiceImpl implements TaskService { this.workbasketService = taskanaEngine.getEngine().getWorkbasketService(); this.attachmentMapper = attachmentMapper; this.classificationService = taskanaEngine.getEngine().getClassificationService(); - this.historyEventProducer = taskanaEngine.getHistoryEventProducer(); + this.historyEventManager = taskanaEngine.getHistoryEventManager(); this.taskTransferrer = new TaskTransferrer(taskanaEngine, taskMapper, this); this.taskCommentService = new TaskCommentServiceImpl(taskanaEngine, taskCommentMapper, this); this.serviceLevelHandler = new ServiceLevelHandler(taskanaEngine, taskMapper, attachmentMapper); @@ -231,11 +232,15 @@ public class TaskServiceImpl implements TaskService { try { this.taskMapper.insert(task); LOGGER.debug("Method createTask() created Task '{}'.", task.getId()); - if (HistoryEventProducer.isHistoryEnabled()) { + if (HistoryEventManager.isHistoryEnabled()) { String details = determineChangesInTaskAttributes(newTask(), task); - historyEventProducer.createEvent( - new CreatedEvent(task, CurrentUserContext.getUserid(), details)); + historyEventManager.createEvent( + new CreatedEvent( + IdGenerator.generateWithPrefix(ID_PREFIX_HISTORY_EVENT), + task, + CurrentUserContext.getUserid(), + details)); } } catch (PersistenceException e) { // Error messages: @@ -434,13 +439,17 @@ public class TaskServiceImpl implements TaskService { LOGGER.debug("Method updateTask() updated task '{}' for user '{}'.", task.getId(), userId); - if (HistoryEventProducer.isHistoryEnabled()) { + if (HistoryEventManager.isHistoryEnabled()) { String changeDetails = determineChangesInTaskAttributes(oldTaskImpl, newTaskImpl); LOGGER.warn(changeDetails); - historyEventProducer.createEvent( - new UpdatedEvent(task, CurrentUserContext.getUserid(), changeDetails)); + historyEventManager.createEvent( + new UpdatedEvent( + IdGenerator.generateWithPrefix(ID_PREFIX_HISTORY_EVENT), + task, + CurrentUserContext.getUserid(), + changeDetails)); } } finally { @@ -537,8 +546,18 @@ public class TaskServiceImpl implements TaskService { while (taskIdIterator.hasNext()) { removeSingleTaskForTaskDeletionById(bulkLog, taskSummaries, taskIdIterator); } + if (!taskIds.isEmpty()) { + attachmentMapper.deleteMultipleByTaskIds(taskIds); taskMapper.deleteMultiple(taskIds); + + if (taskanaEngine.getEngine().isHistoryEnabled() + && taskanaEngine + .getEngine() + .getConfiguration() + .isDeleteHistoryOnTaskDeletionEnabled()) { + historyEventManager.deleteEvents(taskIds); + } } return bulkLog; } finally { @@ -1198,8 +1217,12 @@ public class TaskServiceImpl implements TaskService { claimActionsOnTask(task, userId, now); taskMapper.update(task); LOGGER.debug("Task '{}' claimed by user '{}'.", taskId, userId); - if (HistoryEventProducer.isHistoryEnabled()) { - historyEventProducer.createEvent(new ClaimedEvent(task, CurrentUserContext.getUserid())); + if (HistoryEventManager.isHistoryEnabled()) { + historyEventManager.createEvent( + new ClaimedEvent( + IdGenerator.generateWithPrefix(ID_PREFIX_HISTORY_EVENT), + task, + CurrentUserContext.getUserid())); } } finally { taskanaEngine.returnConnection(); @@ -1294,9 +1317,12 @@ public class TaskServiceImpl implements TaskService { task.setState(TaskState.READY); taskMapper.update(task); LOGGER.debug("Task '{}' unclaimed by user '{}'.", taskId, userId); - if (HistoryEventProducer.isHistoryEnabled()) { - historyEventProducer.createEvent( - new ClaimCancelledEvent(task, CurrentUserContext.getUserid())); + if (HistoryEventManager.isHistoryEnabled()) { + historyEventManager.createEvent( + new ClaimCancelledEvent( + IdGenerator.generateWithPrefix(ID_PREFIX_HISTORY_EVENT), + task, + CurrentUserContext.getUserid())); } } finally { taskanaEngine.returnConnection(); @@ -1332,8 +1358,12 @@ public class TaskServiceImpl implements TaskService { completeActionsOnTask(task, userId, now); taskMapper.update(task); LOGGER.debug("Task '{}' completed by user '{}'.", taskId, userId); - if (HistoryEventProducer.isHistoryEnabled()) { - historyEventProducer.createEvent(new CompletedEvent(task, CurrentUserContext.getUserid())); + if (HistoryEventManager.isHistoryEnabled()) { + historyEventManager.createEvent( + new CompletedEvent( + IdGenerator.generateWithPrefix(ID_PREFIX_HISTORY_EVENT), + task, + CurrentUserContext.getUserid())); } } finally { taskanaEngine.returnConnection(); @@ -1360,7 +1390,14 @@ public class TaskServiceImpl implements TaskService { throw new InvalidStateException(String.format(TASK_WITH_ID_CALLBACK_NOT_PROCESSED, taskId)); } + attachmentMapper.deleteMultipleByTaskIds(Arrays.asList(taskId)); taskMapper.delete(taskId); + + if (taskanaEngine.getEngine().isHistoryEnabled() + && taskanaEngine.getEngine().getConfiguration().isDeleteHistoryOnTaskDeletionEnabled()) { + historyEventManager.deleteEvents(Arrays.asList(taskId)); + } + LOGGER.debug("Task {} deleted.", taskId); } finally { taskanaEngine.returnConnection(); @@ -1560,7 +1597,7 @@ public class TaskServiceImpl implements TaskService { if (!updateClaimedTaskIds.isEmpty()) { taskMapper.updateClaimed(updateClaimedTaskIds, claimedReference); } - if (HistoryEventProducer.isHistoryEnabled()) { + if (HistoryEventManager.isHistoryEnabled()) { createTasksCompletedEvents(taskSummaryList); } } @@ -1906,7 +1943,10 @@ public class TaskServiceImpl implements TaskService { private void createTasksCompletedEvents(List taskSummaries) { taskSummaries.forEach( task -> - historyEventProducer.createEvent( - new CompletedEvent(task, CurrentUserContext.getUserid()))); + historyEventManager.createEvent( + new CompletedEvent( + IdGenerator.generateWithPrefix(ID_PREFIX_HISTORY_EVENT), + task, + CurrentUserContext.getUserid()))); } } diff --git a/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskTransferrer.java b/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskTransferrer.java index e10a53043..716595379 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskTransferrer.java +++ b/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskTransferrer.java @@ -16,8 +16,9 @@ import pro.taskana.common.api.exceptions.NotAuthorizedException; import pro.taskana.common.api.exceptions.TaskanaException; import pro.taskana.common.internal.InternalTaskanaEngine; import pro.taskana.common.internal.security.CurrentUserContext; +import pro.taskana.common.internal.util.IdGenerator; import pro.taskana.spi.history.api.events.task.TransferredEvent; -import pro.taskana.spi.history.internal.HistoryEventProducer; +import pro.taskana.spi.history.internal.HistoryEventManager; import pro.taskana.task.api.TaskState; import pro.taskana.task.api.exceptions.InvalidStateException; import pro.taskana.task.api.exceptions.TaskNotFoundException; @@ -41,12 +42,13 @@ public class TaskTransferrer { private static final String TASK_WITH_ID = "Task with id "; private static final String WAS_MARKED_FOR_DELETION = " was marked for deletion"; private static final String THE_WORKBASKET = "The workbasket "; + private static final String ID_PREFIX_HISTORY_EVENT = "HEI"; private static final Logger LOGGER = LoggerFactory.getLogger(TaskTransferrer.class); private InternalTaskanaEngine taskanaEngine; private WorkbasketService workbasketService; private TaskServiceImpl taskService; private TaskMapper taskMapper; - private HistoryEventProducer historyEventProducer; + private HistoryEventManager historyEventManager; TaskTransferrer( InternalTaskanaEngine taskanaEngine, TaskMapper taskMapper, TaskServiceImpl taskService) { @@ -55,7 +57,7 @@ public class TaskTransferrer { this.taskService = taskService; this.taskMapper = taskMapper; this.workbasketService = taskanaEngine.getEngine().getWorkbasketService(); - this.historyEventProducer = taskanaEngine.getHistoryEventProducer(); + this.historyEventManager = taskanaEngine.getHistoryEventManager(); } Task transfer(String taskId, String destinationWorkbasketKey, String domain) @@ -110,7 +112,7 @@ public class TaskTransferrer { "Method transfer() transferred Task '{}' to destination workbasket {}", taskId, destinationWorkbasket.getId()); - if (HistoryEventProducer.isHistoryEnabled()) { + if (HistoryEventManager.isHistoryEnabled()) { createTaskTransferredEvent(task, oldWorkbasketSummary, destinationWorkbasket.asSummary()); } return task; @@ -167,7 +169,7 @@ public class TaskTransferrer { "Method transfer() transferred Task '{}' to destination workbasket {}", taskId, destinationWorkbasketId); - if (HistoryEventProducer.isHistoryEnabled()) { + if (HistoryEventManager.isHistoryEnabled()) { createTaskTransferredEvent(task, oldWorkbasketSummary, destinationWorkbasket.asSummary()); } return task; @@ -364,9 +366,13 @@ public class TaskTransferrer { private void createTaskTransferredEvent( Task task, WorkbasketSummary oldWorkbasketSummary, WorkbasketSummary newWorkbasketSummary) { - historyEventProducer.createEvent( + historyEventManager.createEvent( new TransferredEvent( - task, oldWorkbasketSummary, newWorkbasketSummary, CurrentUserContext.getUserid())); + IdGenerator.generateWithPrefix(ID_PREFIX_HISTORY_EVENT), + task, + oldWorkbasketSummary, + newWorkbasketSummary, + CurrentUserContext.getUserid())); } private void updateTasksToBeTransferred( @@ -397,7 +403,7 @@ public class TaskTransferrer { updateObject.setState(TaskState.READY); updateObject.setOwner(null); taskMapper.updateTransfered(taskIds, updateObject); - if (HistoryEventProducer.isHistoryEnabled()) { + if (HistoryEventManager.isHistoryEnabled()) { createTasksTransferredEvents(taskSummaries, updateObject); } } diff --git a/lib/taskana-core/src/main/resources/sql/db2/taskana-schema-db2.sql b/lib/taskana-core/src/main/resources/sql/db2/taskana-schema-db2.sql index 76980dc93..aed05ee0f 100644 --- a/lib/taskana-core/src/main/resources/sql/db2/taskana-schema-db2.sql +++ b/lib/taskana-core/src/main/resources/sql/db2/taskana-schema-db2.sql @@ -208,7 +208,7 @@ CREATE TABLE SCHEDULED_JOB( CREATE TABLE HISTORY_EVENTS ( - ID INT NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1), + ID VARCHAR(40) NOT NULL, BUSINESS_PROCESS_ID VARCHAR(128) NULL, PARENT_BUSINESS_PROCESS_ID VARCHAR(128) NULL, TASK_ID VARCHAR(40) NULL, diff --git a/lib/taskana-core/src/main/resources/sql/h2/taskana-schema-h2.sql b/lib/taskana-core/src/main/resources/sql/h2/taskana-schema-h2.sql index bfffb2f7c..1affd3da4 100644 --- a/lib/taskana-core/src/main/resources/sql/h2/taskana-schema-h2.sql +++ b/lib/taskana-core/src/main/resources/sql/h2/taskana-schema-h2.sql @@ -211,7 +211,7 @@ CREATE TABLE SCHEDULED_JOB( CREATE TABLE HISTORY_EVENTS ( - ID INT NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1), + ID VARCHAR(40) NOT NULL, BUSINESS_PROCESS_ID VARCHAR(128) NULL, PARENT_BUSINESS_PROCESS_ID VARCHAR(128) NULL, TASK_ID VARCHAR(40) NULL, diff --git a/lib/taskana-core/src/main/resources/sql/postgres/taskana-schema-postgres.sql b/lib/taskana-core/src/main/resources/sql/postgres/taskana-schema-postgres.sql index e4d38c93b..b3e0782f8 100644 --- a/lib/taskana-core/src/main/resources/sql/postgres/taskana-schema-postgres.sql +++ b/lib/taskana-core/src/main/resources/sql/postgres/taskana-schema-postgres.sql @@ -206,7 +206,7 @@ CREATE TABLE SCHEDULED_JOB( CREATE TABLE HISTORY_EVENTS ( - ID INT NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1), + ID VARCHAR(40) NOT NULL, BUSINESS_PROCESS_ID VARCHAR(128) NULL, PARENT_BUSINESS_PROCESS_ID VARCHAR(128) NULL, TASK_ID VARCHAR(40) NULL, diff --git a/lib/taskana-core/src/test/java/acceptance/history/TaskEventProducerTest.java b/lib/taskana-core/src/test/java/acceptance/history/HistoryEventManagerTest.java similarity index 59% rename from lib/taskana-core/src/test/java/acceptance/history/TaskEventProducerTest.java rename to lib/taskana-core/src/test/java/acceptance/history/HistoryEventManagerTest.java index f3c7f8ac5..0c9277626 100644 --- a/lib/taskana-core/src/test/java/acceptance/history/TaskEventProducerTest.java +++ b/lib/taskana-core/src/test/java/acceptance/history/HistoryEventManagerTest.java @@ -5,11 +5,11 @@ import static org.assertj.core.api.Assertions.assertThat; import acceptance.AbstractAccTest; import org.junit.jupiter.api.Test; -/** Acceptance test for historyEventProducer class. */ -class TaskEventProducerTest extends AbstractAccTest { +/** Acceptance test for HistoryEventManager class. */ +class HistoryEventManagerTest extends AbstractAccTest { @Test - void testHistoryEventProducerIsNotEnabled() { + void testHistoryEventManagerIsNotEnabled() { assertThat(taskanaEngine.isHistoryEnabled()).isFalse(); } } diff --git a/lib/taskana-core/src/test/java/acceptance/jobs/TaskCleanupJobAccTest.java b/lib/taskana-core/src/test/java/acceptance/jobs/TaskCleanupJobAccTest.java index b7a3f724a..9be3aa28d 100644 --- a/lib/taskana-core/src/test/java/acceptance/jobs/TaskCleanupJobAccTest.java +++ b/lib/taskana-core/src/test/java/acceptance/jobs/TaskCleanupJobAccTest.java @@ -36,7 +36,7 @@ class TaskCleanupJobAccTest extends AbstractAccTest { createAndCompleteTask(); long totalTasksCount = taskService.createTaskQuery().count(); - assertThat(totalTasksCount).isEqualTo(85); + assertThat(totalTasksCount).isEqualTo(88); taskanaEngine.getConfiguration().setTaskCleanupJobAllCompletedSameParentBusiness(false); @@ -51,7 +51,7 @@ class TaskCleanupJobAccTest extends AbstractAccTest { @Test void shouldCleanCompletedTasksUntilDateWithSameParentBussiness() throws Exception { long totalTasksCount = taskService.createTaskQuery().count(); - assertThat(totalTasksCount).isEqualTo(84); + assertThat(totalTasksCount).isEqualTo(87); taskanaEngine.getConfiguration().setTaskCleanupJobAllCompletedSameParentBusiness(true); diff --git a/lib/taskana-core/src/test/java/acceptance/security/TaskQueryAccTest.java b/lib/taskana-core/src/test/java/acceptance/security/TaskQueryAccTest.java index 5194acb44..378b3ccb5 100644 --- a/lib/taskana-core/src/test/java/acceptance/security/TaskQueryAccTest.java +++ b/lib/taskana-core/src/test/java/acceptance/security/TaskQueryAccTest.java @@ -36,7 +36,7 @@ class TaskQueryAccTest extends AbstractAccTest { List results = taskService.createTaskQuery().ownerLike("%a%", "%u%").list(); - assertThat(results).hasSize(4); + assertThat(results).hasSize(7); } @WithAccessId(user = "user-1-1", groups = "businessadmin") @@ -46,7 +46,7 @@ class TaskQueryAccTest extends AbstractAccTest { List results = taskService.createTaskQuery().ownerLike("%a%", "%u%").list(); - assertThat(results).hasSize(4); + assertThat(results).hasSize(7); } @WithAccessId(user = "user-1-1", groups = "admin") @@ -56,6 +56,6 @@ class TaskQueryAccTest extends AbstractAccTest { List results = taskService.createTaskQuery().ownerLike("%a%", "%u%").list(); - assertThat(results).hasSize(36); + assertThat(results).hasSize(39); } } diff --git a/lib/taskana-core/src/test/java/acceptance/task/CancelTaskAccTest.java b/lib/taskana-core/src/test/java/acceptance/task/CancelTaskAccTest.java index f2290a179..a1a60fe83 100644 --- a/lib/taskana-core/src/test/java/acceptance/task/CancelTaskAccTest.java +++ b/lib/taskana-core/src/test/java/acceptance/task/CancelTaskAccTest.java @@ -89,7 +89,7 @@ class CancelTaskAccTest extends AbstractAccTest { void testCancelCompletedTask() { List taskSummaries = taskService.createTaskQuery().stateIn(TaskState.COMPLETED).list(); - assertThat(taskSummaries).hasSize(7); + assertThat(taskSummaries).hasSize(10); ThrowingCallable taskanaCall = () -> taskService.cancelTask(taskSummaries.get(0).getId()); diff --git a/lib/taskana-core/src/test/java/acceptance/task/DeleteTaskAccTest.java b/lib/taskana-core/src/test/java/acceptance/task/DeleteTaskAccTest.java index faf8722d1..0f56c3980 100644 --- a/lib/taskana-core/src/test/java/acceptance/task/DeleteTaskAccTest.java +++ b/lib/taskana-core/src/test/java/acceptance/task/DeleteTaskAccTest.java @@ -15,14 +15,18 @@ import org.junit.jupiter.api.extension.ExtendWith; import pro.taskana.common.api.BulkOperationResults; import pro.taskana.common.api.exceptions.NotAuthorizedException; import pro.taskana.common.api.exceptions.TaskanaException; +import pro.taskana.common.internal.TaskanaEngineProxyForTest; import pro.taskana.common.internal.security.JaasExtension; import pro.taskana.common.internal.security.WithAccessId; import pro.taskana.task.api.TaskService; import pro.taskana.task.api.exceptions.InvalidStateException; import pro.taskana.task.api.exceptions.TaskNotFoundException; import pro.taskana.task.api.models.Task; +import pro.taskana.task.internal.AttachmentMapper; -/** Acceptance test for all "delete task" scenarios. */ +/** + * Acceptance test for all "delete task" scenarios. + */ @ExtendWith(JaasExtension.class) class DeleteTaskAccTest extends AbstractAccTest { @@ -42,6 +46,54 @@ class DeleteTaskAccTest extends AbstractAccTest { assertThatThrownBy(call).isInstanceOf(NotAuthorizedException.class); } + @WithAccessId(user = "admin") + @Test + void should_deleteAttachments_When_MultipleTasksAreDeleted() + throws NotAuthorizedException, InvalidArgumentException, NoSuchFieldException, + IllegalAccessException { + + TaskService taskService = taskanaEngine.getTaskService(); + + TaskanaEngineProxyForTest engineProxy = new TaskanaEngineProxyForTest(taskanaEngine); + AttachmentMapper attachmentMapper = + engineProxy.getEngine().getSqlSession().getMapper(AttachmentMapper.class); + + assertThat(attachmentMapper.findAttachmentSummariesByTaskIds(Arrays.asList( + "TKI:000000000000000000000000000000000067", "TKI:000000000000000000000000000000000068"))) + .hasSize(4); + + taskService.deleteTasks(Arrays.asList("TKI:000000000000000000000000000000000067", + "TKI:000000000000000000000000000000000068")); + + assertThat(attachmentMapper.findAttachmentSummariesByTaskIds(Arrays.asList( + "TKI:000000000000000000000000000000000067", "TKI:000000000000000000000000000000000068"))) + .hasSize(0); + + } + + @WithAccessId(user = "admin") + @Test + void should_deleteAttachments_When_SingleTaskIsDeleted() + throws NotAuthorizedException, NoSuchFieldException, + IllegalAccessException, InvalidStateException, TaskNotFoundException { + + TaskService taskService = taskanaEngine.getTaskService(); + + TaskanaEngineProxyForTest engineProxy = new TaskanaEngineProxyForTest(taskanaEngine); + AttachmentMapper attachmentMapper = + engineProxy.getSqlSession().getMapper(AttachmentMapper.class); + + assertThat(attachmentMapper.findAttachmentsByTaskId("TKI:000000000000000000000000000000000069")) + .hasSize(1); + + taskService.deleteTask( + "TKI:000000000000000000000000000000000069"); + + assertThat(attachmentMapper.findAttachmentsByTaskId("TKI:000000000000000000000000000000000069")) + .hasSize(0); + + } + @WithAccessId(user = "businessadmin") @WithAccessId(user = "taskadmin") @WithAccessId(user = "user-1-1") diff --git a/lib/taskana-core/src/test/java/acceptance/task/QueryTaskByClassificationNameAccTest.java b/lib/taskana-core/src/test/java/acceptance/task/QueryTaskByClassificationNameAccTest.java index 2f71bd5d9..4f8178d75 100644 --- a/lib/taskana-core/src/test/java/acceptance/task/QueryTaskByClassificationNameAccTest.java +++ b/lib/taskana-core/src/test/java/acceptance/task/QueryTaskByClassificationNameAccTest.java @@ -107,14 +107,14 @@ class QueryTaskByClassificationNameAccTest extends AbstractAccTest { .attachmentClassificationNameLike("Widerruf", "Beratungsprotokoll", "Dynamik%") .orderByAttachmentClassificationName(SortDirection.ASCENDING) .list(); - assertThat(tasks).hasSize(7); + assertThat(tasks).hasSize(10); // make sure that unordered query returns the same number of objects tasks = taskService .createTaskQuery() .attachmentClassificationNameLike("Widerruf", "Beratungsprotokoll", "Dynamik%") .list(); - assertThat(tasks).hasSize(7); + assertThat(tasks).hasSize(10); } @WithAccessId(user = "user-1-1") @@ -128,14 +128,14 @@ class QueryTaskByClassificationNameAccTest extends AbstractAccTest { .attachmentClassificationNameIn("Widerruf", "Beratungsprotokoll", "Dynamikänderung") .orderByAttachmentClassificationName(SortDirection.ASCENDING) .list(); - assertThat(tasks).hasSize(4); + assertThat(tasks).hasSize(7); // make sure that unordered query returns the same number of objects tasks = taskService .createTaskQuery() .attachmentClassificationNameIn("Widerruf", "Beratungsprotokoll", "Dynamikänderung") .list(); - assertThat(tasks).hasSize(4); + assertThat(tasks).hasSize(7); } @WithAccessId(user = "user-1-1") @@ -162,12 +162,12 @@ class QueryTaskByClassificationNameAccTest extends AbstractAccTest { // we expect 4 result objects in this case, because task // TKI:000000000000000000000000000000000001 has 2 attachments with different Classifications // therefore task TKI:000000000000000000000000000000000001 occurs twice in the result set - assertThat(tasks).hasSize(4); + assertThat(tasks).hasSize(7); long numberOfTasks = taskQuery .attachmentClassificationNameIn("Widerruf", "Beratungsprotokoll", "Dynamikänderung") .count(); - assertThat(numberOfTasks).isEqualTo(3); + assertThat(numberOfTasks).isEqualTo(6); // the count returns only the number of tasks that have an attachment with the specified // classification name. // therefore, task 001 is counted only once. diff --git a/lib/taskana-core/src/test/java/acceptance/task/QueryTasksAccTest.java b/lib/taskana-core/src/test/java/acceptance/task/QueryTasksAccTest.java index dc9c78210..417ba8a23 100644 --- a/lib/taskana-core/src/test/java/acceptance/task/QueryTasksAccTest.java +++ b/lib/taskana-core/src/test/java/acceptance/task/QueryTasksAccTest.java @@ -145,7 +145,7 @@ class QueryTasksAccTest extends AbstractAccTest { List results = taskService.createTaskQuery().ownerLike("%a%", "%u%").orderByCreated(ASCENDING).list(); - assertThat(results).hasSize(36); + assertThat(results).hasSize(39); TaskSummary previousSummary = null; for (TaskSummary taskSummary : results) { if (previousSummary != null) { @@ -206,7 +206,7 @@ class QueryTasksAccTest extends AbstractAccTest { List result4 = taskService.createTaskQuery().classificationKeyNotIn("L1050", "L1060", "T2100").list(); - assertThat(result4).hasSize(7); + assertThat(result4).hasSize(10); } @WithAccessId(user = "admin") @@ -256,7 +256,7 @@ class QueryTasksAccTest extends AbstractAccTest { .createTaskQuery() .externalIdLike("ETI:000000000000000000000000000000%") .listValues(TaskQueryColumnName.EXTERNAL_ID, DESCENDING); - assertThat(resultValues).hasSize(71); + assertThat(resultValues).hasSize(74); long countAllExternalIds = taskService.createTaskQuery().externalIdLike("ETI:%").count(); long countAllIds = taskService.createTaskQuery().count(); @@ -475,7 +475,7 @@ class QueryTasksAccTest extends AbstractAccTest { @Test void testQueryForReadEquals() { List results = taskService.createTaskQuery().readEquals(true).list(); - assertThat(results).hasSize(36); + assertThat(results).hasSize(39); } @WithAccessId(user = "admin") @@ -497,7 +497,7 @@ class QueryTasksAccTest extends AbstractAccTest { @Test void testQueryForBusinessProcessIdLike() { List results = taskService.createTaskQuery().businessProcessIdLike("pI_%").list(); - assertThat(results).hasSize(77); + assertThat(results).hasSize(80); } @WithAccessId(user = "admin") diff --git a/lib/taskana-core/src/test/java/acceptance/task/QueryTasksByTimeIntervalsAccTest.java b/lib/taskana-core/src/test/java/acceptance/task/QueryTasksByTimeIntervalsAccTest.java index 8380f46b0..2705d05a0 100644 --- a/lib/taskana-core/src/test/java/acceptance/task/QueryTasksByTimeIntervalsAccTest.java +++ b/lib/taskana-core/src/test/java/acceptance/task/QueryTasksByTimeIntervalsAccTest.java @@ -42,7 +42,7 @@ class QueryTasksByTimeIntervalsAccTest extends AbstractAccTest { .orderByCreated(asc) .list(); - assertThat(results).hasSize(50); + assertThat(results).hasSize(53); TaskSummary previousSummary = null; for (TaskSummary taskSummary : results) { Instant cr = taskSummary.getCreated(); @@ -88,7 +88,7 @@ class QueryTasksByTimeIntervalsAccTest extends AbstractAccTest { List results = taskService.createTaskQuery().createdWithin(interval1).orderByCreated(asc).list(); - assertThat(results).hasSize(48); + assertThat(results).hasSize(51); TaskSummary previousSummary = null; for (TaskSummary taskSummary : results) { Instant cr = taskSummary.getCreated(); @@ -118,7 +118,7 @@ class QueryTasksByTimeIntervalsAccTest extends AbstractAccTest { .orderByCreated(asc) .list(); - assertThat(results).hasSize(35); + assertThat(results).hasSize(38); TaskSummary previousSummary = null; for (TaskSummary taskSummary : results) { Instant cr = taskSummary.getClaimed(); @@ -141,7 +141,7 @@ class QueryTasksByTimeIntervalsAccTest extends AbstractAccTest { List results = taskService.createTaskQuery().completedWithin(interval).orderByCompleted(asc).list(); - assertThat(results).hasSize(15); + assertThat(results).hasSize(18); TaskSummary previousSummary = null; for (TaskSummary taskSummary : results) { Instant cr = taskSummary.getCompleted(); @@ -187,7 +187,7 @@ class QueryTasksByTimeIntervalsAccTest extends AbstractAccTest { List results = taskService.createTaskQuery().plannedWithin(interval).orderByPlanned(asc).list(); - assertThat(results).hasSize(82); + assertThat(results).hasSize(85); TaskSummary previousSummary = null; for (TaskSummary taskSummary : results) { Instant cr = taskSummary.getPlanned(); @@ -210,7 +210,7 @@ class QueryTasksByTimeIntervalsAccTest extends AbstractAccTest { List results = taskService.createTaskQuery().dueWithin(interval).orderByPlanned(asc).list(); - assertThat(results).hasSize(82); + assertThat(results).hasSize(85); TaskSummary previousSummary = null; for (TaskSummary taskSummary : results) { Instant cr = taskSummary.getDue(); diff --git a/lib/taskana-core/src/test/java/acceptance/task/QueryTasksByWorkbasketAccTest.java b/lib/taskana-core/src/test/java/acceptance/task/QueryTasksByWorkbasketAccTest.java index aa3e68e8e..f2f1fb150 100644 --- a/lib/taskana-core/src/test/java/acceptance/task/QueryTasksByWorkbasketAccTest.java +++ b/lib/taskana-core/src/test/java/acceptance/task/QueryTasksByWorkbasketAccTest.java @@ -39,7 +39,7 @@ class QueryTasksByWorkbasketAccTest extends AbstractAccTest { .createTaskQuery() .workbasketKeyDomainIn(workbasketIdentifiers.toArray(new KeyDomain[0])) .list(); - assertThat(results).hasSize(30); + assertThat(results).hasSize(55); String[] ids = results.stream() @@ -48,7 +48,7 @@ class QueryTasksByWorkbasketAccTest extends AbstractAccTest { .toArray(new String[0]); List result2 = taskService.createTaskQuery().workbasketIdIn(ids).list(); - assertThat(result2).hasSize(30); + assertThat(result2).hasSize(59); } @WithAccessId(user = "user-1-1") diff --git a/lib/taskana-core/src/test/java/acceptance/task/SetOwnerAccTest.java b/lib/taskana-core/src/test/java/acceptance/task/SetOwnerAccTest.java index 9f19fc0d4..87c91ad06 100644 --- a/lib/taskana-core/src/test/java/acceptance/task/SetOwnerAccTest.java +++ b/lib/taskana-core/src/test/java/acceptance/task/SetOwnerAccTest.java @@ -164,7 +164,7 @@ class SetOwnerAccTest extends AbstractAccTest { allTaskSummaries.stream().map(TaskSummary::getId).collect(Collectors.toList()); BulkOperationResults results = taskanaEngine.getTaskService().setOwnerOfTasks("theWorkaholic", allTaskIds); - assertThat(allTaskSummaries).hasSize(84); + assertThat(allTaskSummaries).hasSize(87); assertThat(results.containsErrors()).isTrue(); Condition invalidStateException = @@ -173,10 +173,10 @@ class SetOwnerAccTest extends AbstractAccTest { new Condition<>( c -> c.getClass() == NotAuthorizedException.class, "NotAuthorizedException"); assertThat(results.getErrorMap()) - .hasSize(82) + .hasSize(62) .extractingFromEntries(Entry::getValue) .hasOnlyElementsOfTypes(InvalidStateException.class, NotAuthorizedException.class) - .areExactly(28, invalidStateException) + .areExactly(39, invalidStateException) .areExactly(54, notAuthorizedException); } @@ -189,10 +189,10 @@ class SetOwnerAccTest extends AbstractAccTest { allTaskSummaries.stream().map(TaskSummary::getId).collect(Collectors.toList()); BulkOperationResults results = taskanaEngine.getTaskService().setOwnerOfTasks("theWorkaholic", allTaskIds); - assertThat(allTaskSummaries).hasSize(84); + assertThat(allTaskSummaries).hasSize(87); assertThat(results.containsErrors()).isTrue(); assertThat(results.getErrorMap()) - .hasSize(37) + .hasSize(40) .extractingFromEntries(Entry::getValue) .hasOnlyElementsOfType(InvalidStateException.class); } diff --git a/lib/taskana-core/src/test/java/acceptance/task/TerminateTaskAccTest.java b/lib/taskana-core/src/test/java/acceptance/task/TerminateTaskAccTest.java index 704a1335a..2f04769f6 100644 --- a/lib/taskana-core/src/test/java/acceptance/task/TerminateTaskAccTest.java +++ b/lib/taskana-core/src/test/java/acceptance/task/TerminateTaskAccTest.java @@ -79,7 +79,7 @@ class TerminateTaskAccTest extends AbstractAccTest { void should_ThrowException_When_TerminateTaskWithTaskStateCompleted() { List taskSummaries = taskService.createTaskQuery().stateIn(TaskState.COMPLETED).list(); - assertThat(taskSummaries.size()).isEqualTo(7); + assertThat(taskSummaries.size()).isEqualTo(10); ThrowingCallable taskanaCall = () -> taskService.terminateTask(taskSummaries.get(0).getId()); assertThatThrownBy(taskanaCall).isInstanceOf(InvalidStateException.class); diff --git a/lib/taskana-core/src/test/java/pro/taskana/task/internal/TaskTransferrerTest.java b/lib/taskana-core/src/test/java/pro/taskana/task/internal/TaskTransferrerTest.java index c1b4343f4..3fd3bfbb5 100644 --- a/lib/taskana-core/src/test/java/pro/taskana/task/internal/TaskTransferrerTest.java +++ b/lib/taskana-core/src/test/java/pro/taskana/task/internal/TaskTransferrerTest.java @@ -89,7 +89,7 @@ class TaskTransferrerTest { verify(taskMapperMock, times(1)).update(any()); verify(internalTaskanaEngineMock, times(1)).returnConnection(); verify(internalTaskanaEngineMock, times(1)).getEngine(); - verify(internalTaskanaEngineMock).getHistoryEventProducer(); + verify(internalTaskanaEngineMock).getHistoryEventManager(); verify(taskanaEngineMock).getWorkbasketService(); verifyNoMoreInteractions( attachmentMapperMock, diff --git a/lib/taskana-core/src/test/resources/taskana.properties b/lib/taskana-core/src/test/resources/taskana.properties index ac1d20476..b79925b1d 100644 --- a/lib/taskana-core/src/test/resources/taskana.properties +++ b/lib/taskana-core/src/test/resources/taskana.properties @@ -14,4 +14,6 @@ taskana.jobs.cleanup.firstRunAt=2018-07-25T08:00:00Z taskana.jobs.cleanup.minimumAge=P14D taskana.german.holidays.enabled=true taskana.german.holidays.corpus-christi.enabled=false +taskana.history.deletion.on.task.deletion.enabled=true + diff --git a/lib/taskana-data/src/main/java/pro/taskana/sampledata/SampleDataProvider.java b/lib/taskana-data/src/main/java/pro/taskana/sampledata/SampleDataProvider.java index 817b4bc1c..2a0666c7f 100644 --- a/lib/taskana-data/src/main/java/pro/taskana/sampledata/SampleDataProvider.java +++ b/lib/taskana-data/src/main/java/pro/taskana/sampledata/SampleDataProvider.java @@ -13,11 +13,12 @@ public final class SampleDataProvider { static final String TEST_CLASSIFICATION = "/sql/test-data/classification.sql"; static final String TEST_OBJECT_REFERENCE = "/sql/test-data/object-reference.sql"; static final String TEST_ATTACHMENT = "/sql/test-data/attachment.sql"; + static final String TEST_HISTORY_EVENT = "/sql/test-data/history-event.sql"; static final String MONITOR_SAMPLE_DATA = "/sql/monitor-data/monitor-sample-data.sql"; private static final String DB_CLEAR_TABLES_SCRIPT = "/sql/clear/clear-db.sql"; private static final String DB_DROP_TABLES_SCRIPT = "/sql/clear/drop-tables.sql"; private static final String CLEAR_HISTORY_EVENTS = "/sql/clear/clear-history-events.sql"; - private static final String HISTORY_EVENT = "/sql/sample-data/history-event.sql"; + private static final String SAMPLE_HISTORY_EVENT = "/sql/sample-data/history-event.sql"; private static final String SAMPLE_TASK = "/sql/sample-data/task.sql"; private static final String SAMPLE_TASK_COMMENT = "/sql/sample-data/task-comment.sql"; private static final String SAMPLE_WORKBASKET = "/sql/sample-data/workbasket.sql"; @@ -45,7 +46,7 @@ public final class SampleDataProvider { static Stream getScriptsWithEvents() { return Stream.concat( - getSampleDataCreationScripts(), Stream.of(CLEAR_HISTORY_EVENTS, HISTORY_EVENT)); + getSampleDataCreationScripts(), Stream.of(CLEAR_HISTORY_EVENTS, SAMPLE_HISTORY_EVENT)); } static Stream getScriptsToClearDatabase() { @@ -61,6 +62,7 @@ public final class SampleDataProvider { TEST_CLASSIFICATION, TEST_WORKBASKET, TEST_TASK, + TEST_HISTORY_EVENT, TEST_TASK_COMMENT, TEST_WORKBASKET_ACCESS_LIST, TEST_DISTRIBUTION_TARGETS, diff --git a/lib/taskana-data/src/main/resources/sql/clear/clear-db.sql b/lib/taskana-data/src/main/resources/sql/clear/clear-db.sql index fc058ebbb..10bfafded 100644 --- a/lib/taskana-data/src/main/resources/sql/clear/clear-db.sql +++ b/lib/taskana-data/src/main/resources/sql/clear/clear-db.sql @@ -1,5 +1,6 @@ -- the order is important! DELETE FROM TASK_COMMENT; +DELETE FROM HISTORY_EVENTS; DELETE FROM CONFIGURATION; DELETE FROM ATTACHMENT; DELETE FROM TASK; diff --git a/lib/taskana-data/src/main/resources/sql/test-data/attachment.sql b/lib/taskana-data/src/main/resources/sql/test-data/attachment.sql index 5b5ccce12..476ff4d42 100644 --- a/lib/taskana-data/src/main/resources/sql/test-data/attachment.sql +++ b/lib/taskana-data/src/main/resources/sql/test-data/attachment.sql @@ -17,3 +17,8 @@ INSERT INTO ATTACHMENT VALUES('TAI:000000000000000000000000000000000013','TKI:00 INSERT INTO ATTACHMENT VALUES('TAI:000000000000000000000000000000000014','TKI:000000000000000000000000000000000010', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'L11010' , 'CLI:100000000000000000000000000000000004', 'novatec' , 'novasys', 'nvinst', 'typ5', 'val4', 'ch8', null, null); INSERT INTO ATTACHMENT VALUES('TAI:000000000000000000000000000000000015','TKI:000000000000000000000000000000000011', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'L10303' , 'CLI:100000000000000000000000000000000002', 'novatec' , 'novasys', 'nvinst', 'typ5', 'val5', 'ch8', '2018-01-30 12:00:00', null); INSERT INTO ATTACHMENT VALUES('TAI:000000000000000000000000000000000016','TKI:000000000000000000000000000000000012', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'L1050' , 'CLI:100000000000000000000000000000000003', 'novatec' , 'novasys', 'nvinst', 'typ5', 'val6', 'ch8', null, null); +INSERT INTO ATTACHMENT VALUES('TAI:000000000000000000000000000000000017','TKI:000000000000000000000000000000000067', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'L1050' , 'CLI:100000000000000000000000000000000003', 'novatec' , 'novasys', 'nvinst', 'typ1', 'val1', 'ch1', null, null); +INSERT INTO ATTACHMENT VALUES('TAI:000000000000000000000000000000000018','TKI:000000000000000000000000000000000067', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'L1050' , 'CLI:100000000000000000000000000000000003', 'novatec' , 'novasys', 'nvinst', 'typ1', 'val1', 'ch1', null, null); +INSERT INTO ATTACHMENT VALUES('TAI:000000000000000000000000000000000019','TKI:000000000000000000000000000000000068', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'L1050' , 'CLI:100000000000000000000000000000000003', 'novatec' , 'novasys', 'nvinst', 'typ1', 'val1', 'ch1', null, null); +INSERT INTO ATTACHMENT VALUES('TAI:000000000000000000000000000000000020','TKI:000000000000000000000000000000000068', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'L1050' , 'CLI:100000000000000000000000000000000003', 'novatec' , 'novasys', 'nvinst', 'typ1', 'val1', 'ch1', null, null); +INSERT INTO ATTACHMENT VALUES('TAI:000000000000000000000000000000000021','TKI:000000000000000000000000000000000069', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'L1050' , 'CLI:100000000000000000000000000000000003', 'novatec' , 'novasys', 'nvinst', 'typ1', 'val1', 'ch1', null, null); diff --git a/lib/taskana-data/src/main/resources/sql/test-data/history-event.sql b/lib/taskana-data/src/main/resources/sql/test-data/history-event.sql new file mode 100644 index 000000000..ef3b44410 --- /dev/null +++ b/lib/taskana-data/src/main/resources/sql/test-data/history-event.sql @@ -0,0 +1,17 @@ +INSERT INTO HISTORY_EVENTS (ID,BUSINESS_PROCESS_ID, PARENT_BUSINESS_PROCESS_ID, TASK_ID, EVENT_TYPE, CREATED, USER_ID, DOMAIN, WORKBASKET_KEY, POR_COMPANY, POR_SYSTEM, POR_INSTANCE, POR_TYPE, POR_VALUE, TASK_CLASSIFICATION_KEY, + TASK_CLASSIFICATION_CATEGORY, ATTACHMENT_CLASSIFICATION_KEY, OLD_VALUE, NEW_VALUE, CUSTOM_1, CUSTOM_2, CUSTOM_3, CUSTOM_4, DETAILS) VALUES +-- BUSINESS_PROCESS_ID, PARENT_BUSINESS_PROCESS_ID, TASK_ID, EVENT_TYPE, CREATED, USER_ID, DOMAIN, WORKBASKET_KEY, POR_COMPANY , POR_SYSTEM, POR_INSTANCE , POR_TYPE , POR_VALUE , TASK_CLASSIFICATION_KEY, TASK_CLASSIFICATION_CATEGORY , ATTACHMENT_CLASSIFICATION_KEY , OLD_VALUE , NEW_VALUE , CUSTOM_1 , CUSTOM_2 , CUSTOM_3 , CUSTOM_4, details +('HEI:000000000000000000000000000000000000','BPI:01' ,'' ,'TKI:000000000000000000000000000000000000', 'TASK_UPDATED', '2018-01-29 15:55:00' , 'admin', 'DOMAIN_B', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344', 'L140101' , 'TASK' ,'' ,'old_val' ,'new_val' ,'custom1' ,'custom2' , 'custom3' ,'custom4', '{"changes":[{"newValue":"BPI:01","fieldName":"businessProcessId","oldValue":"BPI:02"},{"newValue":"admin","fieldName":"owner","oldValue":"owner1"}]}' ), +('HEI:000000000000000000000000000000000001','BPI:02' ,'' ,'TKI:000000000000000000000000000000000000', 'TASK_CREATED', '2018-01-29 15:55:01','peter', 'DOMAIN_A', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344', '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom2' ,'' ,'someDetails' ), +('HEI:000000000000000000000000000000000002','BPI:03' ,'BPI:01','TKI:000000000000000000000000000000000036','TASK_CREATED', '2018-01-29 15:55:02' , 'admin', 'DOMAIN_A', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'' ,'someDetails' ), +('HEI:000000000000000000000000000000000003','BPI:02' ,'' ,'TKI:000000000000000000000000000000000036', 'TASK_CREATED', '2018-01-29 15:55:03','peter', 'DOMAIN_A', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344', '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom2' ,'' ,'someDetails' ), +('HEI:000000000000000000000000000000000004','BPI:03' ,'BPI:01','TKI:000000000000000000000000000000000037','TASK_CREATED', '2018-01-29 15:55:04' , 'admin', 'DOMAIN_A', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'' ,'someDetails' ), +('HEI:000000000000000000000000000000000005','BPI:02' ,'' ,'TKI:000000000000000000000000000000000038', 'TASK_CREATED', '2018-01-29 15:55:05','peter', 'DOMAIN_A', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344', '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom2' ,'' ,'someDetails' ), +('HEI:000000000000000000000000000000000006','BPI:03' ,'BPI:01','TKI:000000000000000000000000000000000038','TASK_CREATED', '2018-01-29 15:55:06' , 'admin', 'DOMAIN_A', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'' ,'someDetails' ), +('HEI:000000000000000000000000000000000007','BPI:02' ,'' ,'TKI:000000000000000000000000000000000039', 'TASK_CREATED', '2018-01-29 15:55:07','peter', 'DOMAIN_A', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344', '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom2' ,'' ,'someDetails' ), +('HEI:000000000000000000000000000000000008','BPI:03' ,'BPI:01','TKI:000000000000000000000000000000000039','TASK_CREATED', '2018-01-29 15:55:08' , 'admin', 'DOMAIN_A', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'' ,'someDetails' ), +('HEI:000000000000000000000000000000000009','BPI:02' ,'' ,'TKI:000000000000000000000000000000000040', 'TASK_CREATED', '2018-01-29 15:55:09','peter', 'DOMAIN_A', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344', '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom2' ,'' ,'someDetails' ), +('HEI:000000000000000000000000000000000010','BPI:03' ,'BPI:01','TKI:000000000000000000000000000000000040','TASK_CREATED', '2018-01-29 15:55:10' , 'admin', 'DOMAIN_A', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'' ,'someDetails' ), +('HEI:000000000000000000000000000000000011','BPI:02' ,'' ,'TKI:000000000000000000000000000000000066', 'TASK_CREATED', '2018-01-29 15:55:11','peter', 'DOMAIN_A', 'WBI:100000000000000000000000000000000001', '00' , 'PASystem', '00' , 'VNR' , '11223344', '' , '' ,'' ,'2old_val' ,'new_val2' ,'custom1' ,'' , 'custom2' ,'' ,'someDetails' ), +('HEI:000000000000000000000000000000000012','BPI:03' ,'BPI:01','TKI:000000000000000000000000000000000066','TASK_CREATED', '2018-01-29 15:55:12' , 'admin', 'DOMAIN_A', 'WBI:100000000000000000000000000000000002', '11' , '' , '22' , '' , '' , 'L140101' , 'TASK' ,'DOCTYPE_DEFAULT' ,'' ,'' ,'custom1' ,'' , 'custom3' ,'' ,'someDetails' ) +; diff --git a/lib/taskana-data/src/main/resources/sql/test-data/task.sql b/lib/taskana-data/src/main/resources/sql/test-data/task.sql index 26548d03e..3f7e8d2b0 100644 --- a/lib/taskana-data/src/main/resources/sql/test-data/task.sql +++ b/lib/taskana-data/src/main/resources/sql/test-data/task.sql @@ -47,6 +47,9 @@ INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000037', 'ETI:0000000 INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000038', 'ETI:000000000000000000000000000000000038', '2018-01-29 15:55:24', '2018-01-30 15:55:24', '2018-01-30 16:55:24', '2018-01-30 16:55:24', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'COMPLETED' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000007' , 'USER-1-2' , 'DOMAIN_A', 'PI_0000000000038' , 'DOC_0000000000000000038' , 'user-1-2' , '00' , 'PASystem' , '00' , 'SDNR' , '00011122' , true , false , null , 'NONE' , null , null , null , null , null , 'al' , '11' , null , null , null , null , null , null , null , 'abc' , null , null ); INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000039', 'ETI:000000000000000000000000000000000039', '2018-01-29 15:55:24', '2018-01-30 15:55:24', '2018-01-30 16:55:24', '2018-01-30 16:55:24', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'COMPLETED' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000007' , 'USER-1-2' , 'DOMAIN_A', 'PI_0000000000039' , 'DOC_0000000000000000039' , 'user-1-2' , '00' , 'PASystem' , '00' , 'SDNR' , '00011122' , true , false , null , 'NONE' , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000040', 'ETI:000000000000000000000000000000000040', '2018-01-29 15:55:24', '2018-01-30 15:55:24', '2018-01-30 16:55:24', '2018-01-30 16:55:24', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'COMPLETED' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000007' , 'USER-1-2' , 'DOMAIN_A', 'PI_0000000000040' , 'DOC_0000000000000000040' , 'user-1-2' , '00' , 'PASystem' , '00' , 'SDNR' , '00011122' , true , false , null , 'NONE' , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); +INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000067', 'ETI:000000000000000000000000000000000067', '2018-01-29 15:55:24', '2018-01-30 15:55:24', '2018-01-30 16:55:24', '2018-01-30 16:55:24', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'COMPLETED' , 'EXTERN' , 'T2000' , 'CLI:100000000000000000000000000000000016', 'WBI:100000000000000000000000000000000006' , 'USER-1-2' , 'DOMAIN_A', 'PI_0000000000067' , 'DOC_0000000000000000067' , 'user-1-2' , '00' , 'PASystem' , '00' , 'SDNR' , '00011122' , true , false , null , 'NONE' , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); +INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000068', 'ETI:000000000000000000000000000000000068', '2018-01-29 15:55:24', '2018-01-30 15:55:24', '2018-01-30 16:55:24', '2018-01-30 16:55:24', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'COMPLETED' , 'EXTERN' , 'T2000' , 'CLI:100000000000000000000000000000000016', 'WBI:100000000000000000000000000000000006' , 'USER-1-2' , 'DOMAIN_A', 'PI_0000000000068' , 'DOC_0000000000000000068' , 'user-1-2' , '00' , 'PASystem' , '00' , 'SDNR' , '00011122' , true , false , null , 'NONE' , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); +INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000069', 'ETI:000000000000000000000000000000000069', '2018-01-29 15:55:24', '2018-01-30 15:55:24', '2018-01-30 16:55:24', '2018-01-30 16:55:24', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'COMPLETED' , 'EXTERN' , 'T2000' , 'CLI:100000000000000000000000000000000016', 'WBI:100000000000000000000000000000000006' , 'USER-1-2' , 'DOMAIN_A', 'PI_0000000000068' , 'DOC_0000000000000000068' , 'user-1-2' , '00' , 'PASystem' , '00' , 'SDNR' , '00011122' , true , false , null , 'NONE' , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); -- Tasks for QueryTasksWithSortingTest INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000041', 'ETI:000000000000000000000000000000000041', '2018-01-29 15:55:00', '2018-01-30 15:55:00', null , '2018-01-30 15:55:00', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Task99' , 'creator_user_id' , 'Lorem ipsum was n Quatsch dolor sit amet.', 'Some custom Note' , 1 , 'CLAIMED' , 'AUTOMATIC' , 'T6310' , 'CLI:000000000000000000000000000000000011', 'WBI:100000000000000000000000000000000015' , 'USER-B-2' , 'DOMAIN_B', 'BPI21' , 'PBPI21' , 'user-b-1' , 'MyCompany1', 'MySystem1', 'MyInstance1' , 'MyType1', 'MyValue1' , true , false , null , 'NONE' , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/task/rest/TaskController.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/task/rest/TaskController.java index ef0eb47e1..829fb9804 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/task/rest/TaskController.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/task/rest/TaskController.java @@ -4,10 +4,10 @@ import java.time.Instant; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; import java.util.stream.Stream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.hateoas.PagedModel.PageMetadata; import org.springframework.hateoas.config.EnableHypermediaSupport; import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType; @@ -26,11 +26,13 @@ import org.springframework.web.bind.annotation.RestController; import pro.taskana.classification.api.exceptions.ClassificationNotFoundException; import pro.taskana.common.api.BaseQuery.SortDirection; +import pro.taskana.common.api.BulkOperationResults; import pro.taskana.common.api.KeyDomain; import pro.taskana.common.api.TimeInterval; import pro.taskana.common.api.exceptions.ConcurrencyException; import pro.taskana.common.api.exceptions.InvalidArgumentException; import pro.taskana.common.api.exceptions.NotAuthorizedException; +import pro.taskana.common.api.exceptions.TaskanaException; import pro.taskana.common.rest.AbstractPagingController; import pro.taskana.common.rest.Mapping; import pro.taskana.common.rest.models.TaskanaPagedModel; @@ -69,6 +71,7 @@ public class TaskController extends AbstractPagingController { private static final String OWNER = "owner"; private static final String OWNER_LIKE = "owner-like"; private static final String DOMAIN = "domain"; + private static final String TASK_ID = "task-id"; private static final String WORKBASKET_ID = "workbasket-id"; private static final String WORKBASKET_KEY = "workbasket-key"; private static final String CLASSIFICATION_KEY = "classification.key"; @@ -133,6 +136,41 @@ public class TaskController extends AbstractPagingController { return response; } + @DeleteMapping(path = Mapping.URL_TASKS) + @Transactional(readOnly = true, rollbackFor = Exception.class) + public ResponseEntity> deleteTasks( + @RequestParam MultiValueMap params) + throws InvalidArgumentException, NotAuthorizedException { + + LOGGER.debug("Entry to deleteTasks(params= {})", params); + + TaskQuery query = taskService.createTaskQuery(); + query = applyFilterParams(query, params); + validateNoInvalidParameterIsLeft(params); + + List taskSummaries = getQueryList(query, null); + + List taskIdsToDelete = + taskSummaries.stream().map(TaskSummary::getId).collect(Collectors.toList()); + + BulkOperationResults result = + taskService.deleteTasks(taskIdsToDelete); + + List successfullyDeletedTaskSummaries = + taskSummaries.stream() + .filter(summary -> !result.getFailedIds().contains(summary.getId())) + .collect(Collectors.toList()); + + ResponseEntity> response = + ResponseEntity.ok( + taskSummaryRepresentationModelAssembler.toCollectionModel( + successfullyDeletedTaskSummaries)); + + LOGGER.debug("Exit from deleteTasks(), returning {}", response); + + return response; + } + @GetMapping(path = Mapping.URL_TASKS_ID) @Transactional(readOnly = true, rollbackFor = Exception.class) public ResponseEntity getTask(@PathVariable String taskId) @@ -342,6 +380,11 @@ public class TaskController extends AbstractPagingController { taskQuery.classificationKeyIn(classificationKeys); params.remove(CLASSIFICATION_KEY); } + if (params.containsKey(TASK_ID)) { + String[] taskIds = extractCommaSeparatedFields(params.get(TASK_ID)); + taskQuery.idIn(taskIds); + params.remove(TASK_ID); + } if (params.containsKey(WORKBASKET_ID)) { String[] workbaskets = extractCommaSeparatedFields(params.get(WORKBASKET_ID)); taskQuery.workbasketIdIn(workbaskets); @@ -438,6 +481,13 @@ public class TaskController extends AbstractPagingController { params.remove(EXTERNAL_ID); } + for (int i = 1; i < 17; i++) { + if (params.containsKey(CUSTOM + i)) { + taskQuery.customAttributeIn(String.valueOf(i), params.get(CUSTOM + i).get(0)); + params.remove(CUSTOM + i); + } + } + for (int i = 1; i <= 16; i++) { if (params.containsKey(CUSTOM + i)) { String[] customValues = extractCommaSeparatedFields(params.get(CUSTOM + i)); diff --git a/rest/taskana-rest-spring/src/test/java/pro/taskana/task/rest/TaskControllerIntTest.java b/rest/taskana-rest-spring/src/test/java/pro/taskana/task/rest/TaskControllerIntTest.java index f2d798750..b251296e4 100644 --- a/rest/taskana-rest-spring/src/test/java/pro/taskana/task/rest/TaskControllerIntTest.java +++ b/rest/taskana-rest-spring/src/test/java/pro/taskana/task/rest/TaskControllerIntTest.java @@ -18,6 +18,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.ParameterizedTypeReference; +import org.springframework.hateoas.CollectionModel; import org.springframework.hateoas.IanaLinkRelations; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; @@ -240,6 +241,27 @@ class TaskControllerIntTest { assertThat(response.getBody().getContent()).hasSize(4); } + + @Test + void should_DeleteAllTasks_For_ProvidedParams() { + ResponseEntity> response = + template.exchange( + restHelper.toUrl(Mapping.URL_TASKS) + + "?task-id=TKI:000000000000000000000000000000000036," + + "TKI:000000000000000000000000000000000037," + + "TKI:000000000000000000000000000000000038" + + "&custom14=abc", + HttpMethod.DELETE, + new HttpEntity(restHelper.getHeadersAdmin()), + new ParameterizedTypeReference>() { + }); + ; + assertThat(response.getBody()).isNotNull(); + assertThat((response.getBody()).getLink(IanaLinkRelations.SELF)) + .isNotNull(); + assertThat(response.getBody().getContent()).hasSize(3); + } + @Test void should_ThrowException_When_ProvidingInvalidWildcardSearchParameters() { diff --git a/rest/taskana-rest-spring/src/test/resources/taskana.properties b/rest/taskana-rest-spring/src/test/resources/taskana.properties index a96fecaf1..357b57464 100644 --- a/rest/taskana-rest-spring/src/test/resources/taskana.properties +++ b/rest/taskana-rest-spring/src/test/resources/taskana.properties @@ -15,3 +15,5 @@ taskana.jobs.cleanup.runEvery=P1D taskana.jobs.cleanup.firstRunAt=2018-07-25T08:00:00Z taskana.jobs.cleanup.minimumAge=P14D taskana.german.holidays.enabled=true +taskana.history.deletion.on.task.deletion.enabled=true +