TSK-442 improve task refresh on classification update

This commit is contained in:
BerndBreier 2020-03-03 16:40:40 +01:00
parent 9251975f47
commit 87fd1af390
13 changed files with 480 additions and 119 deletions

View File

@ -135,6 +135,8 @@ public class TaskanaEngineConfiguration {
this.rolesSeparator = rolesSeparator;
}
setGermanPublicHolidaysEnabled(true);
if (dataSource != null) {
this.dataSource = dataSource;
} else {

View File

@ -23,6 +23,7 @@ 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.apache.ibatis.type.JdbcType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -250,6 +251,7 @@ public class TaskanaEngineImpl implements TaskanaEngine {
// register type handlers
configuration.getTypeHandlerRegistry().register(new MapTypeHandler());
configuration.getTypeHandlerRegistry().register(Instant.class, new InstantTypeHandler());
configuration.getTypeHandlerRegistry().register(JdbcType.TIMESTAMP,new InstantTypeHandler());
// add mappers
configuration.addMapper(TaskMapper.class);
configuration.addMapper(MonitorMapper.class);

View File

@ -3,7 +3,6 @@ package pro.taskana.common.internal.jobs;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -43,16 +42,18 @@ public class ClassificationChangedJob extends AbstractTaskanaJob {
LOGGER.info("Running ClassificationChangedJob for classification ({})", classificationId);
try {
TaskServiceImpl taskService = (TaskServiceImpl) taskanaEngineImpl.getTaskService();
Set<String> affectedTaskIds =
List<String> affectedTaskIds =
taskService.findTasksIdsAffectedByClassificationChange(classificationId);
scheduleTaskRefreshJobs(affectedTaskIds);
if (!affectedTaskIds.isEmpty()) {
scheduleTaskRefreshJobs(affectedTaskIds);
}
LOGGER.info("ClassificationChangedJob ended successfully.");
} catch (Exception e) {
throw new TaskanaException("Error while processing ClassificationChangedJob.", e);
}
}
private void scheduleTaskRefreshJobs(Set<String> affectedTaskIds) {
private void scheduleTaskRefreshJobs(List<String> affectedTaskIds) {
int batchSize = taskanaEngineImpl.getConfiguration().getMaxNumberOfUpdatesPerTransaction();
List<List<String>> affectedTaskBatches = partition(affectedTaskIds, batchSize);
LOGGER.debug(

View File

@ -2,16 +2,22 @@ package pro.taskana.common.internal.jobs;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.Principal;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.List;
import javax.security.auth.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pro.taskana.common.api.ScheduledJob;
import pro.taskana.common.api.TaskanaEngine;
import pro.taskana.common.api.TaskanaRole;
import pro.taskana.common.api.exceptions.SystemException;
import pro.taskana.common.internal.JobServiceImpl;
import pro.taskana.common.internal.TaskanaEngineImpl;
import pro.taskana.common.internal.security.UserPrincipal;
import pro.taskana.common.internal.transaction.TaskanaTransactionProvider;
import pro.taskana.task.internal.TaskServiceImpl;
@ -101,6 +107,35 @@ public class JobRunner {
private void runScheduledJob(ScheduledJob scheduledJob) {
LOGGER.debug("entry to runScheduledJob(job = {})", scheduledJob);
if (taskanaEngine.isUserInRole(TaskanaRole.ADMIN)) {
// we run already as admin
runScheduledJobImpl(scheduledJob);
} else {
// we must establish admin context
try {
Subject.doAs(
getAdminSubject(),
new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws Exception {
try {
runScheduledJobImpl(scheduledJob);
} catch (Exception e) {
throw new SystemException(
String.format("could not run Job %s.", scheduledJob), e);
}
return null;
}
});
} catch (PrivilegedActionException e) {
LOGGER.warn("Attempt to run job {} failed.", scheduledJob, e);
}
}
LOGGER.debug("exit from runScheduledJob");
}
private void runScheduledJobImpl(ScheduledJob scheduledJob) {
try {
TaskanaJob job =
AbstractTaskanaJob.createFromScheduledJob(taskanaEngine, txProvider, scheduledJob);
@ -114,6 +149,24 @@ public class JobRunner {
+ e.getMessage(),
e);
}
LOGGER.debug("exit from runScheduledJob");
}
private Subject getAdminSubject() {
Subject subject = new Subject();
List<Principal> principalList = new ArrayList<>();
try {
principalList.add(
new UserPrincipal(
taskanaEngine
.getConfiguration()
.getRoleMap()
.get(TaskanaRole.ADMIN)
.iterator()
.next()));
} catch (Exception t) {
LOGGER.warn("Could not determine a configured admin user.", t);
}
subject.getPrincipals().addAll(principalList);
return subject;
}
}

View File

@ -23,13 +23,18 @@ public class TaskRefreshJob extends AbstractTaskanaJob {
public static final String ARG_TASK_IDS = "taskIds";
private static final Logger LOGGER = LoggerFactory.getLogger(TaskRefreshJob.class);
private List<String> affectedTaskIds;
private boolean priorityChanged;
private boolean serviceLevelChanged;
public TaskRefreshJob(
TaskanaEngine engine, TaskanaTransactionProvider<Object> txProvider, ScheduledJob job) {
super(engine, txProvider, job);
Map<String, String> args = job.getArguments();
String taskIdsString = args.get(ARG_TASK_IDS);
String taskIdsString = args.get(ClassificationChangedJob.TASK_IDS);
affectedTaskIds = Arrays.asList(taskIdsString.split(","));
priorityChanged = Boolean.parseBoolean(args.get(ClassificationChangedJob.PRIORITY_CHANGED));
serviceLevelChanged =
Boolean.parseBoolean(args.get(ClassificationChangedJob.SERVICE_LEVEL_CHANGED));
}
@Override
@ -37,14 +42,8 @@ public class TaskRefreshJob extends AbstractTaskanaJob {
LOGGER.info("Running TaskRefreshJob for {} tasks", affectedTaskIds.size());
try {
TaskServiceImpl taskService = (TaskServiceImpl) taskanaEngineImpl.getTaskService();
for (String taskId : affectedTaskIds) {
try {
taskService.refreshPriorityAndDueDateOnClassificationUpdate(taskId);
} catch (Exception e) {
LOGGER.warn(
"Task {} could not be refreshed because of exception: {}", taskId, e.getMessage());
}
}
taskService.refreshPriorityAndDueDatesOfTasksOnClassificationUpdate(
affectedTaskIds, serviceLevelChanged, priorityChanged);
LOGGER.info("TaskRefreshJob ended successfully.");
} catch (Exception e) {
throw new TaskanaException("Error while processing TaskRefreshJob.", e);

View File

@ -1,28 +1,61 @@
package pro.taskana.common.internal.util;
public class Pair<L, R> {
import java.util.Objects;
private final L left;
public final class Pair<L, R> {
private final R right;
private L left;
private R right;
public Pair(L left, R right) {
this.left = left;
this.right = right;
}
public Pair() {}
public L getLeft() {
return left;
}
public void setLeft(L left) {
this.left = left;
}
public R getRight() {
return right;
}
public void setRight(R right) {
this.right = right;
}
public static <L, R> Pair<L, R> of(L left, R right) {
return new Pair<>(left, right);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Pair<?, ?> other = (Pair<?, ?>) obj;
return Objects.equals(left, other.left)
&& Objects.equals(right, other.right);
}
@Override
public int hashCode() {
return Objects.hash(left, right);
}
@Override
public String toString() {
return "Pair [left=" + left + ", right=" + right + "]";

View File

@ -1,5 +1,6 @@
package pro.taskana.task.internal;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Delete;
@ -12,6 +13,7 @@ import org.apache.ibatis.annotations.Update;
import org.apache.ibatis.type.ClobTypeHandler;
import pro.taskana.common.internal.persistence.MapTypeHandler;
import pro.taskana.common.internal.util.Pair;
import pro.taskana.task.internal.models.AttachmentImpl;
import pro.taskana.task.internal.models.AttachmentSummaryImpl;
@ -85,7 +87,8 @@ public interface AttachmentMapper {
@Result(property = "channel", column = "CHANNEL"),
@Result(property = "received", column = "RECEIVED")
})
List<AttachmentSummaryImpl> findAttachmentSummariesByTaskIds(@Param("taskIds") List<String> taskIds);
List<AttachmentSummaryImpl> findAttachmentSummariesByTaskIds(
@Param("taskIds") List<String> taskIds);
@Delete("DELETE FROM ATTACHMENT WHERE ID=#{attachmentId}")
void deleteAttachment(@Param("attachmentId") String attachmentId);
@ -113,10 +116,18 @@ public interface AttachmentMapper {
String getCustomAttributesAsString(@Param("attachmentId") String attachmentId);
@Select(
"<script> SELECT DISTINCT TASK_ID FROM ATTACHMENT WHERE CLASSIFICATION_ID = #{classificationId} "
"<script> SELECT DISTINCT t.ID, t.PLANNED FROM TASK t "
+ "LEFT JOIN ATTACHMENT AS a on a.TASK_ID = t.ID"
+ " WHERE a.CLASSIFICATION_ID = #{classificationId} "
+ "<if test=\"_databaseId == 'db2'\">with UR </if> "
+ "</script>")
@Results(value = {@Result(property = "taskId", column = "TASK_ID")})
List<String> findTaskIdsAffectedByClassificationChange(
@Results(
value = {
@Result(property = "left", column = "ID"),
@Result(property = "right", column = "PLANNED")
// , javaType = Instant.class,
// typeHandler = InstantTypeHandler.class)
})
List<Pair<String, Instant>> findTaskIdsAndPlannedAffectedByClassificationChange(
@Param("classificationId") String classificationId);
}

View File

@ -9,6 +9,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import org.slf4j.Logger;
@ -50,7 +51,7 @@ class ServiceLevelHandler {
DaysToWorkingDaysConverter.setGermanPublicHolidaysEnabled(
taskanaEngine.getEngine().getConfiguration().isGermanPublicHolidaysEnabled());
try {
this.converter = DaysToWorkingDaysConverter.initialize();
converter = DaysToWorkingDaysConverter.initialize();
} catch (InvalidArgumentException e) {
LOGGER.error(ERROR_CANNOT_INITIALIZE_DAYS_TO_WORKING_DAYS_CONVERTER);
throw new SystemException(
@ -245,6 +246,9 @@ class ServiceLevelHandler {
private TaskImpl updatePlannedDueOnTaskUpdate(
TaskImpl newTaskImpl, TaskImpl oldTaskImpl, DurationPrioHolder durationPrioHolder)
throws InvalidArgumentException {
if (newTaskImpl.getPlanned() == null && newTaskImpl.getDue() == null) {
newTaskImpl.setPlanned(oldTaskImpl.getPlanned());
}
// case 1: no change of planned / due, but potentially change of an attachment or classification
if (oldTaskImpl.getDue().equals(newTaskImpl.getDue())
&& oldTaskImpl.getPlanned().equals(newTaskImpl.getPlanned())) {

View File

@ -13,7 +13,9 @@ import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import pro.taskana.common.internal.CustomPropertySelector;
import pro.taskana.common.internal.persistence.InstantTypeHandler;
import pro.taskana.common.internal.persistence.MapTypeHandler;
import pro.taskana.common.internal.util.Pair;
import pro.taskana.task.api.CallbackState;
import pro.taskana.task.internal.models.MinimalTaskSummary;
import pro.taskana.task.internal.models.TaskImpl;
@ -232,14 +234,34 @@ public interface TaskMapper {
long updateTaskDueDates(
@Param("taskIds") List<String> taskIds, @Param("referenceTask") TaskImpl referenceTask);
@Update(
"<script>"
+ "<if test='taskIds != null'> "
+ "UPDATE TASK SET MODIFIED = #{referenceTask.modified}, "
+ "PRIORITY = #{referenceTask.priority} "
+ "WHERE ID IN(<foreach item='item' collection='taskIds' separator=',' >#{item}</foreach>) "
+ "</if> "
+ "</script>")
long updatePriorityOfTasks(
@Param("taskIds") List<String> taskIds, @Param("referenceTask") TaskImpl referenceTask);
@Select(
"<script>SELECT ID, STATE FROM TASK "
"<script>SELECT ID, PLANNED, STATE FROM TASK "
+ "WHERE ID IN(<foreach item='item' collection='taskIds' separator=',' >#{item}</foreach>) "
+ "AND STATE IN ( 'READY','CLAIMED') "
+ "<if test=\"_databaseId == 'db2'\">with UR </if> "
+ "</script>")
@Results(value = {@Result(property = "taskId", column = "ID")})
List<String> filterTaskIdsForNotCompleted(@Param("taskIds") List<String> taskIds);
@Results(
value = {
@Result(property = "left", column = "ID"),
@Result(
property = "right",
column = "PLANNED",
javaType = Instant.class,
typeHandler = InstantTypeHandler.class)
})
List<Pair<String, Instant>> filterTaskIdsForReadyAndClaimed(
@Param("taskIds") List<String> taskIds);
@Select(
"<script> "

View File

@ -3,6 +3,7 @@ package pro.taskana.task.internal;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@ -748,75 +749,78 @@ public class TaskServiceImpl implements TaskService {
}
}
public Set<String> findTasksIdsAffectedByClassificationChange(String classificationId) {
public List<String> findTasksIdsAffectedByClassificationChange(String classificationId) {
LOGGER.debug(
"entry to findTasksIdsAffectedByClassificationChange(classificationId = {})",
classificationId);
// tasks directly affected
List<TaskSummary> tasks =
List<TaskSummary> tasksAffectedDirectly =
createTaskQuery()
.classificationIdIn(classificationId)
.stateIn(TaskState.READY, TaskState.CLAIMED)
.list();
// tasks indirectly affected via attachments
List<String> taskIdsFromAttachments =
attachmentMapper.findTaskIdsAffectedByClassificationChange(classificationId);
List<Pair<String, Instant>> affectedPairs =
tasksAffectedDirectly.stream()
.map(t -> new Pair<String, Instant>(t.getId(), t.getPlanned()))
.collect(Collectors.toList());
// tasks indirectly affected via attachments
List<Pair<String, Instant>> taskIdsAndPlannedFromAttachments =
attachmentMapper.findTaskIdsAndPlannedAffectedByClassificationChange(classificationId);
List<String> filteredTaskIdsFromAttachments =
List<String> taskIdsFromAttachments =
taskIdsAndPlannedFromAttachments.stream().map(Pair::getLeft).collect(Collectors.toList());
List<Pair<String, Instant>> filteredTaskIdsAndPlannedFromAttachments =
taskIdsFromAttachments.isEmpty()
? new ArrayList<>()
: taskMapper.filterTaskIdsForNotCompleted(taskIdsFromAttachments);
: taskMapper.filterTaskIdsForReadyAndClaimed(taskIdsFromAttachments);
affectedPairs.addAll(filteredTaskIdsAndPlannedFromAttachments);
// sort all affected tasks according to the planned instant
List<String> affectedTaskIds =
affectedPairs.stream()
.sorted(Comparator.comparing(Pair::getRight))
.distinct()
.map(Pair::getLeft)
.collect(Collectors.toList());
Set<String> affectedTaskIds = new HashSet<>(filteredTaskIdsFromAttachments);
for (TaskSummary task : tasks) {
affectedTaskIds.add(task.getId());
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(
"the following tasks are affected by the update of classification {} : {}",
classificationId,
LoggerUtils.setToString(affectedTaskIds));
LoggerUtils.listToString(affectedTaskIds));
}
LOGGER.debug("exit from findTasksIdsAffectedByClassificationChange(). ");
return affectedTaskIds;
}
public void refreshPriorityAndDueDateOnClassificationUpdate(String taskId)
throws ClassificationNotFoundException {
LOGGER.debug("entry to refreshPriorityAndDueDate(taskId = {})", taskId);
TaskImpl task;
BulkOperationResults<String, Exception> bulkLog = new BulkOperationResults<>();
public void refreshPriorityAndDueDatesOfTasksOnClassificationUpdate(
List<String> taskIds, boolean serviceLevelChanged, boolean priorityChanged) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(
"entry to refreshPriorityAndDueDateOfTasks(tasks = {})",
LoggerUtils.listToString(taskIds));
}
Pair<List<MinimalTaskSummary>, BulkLog> resultsPair = getMinimalTaskSummaries(taskIds);
List<MinimalTaskSummary> tasks = resultsPair.getLeft();
try {
taskanaEngine.openConnection();
if (taskId == null || taskId.isEmpty()) {
return;
Set<String> adminAccessIds =
taskanaEngine.getEngine().getConfiguration().getRoleMap().get(TaskanaRole.ADMIN);
if (adminAccessIds.contains(CurrentUserContext.getUserid())) {
serviceLevelHandler.refreshPriorityAndDueDatesOfTasks(
tasks, serviceLevelChanged, priorityChanged);
} else {
taskanaEngine.runAsAdmin(
() -> {
serviceLevelHandler.refreshPriorityAndDueDatesOfTasks(
tasks, serviceLevelChanged, priorityChanged);
return null;
});
}
task = taskMapper.findById(taskId);
List<AttachmentImpl> attachmentImpls = attachmentMapper.findAttachmentsByTaskId(task.getId());
if (attachmentImpls == null) {
attachmentImpls = new ArrayList<>();
}
List<Attachment> attachments =
attachmentHandler.augmentAttachmentsByClassification(attachmentImpls, bulkLog);
task.setAttachments(attachments);
ClassificationSummary classificationSummary =
classificationService
.getClassification(task.getClassificationKey(), task.getDomain())
.asSummary();
task.setClassificationSummary(classificationSummary);
task = serviceLevelHandler.updatePrioPlannedDueOfTask(task, null, true);
task.setModified(Instant.now());
taskMapper.update(task);
} catch (InvalidArgumentException e) {
LOGGER.error("Internal System error. This situation should not happen. Caught exceptio ", e);
} finally {
LOGGER.debug("exit from refreshPriorityAndDueDateOfTasks");
taskanaEngine.returnConnection();
LOGGER.debug("exit from refreshPriorityAndDueDate(). ");
}
}
@ -1217,44 +1221,45 @@ public class TaskServiceImpl implements TaskService {
private void standardSettings(TaskImpl task, Classification classification)
throws InvalidArgumentException {
TaskImpl task1 = task;
LOGGER.debug("entry to standardSettings()");
final Instant now = Instant.now();
task.setId(IdGenerator.generateWithPrefix(ID_PREFIX_TASK));
if (task.getExternalId() == null) {
task.setExternalId(IdGenerator.generateWithPrefix(ID_PREFIX_EXT_TASK_ID));
task1.setId(IdGenerator.generateWithPrefix(ID_PREFIX_TASK));
if (task1.getExternalId() == null) {
task1.setExternalId(IdGenerator.generateWithPrefix(ID_PREFIX_EXT_TASK_ID));
}
task.setState(TaskState.READY);
task.setCreated(now);
task.setModified(now);
task.setRead(false);
task.setTransferred(false);
task1.setState(TaskState.READY);
task1.setCreated(now);
task1.setModified(now);
task1.setRead(false);
task1.setTransferred(false);
String creator = CurrentUserContext.getUserid();
if (taskanaEngine.getEngine().getConfiguration().isSecurityEnabled() && creator == null) {
throw new SystemException(
"TaskanaSecurity is enabled, but the current UserId is NULL while creating a Task.");
}
task.setCreator(creator);
task1.setCreator(creator);
// if no business process id is provided, a unique id is created.
if (task.getBusinessProcessId() == null) {
task.setBusinessProcessId(IdGenerator.generateWithPrefix(ID_PREFIX_BUSINESS_PROCESS));
if (task1.getBusinessProcessId() == null) {
task1.setBusinessProcessId(IdGenerator.generateWithPrefix(ID_PREFIX_BUSINESS_PROCESS));
}
// null in case of manual tasks
if (task.getPlanned() == null && (classification == null || task.getDue() == null)) {
task.setPlanned(now);
if (task1.getPlanned() == null && (classification == null || task1.getDue() == null)) {
task1.setPlanned(now);
}
if (classification != null) {
task = serviceLevelHandler.updatePrioPlannedDueOfTask(task, null, false);
task1 = serviceLevelHandler.updatePrioPlannedDueOfTask(task1, null, false);
}
if (task.getName() == null && classification != null) {
task.setName(classification.getName());
if (task1.getName() == null && classification != null) {
task1.setName(classification.getName());
}
if (task.getDescription() == null && classification != null) {
task.setDescription(classification.getDescription());
if (task1.getDescription() == null && classification != null) {
task1.setDescription(classification.getDescription());
}
try {
attachmentHandler.insertNewAttachmentsOnTaskCreation(task);
@ -1276,7 +1281,6 @@ public class TaskServiceImpl implements TaskService {
} catch (Exception e) {
LOGGER.warn(
"Attempted to determine callback state from {} and caught exception", value, e);
throw new InvalidArgumentException(
String.format("Attempted to set callback state for task %s.", task.getId()), e);
}
@ -1654,20 +1658,20 @@ public class TaskServiceImpl implements TaskService {
updateClassificationSummary(newTaskImpl, oldTaskImpl);
newTaskImpl = serviceLevelHandler.updatePrioPlannedDueOfTask(newTaskImpl, oldTaskImpl, false);
TaskImpl newTaskImpl1 =
serviceLevelHandler.updatePrioPlannedDueOfTask(newTaskImpl, oldTaskImpl, false);
// if no business process id is provided, use the id of the old task.
if (newTaskImpl.getBusinessProcessId() == null) {
newTaskImpl.setBusinessProcessId(oldTaskImpl.getBusinessProcessId());
if (newTaskImpl1.getBusinessProcessId() == null) {
newTaskImpl1.setBusinessProcessId(oldTaskImpl.getBusinessProcessId());
}
// owner can only be changed if task is in state ready
boolean isOwnerChanged = !Objects.equals(newTaskImpl.getOwner(), oldTaskImpl.getOwner());
boolean isOwnerChanged = !Objects.equals(newTaskImpl1.getOwner(), oldTaskImpl.getOwner());
if (isOwnerChanged && oldTaskImpl.getState() != TaskState.READY) {
throw new InvalidStateException(
String.format(TASK_WITH_ID_IS_NOT_READY, oldTaskImpl.getId(), oldTaskImpl.getState()));
}
}
private void updateClassificationSummary(TaskImpl newTaskImpl, TaskImpl oldTaskImpl)

View File

@ -225,14 +225,7 @@ public class UpdateClassificationAccTest extends AbstractAccTest {
classification.setPriority(1000);
classification.setServiceLevel("P15D");
classificationService.updateClassification(classification);
Thread.sleep(10);
JobRunner runner = new JobRunner(taskanaEngine);
// need to run jobs twice, since the first job creates a second one.
runner.runJobs();
Thread.sleep(10); // otherwise the next runJobs call intermittently doesn't find the Job created
// by the previous step (it searches with DueDate < CurrentTime)
runner.runJobs();
updateClassificationAndRunAssociatedJobs(classification);
// Get and check the new value
Classification updatedClassification =
classificationService.getClassification("CLI:100000000000000000000000000000000003");
@ -241,7 +234,7 @@ public class UpdateClassificationAccTest extends AbstractAccTest {
assertFalse(modifiedBefore.isAfter(updatedClassification.getModified()));
// TODO - resume old behaviour after attachment query is possible.
TaskService taskService = taskanaEngine.getTaskService();
DaysToWorkingDaysConverter.setGermanPublicHolidaysEnabled(true);
DaysToWorkingDaysConverter converter = DaysToWorkingDaysConverter.initialize(Instant.now());
List<String> tasksWithP1D =
@ -252,15 +245,18 @@ public class UpdateClassificationAccTest extends AbstractAccTest {
"TKI:000000000000000000000000000000000000",
"TKI:000000000000000000000000000000000011",
"TKI:000000000000000000000000000000000053"));
validateNewTaskProperties(before, tasksWithP1D, taskService, converter, 1);
validateTaskPropertiesAfterClassificationChange(
before, tasksWithP1D, taskService, converter, 1, 1000);
List<String> tasksWithP8D =
new ArrayList<>(Arrays.asList("TKI:000000000000000000000000000000000008"));
validateNewTaskProperties(before, tasksWithP8D, taskService, converter, 8);
validateTaskPropertiesAfterClassificationChange(
before, tasksWithP8D, taskService, converter, 8, 1000);
List<String> tasksWithP14D =
new ArrayList<>(Arrays.asList("TKI:000000000000000000000000000000000010"));
validateNewTaskProperties(before, tasksWithP14D, taskService, converter, 14);
validateTaskPropertiesAfterClassificationChange(
before, tasksWithP14D, taskService, converter, 14, 1000);
List<String> tasksWithP15D =
new ArrayList<>(
@ -299,7 +295,8 @@ public class UpdateClassificationAccTest extends AbstractAccTest {
"TKI:000000000000000000000000000000000101",
"TKI:000000000000000000000000000000000102",
"TKI:000000000000000000000000000000000103"));
validateNewTaskProperties(before, tasksWithP15D, taskService, converter, 15);
validateTaskPropertiesAfterClassificationChange(
before, tasksWithP15D, taskService, converter, 15, 1000);
}
@WithAccessId(
@ -333,29 +330,263 @@ public class UpdateClassificationAccTest extends AbstractAccTest {
classificationService.updateClassification(created);
}
private void validateNewTaskProperties(
@WithAccessId(
userName = "dummy",
groupNames = {"admin"})
@Test
public void testUpdateClassificationChangePriority()
throws ClassificationNotFoundException, NotAuthorizedException, ConcurrencyException,
InterruptedException, TaskNotFoundException, InvalidArgumentException {
final Instant before = Instant.now();
Classification classification =
classificationService.getClassification("CLI:100000000000000000000000000000000003");
final Instant modifiedBefore = classification.getModified();
classification.setPriority(99);
classification.setServiceLevel("P1D");
updateClassificationAndRunAssociatedJobs(classification);
// Get and check the new value
Classification updatedClassification =
classificationService.getClassification("CLI:100000000000000000000000000000000003");
assertNotNull(updatedClassification);
assertFalse(modifiedBefore.isAfter(updatedClassification.getModified()));
// TODO - resume old behaviour after attachment query is possible.
TaskService taskService = taskanaEngine.getTaskService();
DaysToWorkingDaysConverter.setGermanPublicHolidaysEnabled(true);
DaysToWorkingDaysConverter converter = DaysToWorkingDaysConverter.initialize(Instant.now());
List<String> tasksWithPrio99 =
new ArrayList<>(
Arrays.asList(
"TKI:000000000000000000000000000000000003",
"TKI:000000000000000000000000000000000004",
"TKI:000000000000000000000000000000000005",
"TKI:000000000000000000000000000000000006",
"TKI:000000000000000000000000000000000007",
"TKI:000000000000000000000000000000000009",
"TKI:000000000000000000000000000000000012",
"TKI:000000000000000000000000000000000013",
"TKI:000000000000000000000000000000000014",
"TKI:000000000000000000000000000000000015",
"TKI:000000000000000000000000000000000016",
"TKI:000000000000000000000000000000000017",
"TKI:000000000000000000000000000000000018",
"TKI:000000000000000000000000000000000019",
"TKI:000000000000000000000000000000000020",
"TKI:000000000000000000000000000000000021",
"TKI:000000000000000000000000000000000022",
"TKI:000000000000000000000000000000000023",
"TKI:000000000000000000000000000000000024",
"TKI:000000000000000000000000000000000025",
"TKI:000000000000000000000000000000000026",
"TKI:000000000000000000000000000000000027",
"TKI:000000000000000000000000000000000028",
"TKI:000000000000000000000000000000000029",
"TKI:000000000000000000000000000000000030",
"TKI:000000000000000000000000000000000031",
"TKI:000000000000000000000000000000000032",
"TKI:000000000000000000000000000000000033",
"TKI:000000000000000000000000000000000034",
"TKI:000000000000000000000000000000000035",
"TKI:000000000000000000000000000000000100",
"TKI:000000000000000000000000000000000101",
"TKI:000000000000000000000000000000000102",
"TKI:000000000000000000000000000000000103",
"TKI:200000000000000000000000000000000007",
"TKI:000000000000000000000000000000000000",
"TKI:000000000000000000000000000000000052",
"TKI:000000000000000000000000000000000053",
"TKI:000000000000000000000000000000000054",
"TKI:000000000000000000000000000000000008",
"TKI:000000000000000000000000000000000009",
"TKI:000000000000000000000000000000000010"));
validateTaskPropertiesAfterClassificationChange(
before, tasksWithPrio99, taskService, converter, 1, 99);
List<String> tasksWithPrio101 =
new ArrayList<>(Arrays.asList("TKI:000000000000000000000000000000000011"));
validateTaskPropertiesAfterClassificationChange(
before, tasksWithPrio101, taskService, converter, 1, 101);
updatedClassification.setPriority(7);
updateClassificationAndRunAssociatedJobs(updatedClassification);
List<String> tasksWithPrio7 =
new ArrayList<>(
Arrays.asList(
"TKI:000000000000000000000000000000000003",
"TKI:000000000000000000000000000000000004",
"TKI:000000000000000000000000000000000005",
"TKI:000000000000000000000000000000000006",
"TKI:000000000000000000000000000000000007",
"TKI:000000000000000000000000000000000009",
"TKI:000000000000000000000000000000000010",
"TKI:000000000000000000000000000000000012",
"TKI:000000000000000000000000000000000013",
"TKI:000000000000000000000000000000000014",
"TKI:000000000000000000000000000000000015",
"TKI:000000000000000000000000000000000016",
"TKI:000000000000000000000000000000000017",
"TKI:000000000000000000000000000000000018",
"TKI:000000000000000000000000000000000019",
"TKI:000000000000000000000000000000000020",
"TKI:000000000000000000000000000000000021",
"TKI:000000000000000000000000000000000022",
"TKI:000000000000000000000000000000000023",
"TKI:000000000000000000000000000000000024",
"TKI:000000000000000000000000000000000025",
"TKI:000000000000000000000000000000000026",
"TKI:000000000000000000000000000000000027",
"TKI:000000000000000000000000000000000028",
"TKI:000000000000000000000000000000000029",
"TKI:000000000000000000000000000000000030",
"TKI:000000000000000000000000000000000031",
"TKI:000000000000000000000000000000000032",
"TKI:000000000000000000000000000000000033",
"TKI:000000000000000000000000000000000034",
"TKI:000000000000000000000000000000000035",
"TKI:000000000000000000000000000000000100",
"TKI:000000000000000000000000000000000101",
"TKI:000000000000000000000000000000000102",
"TKI:000000000000000000000000000000000103",
"TKI:000000000000000000000000000000000000",
"TKI:000000000000000000000000000000000052",
"TKI:000000000000000000000000000000000053",
"TKI:000000000000000000000000000000000054",
"TKI:000000000000000000000000000000000055",
"TKI:200000000000000000000000000000000007"));
validateTaskPropertiesAfterClassificationChange(
before, tasksWithPrio7, taskService, converter, 1, 7);
List<String> tasksWithPrio9 =
new ArrayList<>(Arrays.asList("TKI:000000000000000000000000000000000008"));
validateTaskPropertiesAfterClassificationChange(
before, tasksWithPrio9, taskService, converter, 1, 9);
tasksWithPrio101 = new ArrayList<>(Arrays.asList("TKI:000000000000000000000000000000000011"));
validateTaskPropertiesAfterClassificationChange(
before, tasksWithPrio101, taskService, converter, 1, 101);
}
@WithAccessId(
userName = "dummy",
groupNames = {"admin"})
@Test
public void testUpdateClassificationChangeServiceLevel()
throws ClassificationNotFoundException, NotAuthorizedException, ConcurrencyException,
InterruptedException, TaskNotFoundException, InvalidArgumentException {
final Instant before = Instant.now();
Classification classification =
classificationService.getClassification("CLI:100000000000000000000000000000000003");
final Instant modifiedBefore = classification.getModified();
classification.setPriority(555);
classification.setServiceLevel("P12D");
updateClassificationAndRunAssociatedJobs(classification);
// Get and check the new value
Classification updatedClassification =
classificationService.getClassification("CLI:100000000000000000000000000000000003");
assertNotNull(updatedClassification);
assertFalse(modifiedBefore.isAfter(updatedClassification.getModified()));
// TODO - resume old behaviour after attachment query is possible.
TaskService taskService = taskanaEngine.getTaskService();
DaysToWorkingDaysConverter.setGermanPublicHolidaysEnabled(true);
DaysToWorkingDaysConverter converter = DaysToWorkingDaysConverter.initialize(Instant.now());
List<String> tasksWithPD12 =
new ArrayList<>(
Arrays.asList(
"TKI:000000000000000000000000000000000003",
"TKI:000000000000000000000000000000000004",
"TKI:000000000000000000000000000000000005",
"TKI:000000000000000000000000000000000006",
"TKI:000000000000000000000000000000000007",
"TKI:000000000000000000000000000000000009",
"TKI:000000000000000000000000000000000010",
"TKI:000000000000000000000000000000000012",
"TKI:000000000000000000000000000000000013",
"TKI:000000000000000000000000000000000014",
"TKI:000000000000000000000000000000000015",
"TKI:000000000000000000000000000000000016",
"TKI:000000000000000000000000000000000017",
"TKI:000000000000000000000000000000000018",
"TKI:000000000000000000000000000000000019",
"TKI:000000000000000000000000000000000020",
"TKI:000000000000000000000000000000000021",
"TKI:000000000000000000000000000000000022",
"TKI:000000000000000000000000000000000023",
"TKI:000000000000000000000000000000000024",
"TKI:000000000000000000000000000000000025",
"TKI:000000000000000000000000000000000026",
"TKI:000000000000000000000000000000000027",
"TKI:000000000000000000000000000000000028",
"TKI:000000000000000000000000000000000029",
"TKI:000000000000000000000000000000000030",
"TKI:000000000000000000000000000000000031",
"TKI:000000000000000000000000000000000032",
"TKI:000000000000000000000000000000000033",
"TKI:000000000000000000000000000000000034",
"TKI:000000000000000000000000000000000035",
"TKI:000000000000000000000000000000000100",
"TKI:000000000000000000000000000000000101",
"TKI:000000000000000000000000000000000102",
"TKI:000000000000000000000000000000000103",
"TKI:200000000000000000000000000000000007"));
validateTaskPropertiesAfterClassificationChange(
before, tasksWithPD12, taskService, converter, 12, 555);
List<String> tasksWithPD8 =
new ArrayList<>(Arrays.asList("TKI:000000000000000000000000000000000008"));
validateTaskPropertiesAfterClassificationChange(
before, tasksWithPD8, taskService, converter, 8, 555);
List<String> tasksWithPD1 =
new ArrayList<>(
Arrays.asList(
"TKI:000000000000000000000000000000000000",
"TKI:000000000000000000000000000000000011",
"TKI:000000000000000000000000000000000052",
"TKI:000000000000000000000000000000000053",
"TKI:000000000000000000000000000000000054",
"TKI:000000000000000000000000000000000055"));
validateTaskPropertiesAfterClassificationChange(
before, tasksWithPD1, taskService, converter, 1, 555);
}
private void updateClassificationAndRunAssociatedJobs(Classification classification)
throws ClassificationNotFoundException, NotAuthorizedException, ConcurrencyException,
InvalidArgumentException, InterruptedException {
classificationService.updateClassification(classification);
Thread.sleep(10);
// run the ClassificationChangedJob
JobRunner runner = new JobRunner(taskanaEngine);
// run the TaskRefreshJob that was scheduled by the ClassificationChangedJob.
runner.runJobs();
Thread.sleep(10); // otherwise the next runJobs call intermittently doesn't find the Job created
// by the previous step (it searches with DueDate < CurrentTime)
runner.runJobs();
}
private void validateTaskPropertiesAfterClassificationChange(
Instant before,
List<String> tasksWithP15D,
List<String> tasksUpdated,
TaskService taskService,
DaysToWorkingDaysConverter converter,
int serviceLevel)
throws TaskNotFoundException, NotAuthorizedException {
for (String taskId : tasksWithP15D) {
int serviceLevel,
int priority)
throws TaskNotFoundException, NotAuthorizedException, InvalidArgumentException {
for (String taskId : tasksUpdated) {
Task task = taskService.getTask(taskId);
assertTrue(
task.getModified().isAfter(before), "Task " + task.getId() + " has not been refreshed.");
assertEquals(1000, task.getPriority());
long calendarDays = converter.convertWorkingDaysToDays(task.getPlanned(), serviceLevel);
String msg =
"Task: "
+ taskId
+ ": Due Date "
+ task.getDue()
+ " does not match planned "
+ task.getPlanned()
+ " + calendar days "
+ calendarDays;
String.format(
"Task: %s and Due Date: %s do not match planned %s. Calendar days : %s.",
taskId, task.getDue(), task.getPlanned(), calendarDays);
assertEquals(task.getDue(), task.getPlanned().plus(Duration.ofDays(calendarDays)), msg);
}
}

View File

@ -68,7 +68,6 @@ public class UpdateObjectsUseUtcTimeStampsAccTest extends AbstractAccTest {
TaskImpl ti = (TaskImpl) task;
ti.setCompleted(now.plus(Duration.ofHours(27)));
TimeZone originalZone = TimeZone.getDefault();
Task updatedTask = taskService.updateTask(task);
TimeZone.setDefault(TimeZone.getTimeZone("EST"));

View File

@ -220,7 +220,7 @@ public class ServiceLevelPriorityAccTest extends AbstractAccTest {
String tkId1 = "TKI:000000000000000000000000000000000008";
String tkId2 = "TKI:000000000000000000000000000000000009";
String tkId3 = "TKI:000000000000000000000000000000000008";
String tkId4 = "TKI:000000000000000000000000000000000010";
String tkId4 = "TKI:000000000000000000000000000000000010"; // all three have P13D
List<String> taskIds = Arrays.asList(tkId1, tkId2, tkId3, tkId4);
Instant planned = getInstant("2020-05-03T07:00:00");