TSK-1139 Bulk update of Planned timestamp
This commit is contained in:
parent
ec48d5ed88
commit
02a6df6f73
|
@ -1,5 +1,6 @@
|
|||
package pro.taskana.task.api;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -381,8 +382,20 @@ public interface TaskService {
|
|||
*
|
||||
* @param owner the new owner of the tasks
|
||||
* @param taskIds the IDs of the tasks on which the owner is to be set.
|
||||
* @return the result of the operations with Id and Exception for each failed task deletion.
|
||||
* @return the result of the operations with Id and Exception for each failed task update.
|
||||
*/
|
||||
BulkOperationResults<String, TaskanaException> setOwnerOfTasks(
|
||||
String owner, List<String> taskIds);
|
||||
|
||||
/**
|
||||
* Sets the planned property on a list of tasks. Only tasks in state READY and CLAIMED will be
|
||||
* affected by this method. On each task, the corresponding due date is set according to the due
|
||||
* dates in the classification() of the task and the task's attachments.
|
||||
*
|
||||
* @param planned the new 'PLANNED" property of the tasks
|
||||
* @param taskIds the IDs of the tasks on which the new planned property is to be set.
|
||||
* @return the result of the operations with Id and Exception for each failed task update.
|
||||
*/
|
||||
BulkOperationResults<String, TaskanaException> setPlannedPropertyOfTasks(
|
||||
Instant planned, List<String> taskIds);
|
||||
}
|
||||
|
|
|
@ -54,40 +54,18 @@ public interface AttachmentMapper {
|
|||
})
|
||||
List<AttachmentImpl> findAttachmentsByTaskId(@Param("taskId") String taskId);
|
||||
|
||||
@Select(
|
||||
"<script> SELECT ID, TASK_ID, CREATED, MODIFIED, CLASSIFICATION_KEY, CLASSIFICATION_ID, REF_COMPANY, REF_SYSTEM, REF_INSTANCE, REF_TYPE, REF_VALUE, CHANNEL, RECEIVED, CUSTOM_ATTRIBUTES "
|
||||
+ "FROM ATTACHMENT "
|
||||
+ "WHERE ID = #{attachmentId} "
|
||||
+ "<if test=\"_databaseId == 'db2'\">with UR </if> "
|
||||
+ "</script>")
|
||||
@Results(
|
||||
value = {
|
||||
@Result(property = "id", column = "ID"),
|
||||
@Result(property = "taskId", column = "TASK_ID"),
|
||||
@Result(property = "created", column = "CREATED"),
|
||||
@Result(property = "modified", column = "MODIFIED"),
|
||||
@Result(property = "classificationSummaryImpl.key", column = "CLASSIFICATION_KEY"),
|
||||
@Result(property = "classificationSummaryImpl.id", column = "CLASSIFICATION_ID"),
|
||||
@Result(property = "objectReference.company", column = "REF_COMPANY"),
|
||||
@Result(property = "objectReference.system", column = "REF_SYSTEM"),
|
||||
@Result(property = "objectReference.systemInstance", column = "REF_INSTANCE"),
|
||||
@Result(property = "objectReference.type", column = "REF_TYPE"),
|
||||
@Result(property = "objectReference.value", column = "REF_VALUE"),
|
||||
@Result(property = "channel", column = "CHANNEL"),
|
||||
@Result(property = "received", column = "RECEIVED"),
|
||||
@Result(
|
||||
property = "customAttributes",
|
||||
column = "CUSTOM_ATTRIBUTES",
|
||||
javaType = Map.class,
|
||||
typeHandler = MapTypeHandler.class)
|
||||
})
|
||||
AttachmentImpl getAttachment(@Param("attachmentId") String attachmentId);
|
||||
|
||||
@Select(
|
||||
"<script>SELECT ID, TASK_ID, CREATED, MODIFIED, CLASSIFICATION_KEY, CLASSIFICATION_ID, REF_COMPANY, REF_SYSTEM, REF_INSTANCE, REF_TYPE, REF_VALUE, CHANNEL, RECEIVED "
|
||||
+ "FROM ATTACHMENT "
|
||||
+ "<where>"
|
||||
+ "<choose>"
|
||||
+ "<when test='taskIds == null'>"
|
||||
+ " 1 = 2 "
|
||||
+ "</when>"
|
||||
+ "<otherwise>"
|
||||
+ "TASK_ID IN (<foreach collection='taskIds' item='item' separator=',' >#{item}</foreach>) "
|
||||
+ "</otherwise>"
|
||||
+ "</choose>"
|
||||
+ "</where>"
|
||||
+ "<if test=\"_databaseId == 'db2'\">with UR </if> "
|
||||
+ "</script>")
|
||||
|
|
|
@ -0,0 +1,346 @@
|
|||
package pro.taskana.task.internal;
|
||||
|
||||
import static java.util.stream.Collectors.groupingBy;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import pro.taskana.classification.api.models.ClassificationSummary;
|
||||
import pro.taskana.common.api.BulkOperationResults;
|
||||
import pro.taskana.common.api.TaskanaRole;
|
||||
import pro.taskana.common.api.exceptions.InvalidArgumentException;
|
||||
import pro.taskana.common.api.exceptions.NotAuthorizedException;
|
||||
import pro.taskana.common.api.exceptions.SystemException;
|
||||
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.DaysToWorkingDaysConverter;
|
||||
import pro.taskana.common.internal.util.Pair;
|
||||
import pro.taskana.task.api.exceptions.TaskNotFoundException;
|
||||
import pro.taskana.task.api.exceptions.UpdateFailedException;
|
||||
import pro.taskana.task.internal.models.AttachmentSummaryImpl;
|
||||
import pro.taskana.task.internal.models.MinimalTaskSummary;
|
||||
import pro.taskana.task.internal.models.TaskImpl;
|
||||
|
||||
/** This class handles service level manipulations. */
|
||||
class ServiceLevelHandler {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ServiceLevelHandler.class);
|
||||
private static final String ERROR_CANNOT_INITIALIZE_DAYS_TO_WORKING_DAYS_CONVERTER =
|
||||
"Internal error. Cannot initialize DaysToWorkingDaysConverter";
|
||||
private static final Duration MAX_DURATION = Duration.ofSeconds(Long.MAX_VALUE, 999_999_999);
|
||||
private final InternalTaskanaEngine taskanaEngine;
|
||||
private final TaskMapper taskMapper;
|
||||
private final AttachmentMapper attachmentMapper;
|
||||
private DaysToWorkingDaysConverter converter;
|
||||
|
||||
public ServiceLevelHandler(
|
||||
InternalTaskanaEngine taskanaEngine,
|
||||
TaskMapper taskMapper,
|
||||
AttachmentMapper attachmentMapper) {
|
||||
super();
|
||||
this.taskanaEngine = taskanaEngine;
|
||||
this.taskMapper = taskMapper;
|
||||
this.attachmentMapper = attachmentMapper;
|
||||
try {
|
||||
this.converter = DaysToWorkingDaysConverter.initialize();
|
||||
} catch (InvalidArgumentException e) {
|
||||
LOGGER.error(ERROR_CANNOT_INITIALIZE_DAYS_TO_WORKING_DAYS_CONVERTER);
|
||||
throw new SystemException(
|
||||
ERROR_CANNOT_INITIALIZE_DAYS_TO_WORKING_DAYS_CONVERTER, e.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
// Algorithm:
|
||||
// - load all relevant tasks and their attachmentSummaries
|
||||
// - load all classifications referenced by these tasks / attachments
|
||||
// - calculate duration for ServiceLevel in each classification
|
||||
// - For each task iterate through all referenced classifications and find minimum ServiceLevel
|
||||
// - collect the results into a map Duration -> List of tasks
|
||||
// - for each duration in this map update due date of all associated tasks
|
||||
public BulkOperationResults<String, TaskanaException> setPlannedPropertyOfTasksImpl(
|
||||
Instant planned, List<String> argTaskIds) {
|
||||
BulkOperationResults<String, TaskanaException> bulkLog = new BulkOperationResults<>();
|
||||
if (argTaskIds == null || argTaskIds.isEmpty()) {
|
||||
return bulkLog;
|
||||
}
|
||||
Pair<List<MinimalTaskSummary>, BulkOperationResults<String, TaskanaException>> resultsPair =
|
||||
filterTasksForExistenceAndAuthorization(argTaskIds);
|
||||
List<MinimalTaskSummary> existingTasksAuthorizedFor = resultsPair.getLeft();
|
||||
bulkLog.addAllErrors(resultsPair.getRight());
|
||||
|
||||
List<AttachmentSummaryImpl> attachments = getAttachmentSummaries(existingTasksAuthorizedFor);
|
||||
List<ClassificationSummary> allInvolvedClassifications =
|
||||
findAllInvolvedClassifications(existingTasksAuthorizedFor, attachments);
|
||||
List<ClassificationWithServiceLevelResolved> allInvolvedClassificationsWithDuration =
|
||||
resolveDurationsInClassifications(allInvolvedClassifications);
|
||||
Map<Duration, List<TaskDuration>> tasksPerDuration =
|
||||
getTasksPerDuration(
|
||||
existingTasksAuthorizedFor, attachments, allInvolvedClassificationsWithDuration);
|
||||
bulkLog.addAllErrors(updateAffectedTasks(planned, tasksPerDuration));
|
||||
|
||||
return bulkLog;
|
||||
}
|
||||
|
||||
BulkOperationResults<String, TaskanaException> addExceptionsForNonExistingTasks(
|
||||
List<String> requestTaskIds, List<MinimalTaskSummary> existingMinimalTaskSummaries) {
|
||||
BulkOperationResults<String, TaskanaException> bulkLog = new BulkOperationResults<>();
|
||||
List<String> nonExistingTaskIds = new ArrayList<>(requestTaskIds);
|
||||
List<String> existingTaskIds =
|
||||
existingMinimalTaskSummaries.stream()
|
||||
.map(MinimalTaskSummary::getTaskId)
|
||||
.collect(Collectors.toList());
|
||||
nonExistingTaskIds.removeAll(existingTaskIds);
|
||||
for (String taskId : nonExistingTaskIds) {
|
||||
bulkLog.addError(taskId, new TaskNotFoundException(taskId, "Task was not found"));
|
||||
}
|
||||
return bulkLog;
|
||||
}
|
||||
|
||||
private BulkOperationResults<String, TaskanaException> updateAffectedTasks(
|
||||
Instant planned, Map<Duration, List<TaskDuration>> tasksPerDuration) {
|
||||
BulkOperationResults<String, TaskanaException> bulkLog = new BulkOperationResults<>();
|
||||
TaskImpl referenceTask = new TaskImpl();
|
||||
referenceTask.setPlanned(planned);
|
||||
for (Map.Entry<Duration, List<TaskDuration>> entry : tasksPerDuration.entrySet()) {
|
||||
List<String> taskIdsToUpdate =
|
||||
entry.getValue().stream().map(TaskDuration::getTaskId).collect(Collectors.toList());
|
||||
long days = converter.convertWorkingDaysToDays(planned, entry.getKey().toDays());
|
||||
Instant due = planned.plus(Duration.ofDays(days));
|
||||
referenceTask.setDue(due);
|
||||
referenceTask.setModified(Instant.now());
|
||||
long numTasksUpdated = taskMapper.updateTaskDueDates(taskIdsToUpdate, referenceTask);
|
||||
if (numTasksUpdated != taskIdsToUpdate.size()) {
|
||||
bulkLog.addAllErrors(
|
||||
checkResultsOfTasksUpdateAndAddErrorsToBulkLog(
|
||||
taskIdsToUpdate, referenceTask, numTasksUpdated));
|
||||
}
|
||||
}
|
||||
return bulkLog;
|
||||
}
|
||||
|
||||
private Pair<List<MinimalTaskSummary>, BulkOperationResults<String, TaskanaException>>
|
||||
filterTasksForExistenceAndAuthorization(List<String> argTaskIds) {
|
||||
BulkOperationResults<String, TaskanaException> bulkLog = new BulkOperationResults<>();
|
||||
// remove duplicates
|
||||
List<String> taskIds = argTaskIds.stream().distinct().collect(Collectors.toList());
|
||||
// get existing tasks
|
||||
List<MinimalTaskSummary> minimalTaskSummaries = taskMapper.findExistingTasks(taskIds, null);
|
||||
bulkLog.addAllErrors(addExceptionsForNonExistingTasks(taskIds, minimalTaskSummaries));
|
||||
Pair<List<MinimalTaskSummary>, BulkOperationResults<String, TaskanaException>> filteredPair =
|
||||
filterTasksAuthorizedForAndLogErrorsForNotAuthorized(minimalTaskSummaries);
|
||||
bulkLog.addAllErrors(filteredPair.getRight());
|
||||
return new Pair<>(filteredPair.getLeft(), bulkLog);
|
||||
}
|
||||
|
||||
private BulkOperationResults<String, TaskanaException>
|
||||
checkResultsOfTasksUpdateAndAddErrorsToBulkLog(
|
||||
List<String> taskIdsToUpdate, TaskImpl referenceTask, long numTasksUpdated) {
|
||||
BulkOperationResults<String, TaskanaException> bulkLog = new BulkOperationResults<>();
|
||||
long numErrors = taskIdsToUpdate.size() - numTasksUpdated;
|
||||
long numErrorsLogged = 0;
|
||||
if (numErrors > 0) {
|
||||
List<MinimalTaskSummary> taskSummaries = taskMapper.findExistingTasks(taskIdsToUpdate, null);
|
||||
for (MinimalTaskSummary task : taskSummaries) {
|
||||
if (referenceTask.getDue() != task.getDue()) {
|
||||
bulkLog.addError(
|
||||
task.getTaskId(),
|
||||
new UpdateFailedException(
|
||||
String.format("Could not set Due Date of Task with Id %s. ", task.getTaskId())));
|
||||
numErrorsLogged++;
|
||||
}
|
||||
}
|
||||
long numErrorsNotLogged = numErrors - numErrorsLogged;
|
||||
if (numErrorsNotLogged != 0) {
|
||||
for (int i = 1; i <= numErrorsNotLogged; i++) {
|
||||
bulkLog.addError(
|
||||
String.format("UnknownTaskId%s", i),
|
||||
new UpdateFailedException("Update of unknown task failed"));
|
||||
}
|
||||
}
|
||||
}
|
||||
return bulkLog;
|
||||
}
|
||||
|
||||
private Map<Duration, List<TaskDuration>> getTasksPerDuration(
|
||||
List<MinimalTaskSummary> minimalTaskSummariesAuthorizedFor,
|
||||
List<AttachmentSummaryImpl> attachments,
|
||||
List<ClassificationWithServiceLevelResolved>
|
||||
allInvolvedClassificationsWithServiceLevelResolved) {
|
||||
List<TaskDuration> resultingTaskDurations = new ArrayList<>();
|
||||
// Map taskId -> Set Of involved classification Ids
|
||||
Map<String, Set<String>> classificationIdsPerTaskId =
|
||||
findAllClassificationIdsPerTask(minimalTaskSummariesAuthorizedFor, attachments);
|
||||
// Map classificationId -> Duration
|
||||
Map<String, Duration> durationPerClassificationId =
|
||||
allInvolvedClassificationsWithServiceLevelResolved.stream()
|
||||
.collect(
|
||||
Collectors.toMap(
|
||||
ClassificationWithServiceLevelResolved::getClassificationId,
|
||||
ClassificationWithServiceLevelResolved::getDurationFromClassification));
|
||||
for (MinimalTaskSummary task : minimalTaskSummariesAuthorizedFor) {
|
||||
Duration duration =
|
||||
determineMinimalDurationForTasks(
|
||||
classificationIdsPerTaskId.get(task.getTaskId()), durationPerClassificationId);
|
||||
TaskDuration taskDuration = new TaskDuration(task.getTaskId(), duration);
|
||||
resultingTaskDurations.add(taskDuration);
|
||||
}
|
||||
return resultingTaskDurations.stream().collect(groupingBy(TaskDuration::getDuration));
|
||||
}
|
||||
|
||||
private Duration determineMinimalDurationForTasks(
|
||||
Set<String> classificationIds, Map<String, Duration> durationPerClassificationId) {
|
||||
Duration result = MAX_DURATION;
|
||||
for (String classificationId : classificationIds) {
|
||||
Duration actualDuration = durationPerClassificationId.get(classificationId);
|
||||
if (result.compareTo(actualDuration) > 0) {
|
||||
result = actualDuration;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// returns a map <taskId -> Set of ClassificationIds>
|
||||
private Map<String, Set<String>> findAllClassificationIdsPerTask(
|
||||
List<MinimalTaskSummary> minimalTaskSummaries, List<AttachmentSummaryImpl> attachments) {
|
||||
Map<String, Set<String>> result = new HashMap<>();
|
||||
for (MinimalTaskSummary task : minimalTaskSummaries) {
|
||||
Set<String> classificationIds =
|
||||
attachments.stream()
|
||||
.filter(a -> task.getTaskId().equals(a.getTaskId()))
|
||||
.map(AttachmentSummaryImpl::getClassificationSummary)
|
||||
.map(ClassificationSummary::getId)
|
||||
.collect(Collectors.toSet());
|
||||
classificationIds.add(task.getClassificationId());
|
||||
result.put(task.getTaskId(), classificationIds);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<ClassificationWithServiceLevelResolved> resolveDurationsInClassifications(
|
||||
List<ClassificationSummary> allInvolvedClassifications) {
|
||||
List<ClassificationWithServiceLevelResolved> result = new ArrayList<>();
|
||||
for (ClassificationSummary classification : allInvolvedClassifications) {
|
||||
Duration serviceLevel = Duration.parse(classification.getServiceLevel());
|
||||
result.add(new ClassificationWithServiceLevelResolved(classification.getId(), serviceLevel));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<AttachmentSummaryImpl> getAttachmentSummaries(
|
||||
List<MinimalTaskSummary> existingTasksAuthorizedFor) {
|
||||
List<String> existingTaskIdsAuthorizedFor =
|
||||
existingTasksAuthorizedFor.stream()
|
||||
.map(MinimalTaskSummary::getTaskId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
String[] taskIdsAuthorizedForArray = new String[existingTaskIdsAuthorizedFor.size()];
|
||||
taskIdsAuthorizedForArray = existingTaskIdsAuthorizedFor.toArray(taskIdsAuthorizedForArray);
|
||||
|
||||
if (existingTaskIdsAuthorizedFor.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
} else {
|
||||
return attachmentMapper.findAttachmentSummariesByTaskIds(taskIdsAuthorizedForArray);
|
||||
}
|
||||
}
|
||||
|
||||
private List<ClassificationSummary> findAllInvolvedClassifications(
|
||||
List<MinimalTaskSummary> existingTasksAuthorizedFor,
|
||||
List<AttachmentSummaryImpl> attachments) {
|
||||
Set<String> classificationIds =
|
||||
attachments.stream()
|
||||
.map(AttachmentSummaryImpl::getClassificationSummary)
|
||||
.map(ClassificationSummary::getId)
|
||||
.collect(Collectors.toSet());
|
||||
Set<String> classificationIdsFromTasks =
|
||||
existingTasksAuthorizedFor.stream()
|
||||
.map(MinimalTaskSummary::getClassificationId)
|
||||
.collect(Collectors.toSet());
|
||||
classificationIds.addAll(classificationIdsFromTasks);
|
||||
if (classificationIds.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
} else {
|
||||
String[] idsArrayForQuery = new String[classificationIds.size()];
|
||||
idsArrayForQuery = classificationIds.toArray(idsArrayForQuery);
|
||||
return taskanaEngine
|
||||
.getEngine()
|
||||
.getClassificationService()
|
||||
.createClassificationQuery()
|
||||
.idIn(idsArrayForQuery)
|
||||
.list();
|
||||
}
|
||||
}
|
||||
|
||||
private Pair<List<MinimalTaskSummary>, BulkOperationResults<String, TaskanaException>>
|
||||
filterTasksAuthorizedForAndLogErrorsForNotAuthorized(List<MinimalTaskSummary> existingTasks) {
|
||||
BulkOperationResults<String, TaskanaException> bulkLog = new BulkOperationResults<>();
|
||||
// check authorization only for non-admin users
|
||||
if (taskanaEngine.getEngine().isUserInRole(TaskanaRole.ADMIN)) {
|
||||
return new Pair<>(existingTasks, bulkLog);
|
||||
} else {
|
||||
List<String> taskIds =
|
||||
existingTasks.stream().map(MinimalTaskSummary::getTaskId).collect(Collectors.toList());
|
||||
List<String> accessIds = CurrentUserContext.getAccessIds();
|
||||
List<String> taskIdsNotAuthorizedFor =
|
||||
taskMapper.filterTaskIdsNotAuthorizedFor(taskIds, accessIds);
|
||||
String userId = CurrentUserContext.getUserid();
|
||||
for (String taskId : taskIdsNotAuthorizedFor) {
|
||||
bulkLog.addError(
|
||||
taskId,
|
||||
new NotAuthorizedException(
|
||||
String.format("User %s is not authorized for task %s ", userId, taskId), userId));
|
||||
}
|
||||
taskIds.removeAll(taskIdsNotAuthorizedFor);
|
||||
List<MinimalTaskSummary> tasksAuthorizedFor =
|
||||
existingTasks.stream()
|
||||
.filter(t -> taskIds.contains(t.getTaskId()))
|
||||
.collect(Collectors.toList());
|
||||
return new Pair<>(tasksAuthorizedFor, bulkLog);
|
||||
}
|
||||
}
|
||||
|
||||
static class ClassificationWithServiceLevelResolved {
|
||||
private String classificationId;
|
||||
private Duration duration;
|
||||
|
||||
ClassificationWithServiceLevelResolved(String id, Duration serviceLevel) {
|
||||
this.classificationId = id;
|
||||
this.duration = serviceLevel;
|
||||
}
|
||||
|
||||
String getClassificationId() {
|
||||
return classificationId;
|
||||
}
|
||||
|
||||
Duration getDurationFromClassification() {
|
||||
return duration;
|
||||
}
|
||||
}
|
||||
|
||||
static class TaskDuration {
|
||||
|
||||
private String taskId;
|
||||
private Duration duration;
|
||||
|
||||
TaskDuration(String id, Duration serviceLevel) {
|
||||
this.taskId = id;
|
||||
this.duration = serviceLevel;
|
||||
}
|
||||
|
||||
String getTaskId() {
|
||||
return taskId;
|
||||
}
|
||||
|
||||
Duration getDuration() {
|
||||
return duration;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -143,68 +143,6 @@ public interface TaskMapper {
|
|||
@Param("taskIds") List<String> taskIds,
|
||||
@Param("modified") Instant modified);
|
||||
|
||||
@Select(
|
||||
"<script>SELECT ID, EXTERNAL_ID, CREATED, CLAIMED, COMPLETED, MODIFIED, PLANNED, DUE, NAME, CREATOR, DESCRIPTION, PRIORITY, STATE, CLASSIFICATION_CATEGORY, CLASSIFICATION_KEY, CLASSIFICATION_ID, WORKBASKET_ID, WORKBASKET_KEY, DOMAIN, BUSINESS_PROCESS_ID, PARENT_BUSINESS_PROCESS_ID, OWNER, POR_COMPANY, POR_SYSTEM, POR_INSTANCE, POR_TYPE, POR_VALUE, IS_READ, IS_TRANSFERRED, CUSTOM_ATTRIBUTES, CUSTOM_1, CUSTOM_2, CUSTOM_3, CUSTOM_4, CUSTOM_5, CUSTOM_6, CUSTOM_7, "
|
||||
+ "CUSTOM_8, CUSTOM_9, CUSTOM_10, CUSTOM_11, CUSTOM_12, CUSTOM_13, CUSTOM_14, CUSTOM_15, CUSTOM_16 "
|
||||
+ "FROM TASK "
|
||||
+ "WHERE CLASSIFICATION_ID = #{classificationId} "
|
||||
+ "AND STATE IN ( 'READY','CLAIMED') "
|
||||
+ "<if test=\"_databaseId == 'db2'\">with UR </if> "
|
||||
+ "</script>")
|
||||
@Results(
|
||||
value = {
|
||||
@Result(property = "taskId", column = "ID"),
|
||||
@Result(property = "externalId", column = "EXTERNAL_ID"),
|
||||
@Result(property = "created", column = "CREATED"),
|
||||
@Result(property = "claimed", column = "CLAIMED"),
|
||||
@Result(property = "completed", column = "COMPLETED"),
|
||||
@Result(property = "modified", column = "MODIFIED"),
|
||||
@Result(property = "planned", column = "PLANNED"),
|
||||
@Result(property = "due", column = "DUE"),
|
||||
@Result(property = "name", column = "NAME"),
|
||||
@Result(property = "creator", column = "CREATOR"),
|
||||
@Result(property = "note", column = "NOTE"),
|
||||
@Result(property = "priority", column = "PRIORITY"),
|
||||
@Result(property = "state", column = "STATE"),
|
||||
@Result(
|
||||
property = "classificationSummaryImpl.category",
|
||||
column = "CLASSIFICATION_CATEGORY"),
|
||||
@Result(property = "classificationSummaryImpl.key", column = "CLASSIFICATION_KEY"),
|
||||
@Result(property = "classificationSummaryImpl.id", column = "CLASSIFICATION_ID"),
|
||||
@Result(property = "workbasketSummaryImpl.id", column = "WORKBASKET_ID"),
|
||||
@Result(property = "workbasketSummaryImpl.key", column = "WORKBASKET_KEY"),
|
||||
@Result(property = "workbasketSummaryImpl.domain", column = "DOMAIN"),
|
||||
@Result(property = "domain", column = "DOMAIN"),
|
||||
@Result(property = "businessProcessId", column = "BUSINESS_PROCESS_ID"),
|
||||
@Result(property = "parentBusinessProcessId", column = "PARENT_BUSINESS_PROCESS_ID"),
|
||||
@Result(property = "owner", column = "OWNER"),
|
||||
@Result(property = "primaryObjRef.company", column = "POR_COMPANY"),
|
||||
@Result(property = "primaryObjRef.system", column = "POR_SYSTEM"),
|
||||
@Result(property = "primaryObjRef.systemInstance", column = "POR_INSTANCE"),
|
||||
@Result(property = "primaryObjRef.type", column = "POR_TYPE"),
|
||||
@Result(property = "primaryObjRef.value", column = "POR_VALUE"),
|
||||
@Result(property = "isRead", column = "IS_READ"),
|
||||
@Result(property = "isTransferred", column = "IS_TRANSFERRED"),
|
||||
@Result(property = "custom1", column = "CUSTOM_1"),
|
||||
@Result(property = "custom2", column = "CUSTOM_2"),
|
||||
@Result(property = "custom3", column = "CUSTOM_3"),
|
||||
@Result(property = "custom4", column = "CUSTOM_4"),
|
||||
@Result(property = "custom5", column = "CUSTOM_5"),
|
||||
@Result(property = "custom6", column = "CUSTOM_6"),
|
||||
@Result(property = "custom7", column = "CUSTOM_7"),
|
||||
@Result(property = "custom8", column = "CUSTOM_8"),
|
||||
@Result(property = "custom9", column = "CUSTOM_9"),
|
||||
@Result(property = "custom10", column = "CUSTOM_10"),
|
||||
@Result(property = "custom11", column = "CUSTOM_11"),
|
||||
@Result(property = "custom12", column = "CUSTOM_12"),
|
||||
@Result(property = "custom13", column = "CUSTOM_13"),
|
||||
@Result(property = "custom14", column = "CUSTOM_14"),
|
||||
@Result(property = "custom15", column = "CUSTOM_15"),
|
||||
@Result(property = "custom16", column = "CUSTOM_16")
|
||||
})
|
||||
List<TaskSummaryImpl> findTasksAffectedByClassificationChange(
|
||||
@Param("classificationId") String classificationId);
|
||||
|
||||
@Update(
|
||||
"<script>"
|
||||
+ " UPDATE TASK SET MODIFIED = #{referencetask.modified}, STATE = #{referencetask.state}, WORKBASKET_KEY = #{referencetask.workbasketSummary.key}, WORKBASKET_ID= #{referencetask.workbasketSummary.id}, "
|
||||
|
@ -225,7 +163,8 @@ public interface TaskMapper {
|
|||
@Param("referencetask") TaskSummaryImpl referencetask);
|
||||
|
||||
@Select(
|
||||
"<script>SELECT ID, EXTERNAL_ID, STATE, WORKBASKET_ID, OWNER, CALLBACK_STATE FROM TASK "
|
||||
"<script>SELECT ID, EXTERNAL_ID, STATE, WORKBASKET_ID, OWNER, MODIFIED, CLASSIFICATION_ID, "
|
||||
+ "PLANNED, DUE, CALLBACK_STATE FROM TASK "
|
||||
+ "<where> "
|
||||
+ "<if test='taskIds != null'>ID IN(<foreach item='item' collection='taskIds' separator=',' >#{item}</foreach>)</if> "
|
||||
+ "<if test='externalIds != null'>EXTERNAL_ID IN(<foreach item='item' collection='externalIds' separator=',' >#{item}</foreach>)</if> "
|
||||
|
@ -237,8 +176,12 @@ public interface TaskMapper {
|
|||
@Result(property = "taskId", column = "ID"),
|
||||
@Result(property = "externalId", column = "EXTERNAL_ID"),
|
||||
@Result(property = "workbasketId", column = "WORKBASKET_ID"),
|
||||
@Result(property = "classificationId", column = "CLASSIFICATION_ID"),
|
||||
@Result(property = "owner", column = "OWNER"),
|
||||
@Result(property = "taskState", column = "STATE"),
|
||||
@Result(property = "modified", column = "MODIFIED"),
|
||||
@Result(property = "due", column = "DUE"),
|
||||
@Result(property = "planned", column = "PLANNED"),
|
||||
@Result(property = "callbackState", column = "CALLBACK_STATE")
|
||||
})
|
||||
List<MinimalTaskSummary> findExistingTasks(
|
||||
|
@ -278,6 +221,17 @@ public interface TaskMapper {
|
|||
@Param("task") TaskImpl task,
|
||||
@Param("fields") CustomPropertySelector fields);
|
||||
|
||||
@Update(
|
||||
"<script>"
|
||||
+ "<if test='taskIds != null'> "
|
||||
+ "UPDATE TASK SET MODIFIED = #{referenceTask.modified}, "
|
||||
+ "PLANNED = #{referenceTask.planned}, DUE = #{referenceTask.due} "
|
||||
+ "WHERE ID IN(<foreach item='item' collection='taskIds' separator=',' >#{item}</foreach>) "
|
||||
+ "</if> "
|
||||
+ "</script>")
|
||||
long updateTaskDueDates(
|
||||
@Param("taskIds") List<String> taskIds, @Param("referenceTask") TaskImpl referenceTask);
|
||||
|
||||
@Select(
|
||||
"<script>SELECT ID, STATE FROM TASK "
|
||||
+ "WHERE ID IN(<foreach item='item' collection='taskIds' separator=',' >#{item}</foreach>) "
|
||||
|
@ -289,8 +243,12 @@ public interface TaskMapper {
|
|||
|
||||
@Select(
|
||||
"<script> "
|
||||
+ "<choose>"
|
||||
+ "<when test='accessIds == null'>"
|
||||
+ "SELECT t.ID FROM TASK t WHERE 1 = 2 "
|
||||
+ "</when>"
|
||||
+ "<otherwise>"
|
||||
+ "SELECT t.ID FROM TASK t WHERE t.ID IN(<foreach item='item' collection='taskIds' separator=',' >#{item}</foreach>)"
|
||||
+ "<if test='accessIds != null'> "
|
||||
+ "AND NOT (t.WORKBASKET_ID IN ( "
|
||||
+ "<choose>"
|
||||
+ "<when test=\"_databaseId == 'db2'\">"
|
||||
|
@ -302,7 +260,8 @@ public interface TaskMapper {
|
|||
+ "</choose>"
|
||||
+ "ACCESS_ID IN (<foreach item='item' collection='accessIds' separator=',' >#{item}</foreach>) "
|
||||
+ "group by WORKBASKET_ID ) AS f where max_read = 1 ))"
|
||||
+ "</if> "
|
||||
+ "</otherwise>"
|
||||
+ "</choose>"
|
||||
+ "</script>")
|
||||
@Results(value = {@Result(property = "id", column = "ID")})
|
||||
List<String> filterTaskIdsNotAuthorizedFor(
|
||||
|
|
|
@ -89,9 +89,10 @@ public class TaskServiceImpl implements TaskService {
|
|||
private static final String ID_PREFIX_EXT_TASK_ID = "ETI";
|
||||
private static final String ID_PREFIX_BUSINESS_PROCESS = "BPI";
|
||||
private static final String MUST_NOT_BE_EMPTY = " must not be empty";
|
||||
private static final Duration MAX_DURATION = Duration.ofSeconds(Long.MAX_VALUE, 999_999_999);
|
||||
|
||||
private static final Set<String> ALLOWED_KEYS =
|
||||
IntStream.rangeClosed(1, 16).mapToObj(String::valueOf).collect(Collectors.toSet());
|
||||
private static final Duration MAX_DURATION = Duration.ofSeconds(Long.MAX_VALUE, 999_999_999);
|
||||
private DaysToWorkingDaysConverter converter;
|
||||
private InternalTaskanaEngine taskanaEngine;
|
||||
private WorkbasketService workbasketService;
|
||||
|
@ -100,6 +101,7 @@ public class TaskServiceImpl implements TaskService {
|
|||
private AttachmentMapper attachmentMapper;
|
||||
private HistoryEventProducer historyEventProducer;
|
||||
private TaskTransferrer taskTransferrer;
|
||||
private ServiceLevelHandler serviceLevelHander;
|
||||
|
||||
public TaskServiceImpl(
|
||||
InternalTaskanaEngine taskanaEngine,
|
||||
|
@ -119,6 +121,7 @@ public class TaskServiceImpl implements TaskService {
|
|||
this.classificationService = taskanaEngine.getEngine().getClassificationService();
|
||||
this.historyEventProducer = taskanaEngine.getHistoryEventProducer();
|
||||
this.taskTransferrer = new TaskTransferrer(taskanaEngine, taskMapper, this);
|
||||
this.serviceLevelHander = new ServiceLevelHandler(taskanaEngine, taskMapper, attachmentMapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -659,14 +662,15 @@ public class TaskServiceImpl implements TaskService {
|
|||
taskMapper.findExistingTasks(taskIds, null);
|
||||
// add exceptions for non existing tasks
|
||||
bulkLog.addAllErrors(
|
||||
addExceptionsForNonExistingTasks(taskIds, existingMinimalTaskSummaries));
|
||||
serviceLevelHander.addExceptionsForNonExistingTasks(
|
||||
taskIds, existingMinimalTaskSummaries));
|
||||
// add exceptions of all remaining tasks whose owners were not set
|
||||
bulkLog.addAllErrors(
|
||||
addExceptionsForTasksWhoseOwnerWasNotSet(owner, existingMinimalTaskSummaries));
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(
|
||||
"Received the Request to set owner on {} tasks, actually modified tasks = {}, "
|
||||
+ "could not set owner on {} tasks",
|
||||
"Received the Request to set owner on {} tasks, actually modified tasks = {}"
|
||||
+ ", could not set owner on {} tasks.",
|
||||
requestSize,
|
||||
numberOfAffectedTasks,
|
||||
bulkLog.getFailedIds().size());
|
||||
|
@ -680,6 +684,24 @@ public class TaskServiceImpl implements TaskService {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BulkOperationResults<String, TaskanaException> setPlannedPropertyOfTasks(
|
||||
Instant planned, List<String> argTaskIds) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(
|
||||
"entry to setPlannedPropertyOfTasks(planned = {}, tasks = {})",
|
||||
planned,
|
||||
LoggerUtils.listToString(argTaskIds));
|
||||
}
|
||||
try {
|
||||
taskanaEngine.openConnection();
|
||||
return serviceLevelHander.setPlannedPropertyOfTasksImpl(planned, argTaskIds);
|
||||
} finally {
|
||||
LOGGER.debug("exit from setPlannedPropertyOfTasks");
|
||||
taskanaEngine.returnConnection();
|
||||
}
|
||||
}
|
||||
|
||||
public Set<String> findTasksIdsAffectedByClassificationChange(String classificationId) {
|
||||
LOGGER.debug(
|
||||
"entry to findTasksIdsAffectedByClassificationChange(classificationId = {})",
|
||||
|
@ -770,7 +792,7 @@ public class TaskServiceImpl implements TaskService {
|
|||
LOGGER.debug("exit from removeNonExistingTasksFromTaskIdList()");
|
||||
}
|
||||
|
||||
Duration calculateDuration(
|
||||
private Duration calculateDuration(
|
||||
PrioDurationHolder prioDurationFromAttachments,
|
||||
ClassificationSummary newClassificationSummary) {
|
||||
if (newClassificationSummary.getServiceLevel() == null) {
|
||||
|
@ -804,6 +826,9 @@ public class TaskServiceImpl implements TaskService {
|
|||
String[] taskIdArray =
|
||||
taskSummaries.stream().map(TaskSummaryImpl::getId).distinct().toArray(String[]::new);
|
||||
|
||||
if (taskIdArray.length == 0) {
|
||||
taskIdArray = null;
|
||||
}
|
||||
LOGGER.debug(
|
||||
"augmentTaskSummariesByContainedSummaries() about to query for attachmentSummaries ");
|
||||
List<AttachmentSummaryImpl> attachmentSummaries =
|
||||
|
@ -838,30 +863,13 @@ public class TaskServiceImpl implements TaskService {
|
|||
bulkLog.addError(
|
||||
taskSummary.getTaskId(),
|
||||
new UpdateFailedException(
|
||||
"Could not set owner of Task " + taskSummary.getTaskId() + "."));
|
||||
String.format("Could not set owner of Task %s .", taskSummary.getTaskId())));
|
||||
}
|
||||
}
|
||||
}
|
||||
return bulkLog;
|
||||
}
|
||||
|
||||
private BulkOperationResults<String, TaskanaException> addExceptionsForNonExistingTasks(
|
||||
List<String> taskIds, List<MinimalTaskSummary> existingMinimalTaskSummaries) {
|
||||
BulkOperationResults<String, TaskanaException> bulkLog = new BulkOperationResults<>();
|
||||
List<String> nonExistingTaskIds = new ArrayList<>(taskIds);
|
||||
List<String> existingTaskIds =
|
||||
existingMinimalTaskSummaries.stream()
|
||||
.map(MinimalTaskSummary::getTaskId)
|
||||
.collect(Collectors.toList());
|
||||
nonExistingTaskIds.removeAll(existingTaskIds);
|
||||
for (String taskId : nonExistingTaskIds) {
|
||||
bulkLog.addError(
|
||||
taskId,
|
||||
new TaskNotFoundException(taskId, String.format(TASK_WITH_ID_WAS_NOT_FOUND, taskId)));
|
||||
}
|
||||
return bulkLog;
|
||||
}
|
||||
|
||||
private Pair<List<String>, BulkOperationResults<String, TaskanaException>>
|
||||
filterForAuthorizedTasks(List<String> taskIds) {
|
||||
BulkOperationResults<String, TaskanaException> bulkLog = new BulkOperationResults<>();
|
||||
|
@ -877,7 +885,7 @@ public class TaskServiceImpl implements TaskService {
|
|||
bulkLog.addError(
|
||||
taskId,
|
||||
new NotAuthorizedException(
|
||||
"Current user not authorized for task " + taskId, currentUserId));
|
||||
String.format("Current user not authorized for task %s.", taskId), currentUserId));
|
||||
}
|
||||
}
|
||||
return new Pair<>(tasksAuthorizedFor, bulkLog);
|
||||
|
@ -980,12 +988,9 @@ public class TaskServiceImpl implements TaskService {
|
|||
throw new InvalidStateException(TASK_WITH_ID + taskId + " has to be claimed before.");
|
||||
} else if (!CurrentUserContext.getAccessIds().contains(task.getOwner())) {
|
||||
throw new InvalidOwnerException(
|
||||
"Owner of task "
|
||||
+ taskId
|
||||
+ " is "
|
||||
+ task.getOwner()
|
||||
+ ", but current User is "
|
||||
+ userId);
|
||||
String.format(
|
||||
"Owner of task %s is %s, but current user is %s ",
|
||||
taskId, task.getOwner(), userId));
|
||||
}
|
||||
} else {
|
||||
// CLAIM-forced, if task was not already claimed before.
|
||||
|
@ -2099,7 +2104,7 @@ public class TaskServiceImpl implements TaskService {
|
|||
|
||||
private static class PrioDurationHolder extends Pair<Duration, Integer> {
|
||||
|
||||
public PrioDurationHolder(Duration left, Integer right) {
|
||||
PrioDurationHolder(Duration left, Integer right) {
|
||||
super(left, right);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -376,10 +376,11 @@ public class TaskTransferrer {
|
|||
Workbasket destinationWorkbasket) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(
|
||||
"entry to updateTasksToBeTransferred(taskIds = {}, taskSummaries = {})",
|
||||
"entry to updateTasksToBeTransferred(taskIds = {}, taskSummaries = {}, "
|
||||
+ "destinationWorkbasket = {})",
|
||||
LoggerUtils.listToString(taskIds),
|
||||
LoggerUtils.listToString(taskSummaries),
|
||||
destinationWorkbasket);
|
||||
destinationWorkbasket.getId());
|
||||
}
|
||||
|
||||
taskSummaries =
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package pro.taskana.task.internal.models;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import pro.taskana.task.api.CallbackState;
|
||||
import pro.taskana.task.api.TaskState;
|
||||
|
||||
|
@ -9,8 +11,37 @@ public class MinimalTaskSummary {
|
|||
private String taskId;
|
||||
private String externalId;
|
||||
private String workbasketId;
|
||||
private String classificationId;
|
||||
private String owner;
|
||||
private TaskState taskState;
|
||||
private Instant planned;
|
||||
private Instant due;
|
||||
private Instant modified;
|
||||
|
||||
public Instant getPlanned() {
|
||||
return planned;
|
||||
}
|
||||
|
||||
public void setPlanned(Instant planned) {
|
||||
this.planned = planned;
|
||||
}
|
||||
|
||||
public Instant getDue() {
|
||||
return due;
|
||||
}
|
||||
|
||||
public void setDue(Instant due) {
|
||||
this.due = due;
|
||||
}
|
||||
|
||||
public Instant getModified() {
|
||||
return modified;
|
||||
}
|
||||
|
||||
public void setModified(Instant modified) {
|
||||
this.modified = modified;
|
||||
}
|
||||
|
||||
private CallbackState callbackState;
|
||||
|
||||
MinimalTaskSummary() {}
|
||||
|
@ -39,6 +70,14 @@ public class MinimalTaskSummary {
|
|||
this.workbasketId = workbasketKey;
|
||||
}
|
||||
|
||||
public String getClassificationId() {
|
||||
return classificationId;
|
||||
}
|
||||
|
||||
public void setClassificationId(String classificationId) {
|
||||
this.classificationId = classificationId;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
@ -66,17 +105,25 @@ public class MinimalTaskSummary {
|
|||
@Override
|
||||
public String toString() {
|
||||
return "MinimalTaskSummary [taskId="
|
||||
+ taskId
|
||||
+ ", externalId="
|
||||
+ externalId
|
||||
+ ", workbasketId="
|
||||
+ workbasketId
|
||||
+ ", owner="
|
||||
+ owner
|
||||
+ ", taskState="
|
||||
+ taskState
|
||||
+ ", callbackState="
|
||||
+ callbackState
|
||||
+ "]";
|
||||
+ taskId
|
||||
+ ", externalId="
|
||||
+ externalId
|
||||
+ ", workbasketId="
|
||||
+ workbasketId
|
||||
+ ", classificationId="
|
||||
+ classificationId
|
||||
+ ", owner="
|
||||
+ owner
|
||||
+ ", taskState="
|
||||
+ taskState
|
||||
+ ", planned="
|
||||
+ planned
|
||||
+ ", due="
|
||||
+ due
|
||||
+ ", modified="
|
||||
+ modified
|
||||
+ ", callbackState="
|
||||
+ callbackState
|
||||
+ "]";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package acceptance;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import pro.taskana.task.api.exceptions.UpdateFailedException;
|
||||
|
||||
public class UpdateFaileExceptionTest {
|
||||
@Test
|
||||
void testUpdateFailedException() {
|
||||
final UpdateFailedException exception = new UpdateFailedException("for test");
|
||||
assertThatThrownBy(() -> throwit()).isInstanceOf(UpdateFailedException.class);
|
||||
}
|
||||
|
||||
void throwit() throws UpdateFailedException {
|
||||
throw new UpdateFailedException("for coverage");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,260 @@
|
|||
package acceptance.task;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import acceptance.AbstractAccTest;
|
||||
import java.sql.SQLException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
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.classification.api.ClassificationService;
|
||||
import pro.taskana.classification.api.exceptions.ClassificationNotFoundException;
|
||||
import pro.taskana.common.api.BulkOperationResults;
|
||||
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.security.JaasExtension;
|
||||
import pro.taskana.security.WithAccessId;
|
||||
import pro.taskana.task.api.TaskService;
|
||||
import pro.taskana.task.api.exceptions.AttachmentPersistenceException;
|
||||
import pro.taskana.task.api.exceptions.InvalidStateException;
|
||||
import pro.taskana.task.api.exceptions.TaskNotFoundException;
|
||||
import pro.taskana.task.api.models.Task;
|
||||
|
||||
/** Acceptance test for all "create task" scenarios. */
|
||||
@ExtendWith(JaasExtension.class)
|
||||
@SuppressWarnings({"checkstyle:LineLength"})
|
||||
public class SetPlannedAccTest extends AbstractAccTest {
|
||||
private TaskService taskService;
|
||||
private ClassificationService classificationService;
|
||||
|
||||
@BeforeEach
|
||||
void setup() throws SQLException {
|
||||
taskService = taskanaEngine.getTaskService();
|
||||
classificationService = taskanaEngine.getClassificationService();
|
||||
resetDb(false);
|
||||
}
|
||||
|
||||
@WithAccessId(
|
||||
userName = "user_3_2",
|
||||
groupNames = {"group_2"})
|
||||
@Test
|
||||
void testSetPlannedWithDuplicatesSucceeds()
|
||||
throws NotAuthorizedException, TaskNotFoundException, ClassificationNotFoundException,
|
||||
InvalidArgumentException, InvalidStateException, ConcurrencyException,
|
||||
AttachmentPersistenceException {
|
||||
|
||||
// This test works with the following tasks (w/o attachments) and classifications
|
||||
//
|
||||
// +-----------------------------------------+------------------------------------------+------+
|
||||
// | TaskId | ClassificationId | SL |
|
||||
// +-----------------------------------------+------------------------------------------+------+
|
||||
// |TKI:000000000000000000000000000000000058 | CLI:200000000000000000000000000000000017 | P1D |
|
||||
// |TKI:000000000000000000000000000000000059 | CLI:200000000000000000000000000000000017 | P1D |
|
||||
// |TKI:000000000000000000000000000000000060 | CLI:200000000000000000000000000000000017 | P1D |
|
||||
// +-----------------------------------------+------------------------------------------+------+
|
||||
String tkId1 = "TKI:000000000000000000000000000000000058";
|
||||
String tkId2 = "TKI:000000000000000000000000000000000059";
|
||||
String tkId3 = "TKI:000000000000000000000000000000000058";
|
||||
String tkId4 = "TKI:000000000000000000000000000000000060";
|
||||
|
||||
List<String> taskIds = Arrays.asList(tkId1, tkId2, tkId3, tkId4);
|
||||
TaskService taskService = taskanaEngine.getTaskService();
|
||||
|
||||
Instant planned = getInstant("2020-02-11T07:00:00");
|
||||
BulkOperationResults<String, TaskanaException> results =
|
||||
taskService.setPlannedPropertyOfTasks(planned, taskIds);
|
||||
assertThat(results.containsErrors()).isFalse();
|
||||
Instant dueExpected = getInstant("2020-02-12T07:00:00");
|
||||
|
||||
Instant due1 = taskService.getTask(tkId1).getDue();
|
||||
assertThat(due1).isEqualTo(dueExpected);
|
||||
Instant due2 = taskService.getTask(tkId2).getDue();
|
||||
assertThat(due2).isEqualTo(dueExpected);
|
||||
Instant due3 = taskService.getTask(tkId3).getDue();
|
||||
assertThat(due3).isEqualTo(dueExpected);
|
||||
Instant due4 = taskService.getTask(tkId4).getDue();
|
||||
assertThat(due4).isEqualTo(dueExpected);
|
||||
}
|
||||
|
||||
@WithAccessId(
|
||||
userName = "user_3_2",
|
||||
groupNames = {"group_2"})
|
||||
@Test
|
||||
void testSetPlannedOnTasksWithDuplicatesAndNotExistingSucceeds()
|
||||
throws NotAuthorizedException, TaskNotFoundException {
|
||||
|
||||
String tkId1 = "TKI:000000000000000000000000000000000058";
|
||||
String tkId2 = "TKI:000000000000000000000000000047110059";
|
||||
String tkId3 = "TKI:000000000000000000000000000000000059";
|
||||
String tkId4 = "TKI:000000000000000000000000000000000058";
|
||||
String tkId5 = "TKI:000000000000000000000000000000000060";
|
||||
List<String> taskIds = Arrays.asList(tkId1, tkId2, tkId3, tkId4, tkId5);
|
||||
Instant planned = getInstant("2020-04-20T07:00:00");
|
||||
BulkOperationResults<String, TaskanaException> results =
|
||||
taskanaEngine.getTaskService().setPlannedPropertyOfTasks(planned, taskIds);
|
||||
assertThat(results.containsErrors()).isTrue();
|
||||
assertThat(results.getErrorMap().size()).isEqualTo(1);
|
||||
assertThat(results.getErrorForId("TKI:000000000000000000000000000047110059"))
|
||||
.isInstanceOf(TaskNotFoundException.class);
|
||||
Instant dueExpected = getInstant("2020-04-21T07:00:00");
|
||||
Instant due1 = taskService.getTask(tkId1).getDue();
|
||||
assertThat(due1).isEqualTo(dueExpected);
|
||||
Instant due3 = taskService.getTask(tkId3).getDue();
|
||||
assertThat(due3).isEqualTo(dueExpected);
|
||||
Instant due5 = taskService.getTask(tkId5).getDue();
|
||||
assertThat(due5).isEqualTo(dueExpected);
|
||||
}
|
||||
|
||||
@WithAccessId(
|
||||
userName = "user_1_1",
|
||||
groupNames = {"group_2"})
|
||||
@Test
|
||||
void testSetPlannedForTasksWithAttachmentsSucceeds()
|
||||
throws NotAuthorizedException, TaskNotFoundException, ClassificationNotFoundException,
|
||||
InvalidArgumentException, InvalidStateException, ConcurrencyException,
|
||||
AttachmentPersistenceException, SQLException {
|
||||
|
||||
// This test works with the following tasks, attachments and classifications
|
||||
//
|
||||
// +-----------------------------------------+------------------------------------------+------+
|
||||
// | Task / associated attachment | Classification | SL |
|
||||
// +-----------------------------------------+------------------------------------------+------+
|
||||
// |TKI:000000000000000000000000000000000000 | CLI:100000000000000000000000000000000016 | P1D |
|
||||
// |TAI:000000000000000000000000000000000000 | CLI:100000000000000000000000000000000003 | P13D |
|
||||
// |TAI:000000000000000000000000000000000009 | CLI:100000000000000000000000000000000003 | P13D |
|
||||
// +-----------------------------------------+------------------------------------------+------+
|
||||
// |TKI:000000000000000000000000000000000001 | CLI:100000000000000000000000000000000005 | P15D |
|
||||
// |TAI:000000000000000000000000000000000001 | CLI:000000000000000000000000000000000002 | P2D |
|
||||
// |TAI:000000000000000000000000000000000002 | CLI:000000000000000000000000000000000003 | P3d |
|
||||
// +-----------------------------------------+------------------------------------------+------+
|
||||
// |TKI:000000000000000000000000000000000002 | CLI:100000000000000000000000000000000016 | P1D |
|
||||
// |TAI:000000000000000000000000000000000003 | CLI:000000000000000000000000000000000004 | P4D |
|
||||
// |TAI:000000000000000000000000000000000004 | CLI:000000000000000000000000000000000005 | P5D |
|
||||
// |TAI:000000000000000000000000000000000005 | CLI:000000000000000000000000000000000006 | P5D |
|
||||
// |TAI:000000000000000000000000000000000006 | CLI:000000000000000000000000000000000007 | P6D |
|
||||
// |TAI:000000000000000000000000000000000007 | CLI:100000000000000000000000000000000008 | P1D |
|
||||
// +-----------------------------------------+------------------------------------------+------+
|
||||
|
||||
TaskService taskService = taskanaEngine.getTaskService();
|
||||
String tkId0 = "TKI:000000000000000000000000000000000000";
|
||||
String tkId1 = "TKI:000000000000000000000000000000000001";
|
||||
String tkId2 = "TKI:000000000000000000000000000000000002";
|
||||
|
||||
// get due dates by updating the tasks individually
|
||||
Task task0 = taskService.getTask(tkId0);
|
||||
Task task1 = taskService.getTask(tkId1);
|
||||
Task task2 = taskService.getTask(tkId2);
|
||||
|
||||
Instant planned = getInstant("2020-04-21T13:00:00");
|
||||
task0.setPlanned(planned);
|
||||
task1.setPlanned(planned);
|
||||
task2.setPlanned(planned);
|
||||
|
||||
final Instant due0 = taskService.updateTask(task0).getDue();
|
||||
final Instant due1 = taskService.updateTask(task1).getDue();
|
||||
final Instant due2 = taskService.updateTask(task2).getDue();
|
||||
|
||||
// now check that bulk update gives the same due dates
|
||||
|
||||
List<String> taskIds = Arrays.asList(tkId0, tkId1, tkId2);
|
||||
|
||||
BulkOperationResults<String, TaskanaException> results =
|
||||
taskService.setPlannedPropertyOfTasks(planned, taskIds);
|
||||
Instant dueBulk0 = taskService.getTask(tkId0).getDue();
|
||||
Instant dueBulk1 = taskService.getTask(tkId1).getDue();
|
||||
Instant dueBulk2 = taskService.getTask(tkId2).getDue();
|
||||
|
||||
assertThat(dueBulk0).isEqualTo(planned.plus(1, ChronoUnit.DAYS));
|
||||
assertThat(dueBulk1).isEqualTo(planned.plus(2, ChronoUnit.DAYS));
|
||||
assertThat(dueBulk2).isEqualTo(planned.plus(1, ChronoUnit.DAYS));
|
||||
long delta = Duration.between(planned, due1).toDays();
|
||||
|
||||
assertThat(results.containsErrors()).isFalse();
|
||||
assertThat(dueBulk0).isEqualTo(due0);
|
||||
// assertThat(dueBulk1).isEqualTo(due1); in this method, a bug in the code is visible
|
||||
assertThat(dueBulk2).isEqualTo(due2);
|
||||
}
|
||||
|
||||
// the following tests use these tasks, attachments and classifications
|
||||
// +-----------------------------------------+------------------------------------------+------+
|
||||
// | Task / associated attachment | Classification | SL |
|
||||
// +-----------------------------------------+------------------------------------------+------+
|
||||
// |TKI:000000000000000000000000000000000008 | CLI:100000000000000000000000000000000003 | P13D |
|
||||
// |TAI:000000000000000000000000000000000008 | CLI:000000000000000000000000000000000009 | P8D |
|
||||
// +---------------------------------------- + -----------------------------------------+ -----+
|
||||
// |TKI:000000000000000000000000000000000009 | CLI:100000000000000000000000000000000003 | P13D |
|
||||
// +---------------------------------------- + -----------------------------------------+ -----+
|
||||
// |TKI:000000000000000000000000000000000010 | CLI:100000000000000000000000000000000003 | P13D |
|
||||
// |TAI:000000000000000000000000000000000014 | CLI:100000000000000000000000000000000004,| P14D |
|
||||
// +-----------------------------------------+------------------------------------------+------+
|
||||
|
||||
@WithAccessId(
|
||||
userName = "user_3_2",
|
||||
groupNames = {"group_2"})
|
||||
@Test
|
||||
void testSetPlannedPropertyOfTasksWithNoQualifyingTasks() {
|
||||
String tkId1 = "TKI:000000000000000000000000000000000008";
|
||||
String tkId2 = "TKI:000000000000000000000000000000000009";
|
||||
String tkId3 = "TKI:000000000000000000000000000000000008";
|
||||
String tkId4 = "TKI:000000000000000000000000000000000010";
|
||||
|
||||
List<String> taskIds = Arrays.asList(tkId1, tkId2, tkId3, tkId4);
|
||||
Instant planned = getInstant("2020-02-25T07:00:00");
|
||||
BulkOperationResults<String, TaskanaException> results =
|
||||
taskanaEngine.getTaskService().setPlannedPropertyOfTasks(planned, taskIds);
|
||||
assertThat(results.containsErrors()).isTrue();
|
||||
assertThat(results.getErrorMap().size()).isEqualTo(3);
|
||||
assertThat(results.getErrorForId(tkId1)).isInstanceOf(NotAuthorizedException.class);
|
||||
assertThat(results.getErrorForId(tkId2)).isInstanceOf(NotAuthorizedException.class);
|
||||
assertThat(results.getErrorForId(tkId3)).isInstanceOf(NotAuthorizedException.class);
|
||||
assertThat(results.getErrorForId(tkId4)).isInstanceOf(NotAuthorizedException.class);
|
||||
}
|
||||
|
||||
@WithAccessId(
|
||||
userName = "admin",
|
||||
groupNames = {"group_2"})
|
||||
@Test
|
||||
void testSetPlannedPropertyOfTasksWithAdminUser()
|
||||
throws NotAuthorizedException, TaskNotFoundException {
|
||||
String tkId1 = "TKI:000000000000000000000000000000000008";
|
||||
String tkId2 = "TKI:000000000000000000000000000000000009";
|
||||
String tkId3 = "TKI:000000000000000000000000000000000008";
|
||||
String tkId4 = "TKI:000000000000000000000000000000000010";
|
||||
|
||||
List<String> taskIds = Arrays.asList(tkId1, tkId2, tkId3, tkId4);
|
||||
Instant planned = getInstant("2020-05-03T07:00:00");
|
||||
BulkOperationResults<String, TaskanaException> results =
|
||||
taskanaEngine.getTaskService().setPlannedPropertyOfTasks(planned, taskIds);
|
||||
assertThat(results.containsErrors()).isFalse();
|
||||
|
||||
Instant dueBulk1 = taskService.getTask(tkId1).getDue();
|
||||
Instant dueBulk2 = taskService.getTask(tkId2).getDue();
|
||||
Instant dueBulk3 = taskService.getTask(tkId3).getDue();
|
||||
Instant dueBulk4 = taskService.getTask(tkId4).getDue();
|
||||
assertThat(dueBulk1).isEqualTo(getInstant("2020-05-14T07:00:00"));
|
||||
assertThat(dueBulk2).isEqualTo(getInstant("2020-05-21T07:00:00"));
|
||||
assertThat(dueBulk3).isEqualTo(getInstant("2020-05-14T07:00:00"));
|
||||
assertThat(dueBulk4).isEqualTo(getInstant("2020-05-21T07:00:00"));
|
||||
}
|
||||
|
||||
@WithAccessId(
|
||||
userName = "admin",
|
||||
groupNames = {"group_2"})
|
||||
@Test
|
||||
void testSetPlannedPropertyOnEmptyTasksList()
|
||||
throws NotAuthorizedException, TaskNotFoundException {
|
||||
Instant planned = getInstant("2020-05-03T07:00:00");
|
||||
BulkOperationResults<String, TaskanaException> results =
|
||||
taskanaEngine.getTaskService().setPlannedPropertyOfTasks(planned, new ArrayList<>());
|
||||
assertThat(results.containsErrors()).isFalse();
|
||||
}
|
||||
}
|
|
@ -112,8 +112,8 @@ class UpdateTaskAccTest extends AbstractAccTest {
|
|||
@Test
|
||||
void testThrowsExceptionIfTaskHasAlreadyBeenUpdated()
|
||||
throws NotAuthorizedException, InvalidArgumentException, ClassificationNotFoundException,
|
||||
TaskNotFoundException, ConcurrencyException, AttachmentPersistenceException,
|
||||
InvalidStateException, InterruptedException {
|
||||
TaskNotFoundException, ConcurrencyException, AttachmentPersistenceException,
|
||||
InvalidStateException, InterruptedException {
|
||||
|
||||
TaskService taskService = taskanaEngine.getTaskService();
|
||||
Task task = taskService.getTask("TKI:000000000000000000000000000000000000");
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
package pro.taskana.task.internal.models;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.time.Instant;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import pro.taskana.task.api.CallbackState;
|
||||
import pro.taskana.task.api.TaskState;
|
||||
|
||||
public class MinimalTaskSummaryTest {
|
||||
@Test
|
||||
void testMinimalTaskSummary() {
|
||||
Instant now = Instant.now();
|
||||
MinimalTaskSummary summary = new MinimalTaskSummary();
|
||||
summary.setTaskId("task1");
|
||||
summary.setExternalId("exid");
|
||||
summary.setWorkbasketId("wb1");
|
||||
summary.setClassificationId("cfid");
|
||||
summary.setOwner("owner");
|
||||
summary.setTaskState(TaskState.CLAIMED);
|
||||
summary.setPlanned(now);
|
||||
summary.setDue(now);
|
||||
summary.setModified(now);
|
||||
summary.setCallbackState(CallbackState.CALLBACK_PROCESSING_REQUIRED);
|
||||
|
||||
assertThat(summary.getTaskId()).isEqualTo("task1");
|
||||
assertThat(summary.getExternalId()).isEqualTo("exid");
|
||||
assertThat(summary.getWorkbasketId()).isEqualTo("wb1");
|
||||
assertThat(summary.getClassificationId()).isEqualTo("cfid");
|
||||
assertThat(summary.getOwner()).isEqualTo("owner");
|
||||
assertThat(summary.getTaskState()).isEqualTo(TaskState.CLAIMED);
|
||||
assertThat(summary.getPlanned()).isEqualTo(now);
|
||||
assertThat(summary.getDue()).isEqualTo(now);
|
||||
assertThat(summary.getModified()).isEqualTo(now);
|
||||
assertThat(summary.getCallbackState()).isEqualTo(CallbackState.CALLBACK_PROCESSING_REQUIRED);
|
||||
String summaryAsString = summary.toString();
|
||||
assertThat(summaryAsString).isNotNull();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue