TSK-1139 Bulk update of Planned timestamp

This commit is contained in:
BerndBreier 2020-02-24 07:32:48 +01:00
parent ec48d5ed88
commit 02a6df6f73
11 changed files with 810 additions and 142 deletions

View File

@ -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);
}

View File

@ -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>")

View File

@ -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;
}
}
}

View File

@ -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(

View File

@ -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);
}
}

View File

@ -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 =

View File

@ -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
+ "]";
}
}

View File

@ -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");
}
}

View File

@ -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();
}
}

View File

@ -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");

View File

@ -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();
}
}