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;
|
package pro.taskana.task.api;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
@ -381,8 +382,20 @@ public interface TaskService {
|
||||||
*
|
*
|
||||||
* @param owner the new owner of the tasks
|
* @param owner the new owner of the tasks
|
||||||
* @param taskIds the IDs of the tasks on which the owner is to be set.
|
* @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(
|
BulkOperationResults<String, TaskanaException> setOwnerOfTasks(
|
||||||
String owner, List<String> taskIds);
|
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);
|
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(
|
@Select(
|
||||||
"<script>SELECT ID, TASK_ID, CREATED, MODIFIED, CLASSIFICATION_KEY, CLASSIFICATION_ID, REF_COMPANY, REF_SYSTEM, REF_INSTANCE, REF_TYPE, REF_VALUE, CHANNEL, RECEIVED "
|
"<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 "
|
+ "FROM ATTACHMENT "
|
||||||
+ "<where>"
|
+ "<where>"
|
||||||
|
+ "<choose>"
|
||||||
|
+ "<when test='taskIds == null'>"
|
||||||
|
+ " 1 = 2 "
|
||||||
|
+ "</when>"
|
||||||
|
+ "<otherwise>"
|
||||||
+ "TASK_ID IN (<foreach collection='taskIds' item='item' separator=',' >#{item}</foreach>) "
|
+ "TASK_ID IN (<foreach collection='taskIds' item='item' separator=',' >#{item}</foreach>) "
|
||||||
|
+ "</otherwise>"
|
||||||
|
+ "</choose>"
|
||||||
+ "</where>"
|
+ "</where>"
|
||||||
+ "<if test=\"_databaseId == 'db2'\">with UR </if> "
|
+ "<if test=\"_databaseId == 'db2'\">with UR </if> "
|
||||||
+ "</script>")
|
+ "</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("taskIds") List<String> taskIds,
|
||||||
@Param("modified") Instant modified);
|
@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(
|
@Update(
|
||||||
"<script>"
|
"<script>"
|
||||||
+ " UPDATE TASK SET MODIFIED = #{referencetask.modified}, STATE = #{referencetask.state}, WORKBASKET_KEY = #{referencetask.workbasketSummary.key}, WORKBASKET_ID= #{referencetask.workbasketSummary.id}, "
|
+ " 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);
|
@Param("referencetask") TaskSummaryImpl referencetask);
|
||||||
|
|
||||||
@Select(
|
@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> "
|
+ "<where> "
|
||||||
+ "<if test='taskIds != null'>ID IN(<foreach item='item' collection='taskIds' separator=',' >#{item}</foreach>)</if> "
|
+ "<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> "
|
+ "<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 = "taskId", column = "ID"),
|
||||||
@Result(property = "externalId", column = "EXTERNAL_ID"),
|
@Result(property = "externalId", column = "EXTERNAL_ID"),
|
||||||
@Result(property = "workbasketId", column = "WORKBASKET_ID"),
|
@Result(property = "workbasketId", column = "WORKBASKET_ID"),
|
||||||
|
@Result(property = "classificationId", column = "CLASSIFICATION_ID"),
|
||||||
@Result(property = "owner", column = "OWNER"),
|
@Result(property = "owner", column = "OWNER"),
|
||||||
@Result(property = "taskState", column = "STATE"),
|
@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")
|
@Result(property = "callbackState", column = "CALLBACK_STATE")
|
||||||
})
|
})
|
||||||
List<MinimalTaskSummary> findExistingTasks(
|
List<MinimalTaskSummary> findExistingTasks(
|
||||||
|
|
@ -278,6 +221,17 @@ public interface TaskMapper {
|
||||||
@Param("task") TaskImpl task,
|
@Param("task") TaskImpl task,
|
||||||
@Param("fields") CustomPropertySelector fields);
|
@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(
|
@Select(
|
||||||
"<script>SELECT ID, STATE FROM TASK "
|
"<script>SELECT ID, STATE FROM TASK "
|
||||||
+ "WHERE ID IN(<foreach item='item' collection='taskIds' separator=',' >#{item}</foreach>) "
|
+ "WHERE ID IN(<foreach item='item' collection='taskIds' separator=',' >#{item}</foreach>) "
|
||||||
|
|
@ -289,8 +243,12 @@ public interface TaskMapper {
|
||||||
|
|
||||||
@Select(
|
@Select(
|
||||||
"<script> "
|
"<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>)"
|
+ "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 ( "
|
+ "AND NOT (t.WORKBASKET_ID IN ( "
|
||||||
+ "<choose>"
|
+ "<choose>"
|
||||||
+ "<when test=\"_databaseId == 'db2'\">"
|
+ "<when test=\"_databaseId == 'db2'\">"
|
||||||
|
|
@ -302,7 +260,8 @@ public interface TaskMapper {
|
||||||
+ "</choose>"
|
+ "</choose>"
|
||||||
+ "ACCESS_ID IN (<foreach item='item' collection='accessIds' separator=',' >#{item}</foreach>) "
|
+ "ACCESS_ID IN (<foreach item='item' collection='accessIds' separator=',' >#{item}</foreach>) "
|
||||||
+ "group by WORKBASKET_ID ) AS f where max_read = 1 ))"
|
+ "group by WORKBASKET_ID ) AS f where max_read = 1 ))"
|
||||||
+ "</if> "
|
+ "</otherwise>"
|
||||||
|
+ "</choose>"
|
||||||
+ "</script>")
|
+ "</script>")
|
||||||
@Results(value = {@Result(property = "id", column = "ID")})
|
@Results(value = {@Result(property = "id", column = "ID")})
|
||||||
List<String> filterTaskIdsNotAuthorizedFor(
|
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_EXT_TASK_ID = "ETI";
|
||||||
private static final String ID_PREFIX_BUSINESS_PROCESS = "BPI";
|
private static final String ID_PREFIX_BUSINESS_PROCESS = "BPI";
|
||||||
private static final String MUST_NOT_BE_EMPTY = " must not be empty";
|
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 =
|
private static final Set<String> ALLOWED_KEYS =
|
||||||
IntStream.rangeClosed(1, 16).mapToObj(String::valueOf).collect(Collectors.toSet());
|
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 DaysToWorkingDaysConverter converter;
|
||||||
private InternalTaskanaEngine taskanaEngine;
|
private InternalTaskanaEngine taskanaEngine;
|
||||||
private WorkbasketService workbasketService;
|
private WorkbasketService workbasketService;
|
||||||
|
|
@ -100,6 +101,7 @@ public class TaskServiceImpl implements TaskService {
|
||||||
private AttachmentMapper attachmentMapper;
|
private AttachmentMapper attachmentMapper;
|
||||||
private HistoryEventProducer historyEventProducer;
|
private HistoryEventProducer historyEventProducer;
|
||||||
private TaskTransferrer taskTransferrer;
|
private TaskTransferrer taskTransferrer;
|
||||||
|
private ServiceLevelHandler serviceLevelHander;
|
||||||
|
|
||||||
public TaskServiceImpl(
|
public TaskServiceImpl(
|
||||||
InternalTaskanaEngine taskanaEngine,
|
InternalTaskanaEngine taskanaEngine,
|
||||||
|
|
@ -119,6 +121,7 @@ public class TaskServiceImpl implements TaskService {
|
||||||
this.classificationService = taskanaEngine.getEngine().getClassificationService();
|
this.classificationService = taskanaEngine.getEngine().getClassificationService();
|
||||||
this.historyEventProducer = taskanaEngine.getHistoryEventProducer();
|
this.historyEventProducer = taskanaEngine.getHistoryEventProducer();
|
||||||
this.taskTransferrer = new TaskTransferrer(taskanaEngine, taskMapper, this);
|
this.taskTransferrer = new TaskTransferrer(taskanaEngine, taskMapper, this);
|
||||||
|
this.serviceLevelHander = new ServiceLevelHandler(taskanaEngine, taskMapper, attachmentMapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -659,14 +662,15 @@ public class TaskServiceImpl implements TaskService {
|
||||||
taskMapper.findExistingTasks(taskIds, null);
|
taskMapper.findExistingTasks(taskIds, null);
|
||||||
// add exceptions for non existing tasks
|
// add exceptions for non existing tasks
|
||||||
bulkLog.addAllErrors(
|
bulkLog.addAllErrors(
|
||||||
addExceptionsForNonExistingTasks(taskIds, existingMinimalTaskSummaries));
|
serviceLevelHander.addExceptionsForNonExistingTasks(
|
||||||
|
taskIds, existingMinimalTaskSummaries));
|
||||||
// add exceptions of all remaining tasks whose owners were not set
|
// add exceptions of all remaining tasks whose owners were not set
|
||||||
bulkLog.addAllErrors(
|
bulkLog.addAllErrors(
|
||||||
addExceptionsForTasksWhoseOwnerWasNotSet(owner, existingMinimalTaskSummaries));
|
addExceptionsForTasksWhoseOwnerWasNotSet(owner, existingMinimalTaskSummaries));
|
||||||
if (LOGGER.isDebugEnabled()) {
|
if (LOGGER.isDebugEnabled()) {
|
||||||
LOGGER.debug(
|
LOGGER.debug(
|
||||||
"Received the Request to set owner on {} tasks, actually modified tasks = {}, "
|
"Received the Request to set owner on {} tasks, actually modified tasks = {}"
|
||||||
+ "could not set owner on {} tasks",
|
+ ", could not set owner on {} tasks.",
|
||||||
requestSize,
|
requestSize,
|
||||||
numberOfAffectedTasks,
|
numberOfAffectedTasks,
|
||||||
bulkLog.getFailedIds().size());
|
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) {
|
public Set<String> findTasksIdsAffectedByClassificationChange(String classificationId) {
|
||||||
LOGGER.debug(
|
LOGGER.debug(
|
||||||
"entry to findTasksIdsAffectedByClassificationChange(classificationId = {})",
|
"entry to findTasksIdsAffectedByClassificationChange(classificationId = {})",
|
||||||
|
|
@ -770,7 +792,7 @@ public class TaskServiceImpl implements TaskService {
|
||||||
LOGGER.debug("exit from removeNonExistingTasksFromTaskIdList()");
|
LOGGER.debug("exit from removeNonExistingTasksFromTaskIdList()");
|
||||||
}
|
}
|
||||||
|
|
||||||
Duration calculateDuration(
|
private Duration calculateDuration(
|
||||||
PrioDurationHolder prioDurationFromAttachments,
|
PrioDurationHolder prioDurationFromAttachments,
|
||||||
ClassificationSummary newClassificationSummary) {
|
ClassificationSummary newClassificationSummary) {
|
||||||
if (newClassificationSummary.getServiceLevel() == null) {
|
if (newClassificationSummary.getServiceLevel() == null) {
|
||||||
|
|
@ -804,6 +826,9 @@ public class TaskServiceImpl implements TaskService {
|
||||||
String[] taskIdArray =
|
String[] taskIdArray =
|
||||||
taskSummaries.stream().map(TaskSummaryImpl::getId).distinct().toArray(String[]::new);
|
taskSummaries.stream().map(TaskSummaryImpl::getId).distinct().toArray(String[]::new);
|
||||||
|
|
||||||
|
if (taskIdArray.length == 0) {
|
||||||
|
taskIdArray = null;
|
||||||
|
}
|
||||||
LOGGER.debug(
|
LOGGER.debug(
|
||||||
"augmentTaskSummariesByContainedSummaries() about to query for attachmentSummaries ");
|
"augmentTaskSummariesByContainedSummaries() about to query for attachmentSummaries ");
|
||||||
List<AttachmentSummaryImpl> attachmentSummaries =
|
List<AttachmentSummaryImpl> attachmentSummaries =
|
||||||
|
|
@ -838,30 +863,13 @@ public class TaskServiceImpl implements TaskService {
|
||||||
bulkLog.addError(
|
bulkLog.addError(
|
||||||
taskSummary.getTaskId(),
|
taskSummary.getTaskId(),
|
||||||
new UpdateFailedException(
|
new UpdateFailedException(
|
||||||
"Could not set owner of Task " + taskSummary.getTaskId() + "."));
|
String.format("Could not set owner of Task %s .", taskSummary.getTaskId())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return bulkLog;
|
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>>
|
private Pair<List<String>, BulkOperationResults<String, TaskanaException>>
|
||||||
filterForAuthorizedTasks(List<String> taskIds) {
|
filterForAuthorizedTasks(List<String> taskIds) {
|
||||||
BulkOperationResults<String, TaskanaException> bulkLog = new BulkOperationResults<>();
|
BulkOperationResults<String, TaskanaException> bulkLog = new BulkOperationResults<>();
|
||||||
|
|
@ -877,7 +885,7 @@ public class TaskServiceImpl implements TaskService {
|
||||||
bulkLog.addError(
|
bulkLog.addError(
|
||||||
taskId,
|
taskId,
|
||||||
new NotAuthorizedException(
|
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);
|
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.");
|
throw new InvalidStateException(TASK_WITH_ID + taskId + " has to be claimed before.");
|
||||||
} else if (!CurrentUserContext.getAccessIds().contains(task.getOwner())) {
|
} else if (!CurrentUserContext.getAccessIds().contains(task.getOwner())) {
|
||||||
throw new InvalidOwnerException(
|
throw new InvalidOwnerException(
|
||||||
"Owner of task "
|
String.format(
|
||||||
+ taskId
|
"Owner of task %s is %s, but current user is %s ",
|
||||||
+ " is "
|
taskId, task.getOwner(), userId));
|
||||||
+ task.getOwner()
|
|
||||||
+ ", but current User is "
|
|
||||||
+ userId);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// CLAIM-forced, if task was not already claimed before.
|
// 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> {
|
private static class PrioDurationHolder extends Pair<Duration, Integer> {
|
||||||
|
|
||||||
public PrioDurationHolder(Duration left, Integer right) {
|
PrioDurationHolder(Duration left, Integer right) {
|
||||||
super(left, right);
|
super(left, right);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -376,10 +376,11 @@ public class TaskTransferrer {
|
||||||
Workbasket destinationWorkbasket) {
|
Workbasket destinationWorkbasket) {
|
||||||
if (LOGGER.isDebugEnabled()) {
|
if (LOGGER.isDebugEnabled()) {
|
||||||
LOGGER.debug(
|
LOGGER.debug(
|
||||||
"entry to updateTasksToBeTransferred(taskIds = {}, taskSummaries = {})",
|
"entry to updateTasksToBeTransferred(taskIds = {}, taskSummaries = {}, "
|
||||||
|
+ "destinationWorkbasket = {})",
|
||||||
LoggerUtils.listToString(taskIds),
|
LoggerUtils.listToString(taskIds),
|
||||||
LoggerUtils.listToString(taskSummaries),
|
LoggerUtils.listToString(taskSummaries),
|
||||||
destinationWorkbasket);
|
destinationWorkbasket.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
taskSummaries =
|
taskSummaries =
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
package pro.taskana.task.internal.models;
|
package pro.taskana.task.internal.models;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
import pro.taskana.task.api.CallbackState;
|
import pro.taskana.task.api.CallbackState;
|
||||||
import pro.taskana.task.api.TaskState;
|
import pro.taskana.task.api.TaskState;
|
||||||
|
|
||||||
|
|
@ -9,8 +11,37 @@ public class MinimalTaskSummary {
|
||||||
private String taskId;
|
private String taskId;
|
||||||
private String externalId;
|
private String externalId;
|
||||||
private String workbasketId;
|
private String workbasketId;
|
||||||
|
private String classificationId;
|
||||||
private String owner;
|
private String owner;
|
||||||
private TaskState taskState;
|
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;
|
private CallbackState callbackState;
|
||||||
|
|
||||||
MinimalTaskSummary() {}
|
MinimalTaskSummary() {}
|
||||||
|
|
@ -39,6 +70,14 @@ public class MinimalTaskSummary {
|
||||||
this.workbasketId = workbasketKey;
|
this.workbasketId = workbasketKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getClassificationId() {
|
||||||
|
return classificationId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClassificationId(String classificationId) {
|
||||||
|
this.classificationId = classificationId;
|
||||||
|
}
|
||||||
|
|
||||||
public String getOwner() {
|
public String getOwner() {
|
||||||
return owner;
|
return owner;
|
||||||
}
|
}
|
||||||
|
|
@ -66,17 +105,25 @@ public class MinimalTaskSummary {
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "MinimalTaskSummary [taskId="
|
return "MinimalTaskSummary [taskId="
|
||||||
+ taskId
|
+ taskId
|
||||||
+ ", externalId="
|
+ ", externalId="
|
||||||
+ externalId
|
+ externalId
|
||||||
+ ", workbasketId="
|
+ ", workbasketId="
|
||||||
+ workbasketId
|
+ workbasketId
|
||||||
+ ", owner="
|
+ ", classificationId="
|
||||||
+ owner
|
+ classificationId
|
||||||
+ ", taskState="
|
+ ", owner="
|
||||||
+ taskState
|
+ owner
|
||||||
+ ", callbackState="
|
+ ", taskState="
|
||||||
+ callbackState
|
+ 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
|
@Test
|
||||||
void testThrowsExceptionIfTaskHasAlreadyBeenUpdated()
|
void testThrowsExceptionIfTaskHasAlreadyBeenUpdated()
|
||||||
throws NotAuthorizedException, InvalidArgumentException, ClassificationNotFoundException,
|
throws NotAuthorizedException, InvalidArgumentException, ClassificationNotFoundException,
|
||||||
TaskNotFoundException, ConcurrencyException, AttachmentPersistenceException,
|
TaskNotFoundException, ConcurrencyException, AttachmentPersistenceException,
|
||||||
InvalidStateException, InterruptedException {
|
InvalidStateException, InterruptedException {
|
||||||
|
|
||||||
TaskService taskService = taskanaEngine.getTaskService();
|
TaskService taskService = taskanaEngine.getTaskService();
|
||||||
Task task = taskService.getTask("TKI:000000000000000000000000000000000000");
|
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