TSK-1143 UpdateTask with Planned sometimes fails
This commit is contained in:
parent
630006de82
commit
1291287419
|
@ -39,7 +39,7 @@ public class TaskRefreshJob extends AbstractTaskanaJob {
|
|||
TaskServiceImpl taskService = (TaskServiceImpl) taskanaEngineImpl.getTaskService();
|
||||
for (String taskId : affectedTaskIds) {
|
||||
try {
|
||||
taskService.refreshPriorityAndDueDate(taskId);
|
||||
taskService.refreshPriorityAndDueDateOnClassificationUpdate(taskId);
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn(
|
||||
"Task {} could not be refreshed because of exception: {}", taskId, e.getMessage());
|
||||
|
|
|
@ -0,0 +1,264 @@
|
|||
package pro.taskana.task.internal;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.ibatis.exceptions.PersistenceException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import pro.taskana.classification.api.ClassificationService;
|
||||
import pro.taskana.classification.api.exceptions.ClassificationNotFoundException;
|
||||
import pro.taskana.classification.api.models.ClassificationSummary;
|
||||
import pro.taskana.common.api.BulkOperationResults;
|
||||
import pro.taskana.common.api.exceptions.InvalidArgumentException;
|
||||
import pro.taskana.common.internal.util.IdGenerator;
|
||||
import pro.taskana.task.api.exceptions.AttachmentPersistenceException;
|
||||
import pro.taskana.task.api.models.Attachment;
|
||||
import pro.taskana.task.api.models.AttachmentSummary;
|
||||
import pro.taskana.task.api.models.ObjectReference;
|
||||
import pro.taskana.task.api.models.Task;
|
||||
import pro.taskana.task.internal.models.AttachmentImpl;
|
||||
import pro.taskana.task.internal.models.TaskImpl;
|
||||
|
||||
public class AttachmentHandler {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AttachmentHandler.class);
|
||||
private static final String ID_PREFIX_ATTACHMENT = "TAI";
|
||||
private final AttachmentMapper attachmentMapper;
|
||||
private final ClassificationService classificationService;
|
||||
|
||||
AttachmentHandler(
|
||||
AttachmentMapper attachmentMapper, ClassificationService classificationService) {
|
||||
this.attachmentMapper = attachmentMapper;
|
||||
this.classificationService = classificationService;
|
||||
}
|
||||
|
||||
public List<Attachment> augmentAttachmentsByClassification(
|
||||
List<AttachmentImpl> attachmentImpls,
|
||||
BulkOperationResults<String, Exception> bulkLog) {
|
||||
LOGGER.debug("entry to augmentAttachmentsByClassification()");
|
||||
List<Attachment> result = new ArrayList<>();
|
||||
if (attachmentImpls == null || attachmentImpls.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
List<ClassificationSummary> classifications =
|
||||
classificationService
|
||||
.createClassificationQuery()
|
||||
.idIn(
|
||||
attachmentImpls.stream()
|
||||
.map(t -> t.getClassificationSummary().getId())
|
||||
.distinct()
|
||||
.toArray(String[]::new))
|
||||
.list();
|
||||
for (AttachmentImpl att : attachmentImpls) {
|
||||
ClassificationSummary classificationSummary =
|
||||
classifications.stream()
|
||||
.filter(cl -> cl.getId().equals(att.getClassificationSummary().getId()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (classificationSummary == null) {
|
||||
String id = att.getClassificationSummary().getId();
|
||||
bulkLog.addError(
|
||||
att.getClassificationSummary().getId(),
|
||||
new ClassificationNotFoundException(
|
||||
id,
|
||||
String.format(
|
||||
"When processing task updates due to change "
|
||||
+ "of classification, the classification with id %s was not found",
|
||||
id)));
|
||||
} else {
|
||||
att.setClassificationSummary(classificationSummary);
|
||||
result.add(att);
|
||||
}
|
||||
}
|
||||
|
||||
LOGGER.debug("exit from augmentAttachmentsByClassification()");
|
||||
return result;
|
||||
}
|
||||
|
||||
void insertAndDeleteAttachmentsOnTaskUpdate(TaskImpl newTaskImpl, TaskImpl oldTaskImpl)
|
||||
throws AttachmentPersistenceException {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(
|
||||
"entry to insertAndDeleteAttachmentsOnTaskUpdate(oldTaskImpl = {}, newTaskImpl = {})",
|
||||
oldTaskImpl,
|
||||
newTaskImpl);
|
||||
}
|
||||
List<Attachment> newAttachments =
|
||||
newTaskImpl.getAttachments().stream().filter(Objects::nonNull).collect(Collectors.toList());
|
||||
newTaskImpl.setAttachments(newAttachments);
|
||||
|
||||
deleteRemovedAttachmentsOnTaskUpdate(newTaskImpl, oldTaskImpl);
|
||||
insertNewAttachmentsOnTaskUpdate(newTaskImpl, oldTaskImpl);
|
||||
updateModifiedAttachmentsOnTaskUpdate(newTaskImpl, oldTaskImpl);
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(
|
||||
"exit from insertAndDeleteAttachmentsOnTaskUpdate(oldTaskImpl = {}, newTaskImpl = {})",
|
||||
oldTaskImpl,
|
||||
newTaskImpl);
|
||||
}
|
||||
}
|
||||
|
||||
void updateModifiedAttachmentsOnTaskUpdate(TaskImpl newTaskImpl, TaskImpl oldTaskImpl) {
|
||||
List<Attachment> newAttachments = newTaskImpl.getAttachments();
|
||||
List<Attachment> oldAttachments = oldTaskImpl.getAttachments();
|
||||
if (newAttachments != null
|
||||
&& !newAttachments.isEmpty()
|
||||
&& oldAttachments != null
|
||||
&& !oldAttachments.isEmpty()) {
|
||||
final Map<String, Attachment> oldAttachmentMap =
|
||||
oldAttachments.stream()
|
||||
.collect(Collectors.toMap(AttachmentSummary::getId, Function.identity()));
|
||||
newAttachments.forEach(
|
||||
a -> {
|
||||
if (oldAttachmentMap.containsKey(a.getId())
|
||||
&& !a.equals(oldAttachmentMap.get(a.getId()))) {
|
||||
attachmentMapper.update((AttachmentImpl) a);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void insertNewAttachmentsOnTaskUpdate(TaskImpl newTaskImpl, TaskImpl oldTaskImpl)
|
||||
throws AttachmentPersistenceException {
|
||||
List<String> oldAttachmentIds =
|
||||
oldTaskImpl.getAttachments().stream()
|
||||
.map(AttachmentSummary::getId)
|
||||
.collect(Collectors.toList());
|
||||
List<AttachmentPersistenceException> exceptions = new ArrayList<>();
|
||||
newTaskImpl
|
||||
.getAttachments()
|
||||
.forEach(
|
||||
a -> {
|
||||
if (!oldAttachmentIds.contains(a.getId())) {
|
||||
try {
|
||||
insertNewAttachmentOnTaskUpdate(newTaskImpl, a);
|
||||
} catch (AttachmentPersistenceException excpt) {
|
||||
exceptions.add(excpt);
|
||||
LOGGER.warn("attempted to insert attachment {} and caught exception", a, excpt);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!exceptions.isEmpty()) {
|
||||
throw exceptions.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
void insertNewAttachmentsOnTaskCreation(TaskImpl task, Instant now)
|
||||
throws InvalidArgumentException {
|
||||
List<Attachment> attachments = task.getAttachments();
|
||||
if (attachments != null) {
|
||||
for (Attachment attachment : attachments) {
|
||||
AttachmentImpl attachmentImpl = (AttachmentImpl) attachment;
|
||||
attachmentImpl.setId(IdGenerator.generateWithPrefix(ID_PREFIX_ATTACHMENT));
|
||||
attachmentImpl.setTaskId(task.getId());
|
||||
attachmentImpl.setCreated(now);
|
||||
attachmentImpl.setModified(now);
|
||||
ObjectReference objRef = attachmentImpl.getObjectReference();
|
||||
validateObjectReference(objRef, "ObjectReference", "Attachment");
|
||||
attachmentMapper.insert(attachmentImpl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void deleteRemovedAttachmentsOnTaskUpdate(TaskImpl newTaskImpl, TaskImpl oldTaskImpl) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(
|
||||
"entry to deleteRemovedAttachmentsOnTaskUpdate(oldTaskImpl = {}, newTaskImpl = {})",
|
||||
oldTaskImpl,
|
||||
newTaskImpl);
|
||||
}
|
||||
|
||||
final List<Attachment> newAttachments = newTaskImpl.getAttachments();
|
||||
List<String> newAttachmentIds = new ArrayList<>();
|
||||
if (newAttachments != null && !newAttachments.isEmpty()) {
|
||||
newAttachmentIds =
|
||||
newAttachments.stream().map(Attachment::getId).collect(Collectors.toList());
|
||||
}
|
||||
List<Attachment> oldAttachments = oldTaskImpl.getAttachments();
|
||||
if (oldAttachments != null && !oldAttachments.isEmpty()) {
|
||||
final List<String> newAttIds = newAttachmentIds;
|
||||
oldAttachments.forEach(
|
||||
a -> {
|
||||
if (!newAttIds.contains(a.getId())) {
|
||||
attachmentMapper.deleteAttachment(a.getId());
|
||||
LOGGER.debug(
|
||||
"TaskService.updateTask() for TaskId={} DELETED an Attachment={}.",
|
||||
newTaskImpl.getId(),
|
||||
a);
|
||||
}
|
||||
});
|
||||
}
|
||||
LOGGER.debug("exit from deleteRemovedAttachmentsOnTaskUpdate()");
|
||||
}
|
||||
|
||||
void insertNewAttachmentOnTaskUpdate(TaskImpl newTaskImpl, Attachment attachment)
|
||||
throws AttachmentPersistenceException {
|
||||
LOGGER.debug("entry to insertNewAttachmentOnTaskUpdate()");
|
||||
AttachmentImpl attachmentImpl = (AttachmentImpl) attachment;
|
||||
initAttachment(attachmentImpl, newTaskImpl);
|
||||
|
||||
try {
|
||||
attachmentMapper.insert(attachmentImpl);
|
||||
LOGGER.debug(
|
||||
"TaskService.updateTask() for TaskId={} INSERTED an Attachment={}.",
|
||||
newTaskImpl.getId(),
|
||||
attachmentImpl);
|
||||
} catch (PersistenceException e) {
|
||||
throw new AttachmentPersistenceException(
|
||||
"Cannot insert the Attachement "
|
||||
+ attachmentImpl.getId()
|
||||
+ " for Task "
|
||||
+ newTaskImpl.getId()
|
||||
+ " because it already exists.",
|
||||
e.getCause());
|
||||
}
|
||||
LOGGER.debug("exit from insertNewAttachmentOnTaskUpdate(), returning");
|
||||
}
|
||||
|
||||
void initAttachment(AttachmentImpl attachment, Task newTask) {
|
||||
LOGGER.debug("entry to initAttachment()");
|
||||
if (attachment.getId() == null) {
|
||||
attachment.setId(IdGenerator.generateWithPrefix(ID_PREFIX_ATTACHMENT));
|
||||
}
|
||||
if (attachment.getCreated() == null) {
|
||||
attachment.setCreated(Instant.now());
|
||||
}
|
||||
if (attachment.getModified() == null) {
|
||||
attachment.setModified(attachment.getCreated());
|
||||
}
|
||||
if (attachment.getTaskId() == null) {
|
||||
attachment.setTaskId(newTask.getId());
|
||||
}
|
||||
LOGGER.debug("exit from initAttachment()");
|
||||
}
|
||||
|
||||
void validateObjectReference(ObjectReference objRef, String objRefType, String objName)
|
||||
throws InvalidArgumentException {
|
||||
LOGGER.debug("entry to validateObjectReference()");
|
||||
// check that all values in the ObjectReference are set correctly
|
||||
if (objRef == null) {
|
||||
throw new InvalidArgumentException(objRefType + " of " + objName + " must not be null");
|
||||
} else if (objRef.getCompany() == null || objRef.getCompany().length() == 0) {
|
||||
throw new InvalidArgumentException(
|
||||
String.format("Company of %s of %s must not be empty", objRefType, objName));
|
||||
} else if (objRef.getSystem() == null || objRef.getSystem().length() == 0) {
|
||||
throw new InvalidArgumentException(
|
||||
String.format("System of %s of %s must not be empty", objRefType, objName));
|
||||
} else if (objRef.getSystemInstance() == null || objRef.getSystemInstance().length() == 0) {
|
||||
throw new InvalidArgumentException(
|
||||
String.format("SystemInstance of %s of %s must not be empty", objRefType, objName));
|
||||
} else if (objRef.getType() == null || objRef.getType().length() == 0) {
|
||||
throw new InvalidArgumentException(
|
||||
String.format("Type of %s of %s must not be empty", objRefType, objName));
|
||||
} else if (objRef.getValue() == null || objRef.getValue().length() == 0) {
|
||||
throw new InvalidArgumentException(
|
||||
String.format("Value of %s of %s must not be empty", objRefType, objName));
|
||||
}
|
||||
LOGGER.debug("exit from validateObjectReference()");
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ import java.time.Duration;
|
|||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -26,6 +27,8 @@ 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.api.models.Attachment;
|
||||
import pro.taskana.task.api.models.AttachmentSummary;
|
||||
import pro.taskana.task.internal.models.AttachmentSummaryImpl;
|
||||
import pro.taskana.task.internal.models.MinimalTaskSummary;
|
||||
import pro.taskana.task.internal.models.TaskImpl;
|
||||
|
@ -42,11 +45,10 @@ class ServiceLevelHandler {
|
|||
private final AttachmentMapper attachmentMapper;
|
||||
private DaysToWorkingDaysConverter converter;
|
||||
|
||||
public ServiceLevelHandler(
|
||||
ServiceLevelHandler(
|
||||
InternalTaskanaEngine taskanaEngine,
|
||||
TaskMapper taskMapper,
|
||||
AttachmentMapper attachmentMapper) {
|
||||
super();
|
||||
this.taskanaEngine = taskanaEngine;
|
||||
this.taskMapper = taskMapper;
|
||||
this.attachmentMapper = attachmentMapper;
|
||||
|
@ -66,13 +68,12 @@ class ServiceLevelHandler {
|
|||
// - 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<>();
|
||||
public BulkLog setPlannedPropertyOfTasksImpl(Instant planned, List<String> argTaskIds) {
|
||||
BulkLog bulkLog = new BulkLog();
|
||||
if (argTaskIds == null || argTaskIds.isEmpty()) {
|
||||
return bulkLog;
|
||||
}
|
||||
Pair<List<MinimalTaskSummary>, BulkOperationResults<String, TaskanaException>> resultsPair =
|
||||
Pair<List<MinimalTaskSummary>, BulkLog> resultsPair =
|
||||
filterTasksForExistenceAndAuthorization(argTaskIds);
|
||||
List<MinimalTaskSummary> existingTasksAuthorizedFor = resultsPair.getLeft();
|
||||
bulkLog.addAllErrors(resultsPair.getRight());
|
||||
|
@ -85,29 +86,172 @@ class ServiceLevelHandler {
|
|||
Map<Duration, List<TaskDuration>> tasksPerDuration =
|
||||
getTasksPerDuration(
|
||||
existingTasksAuthorizedFor, attachments, allInvolvedClassificationsWithDuration);
|
||||
bulkLog.addAllErrors(updateAffectedTasks(planned, tasksPerDuration));
|
||||
BulkLog updateResult = updatePlannedPropertyOfAffectedTasks(planned, tasksPerDuration);
|
||||
bulkLog.addAllErrors(updateResult);
|
||||
|
||||
return bulkLog;
|
||||
}
|
||||
|
||||
BulkOperationResults<String, TaskanaException> addExceptionsForNonExistingTasks(
|
||||
BulkLog addExceptionsForNonExistingTasksToBulkLog(
|
||||
List<String> requestTaskIds, List<MinimalTaskSummary> existingMinimalTaskSummaries) {
|
||||
BulkOperationResults<String, TaskanaException> bulkLog = new BulkOperationResults<>();
|
||||
BulkLog bulkLog = new BulkLog();
|
||||
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"));
|
||||
}
|
||||
nonExistingTaskIds.forEach(
|
||||
taskId ->
|
||||
bulkLog.addError(taskId, new TaskNotFoundException(taskId, "Task was not found")));
|
||||
return bulkLog;
|
||||
}
|
||||
|
||||
private BulkOperationResults<String, TaskanaException> updateAffectedTasks(
|
||||
TaskImpl updatePrioPlannedDueOfTask(
|
||||
TaskImpl newTaskImpl, TaskImpl oldTaskImpl, boolean forRefreshOnClassificationUpdate)
|
||||
throws InvalidArgumentException {
|
||||
boolean onlyPriority = false;
|
||||
if (newTaskImpl.getClassificationSummary() == null
|
||||
|| newTaskImpl.getClassificationSummary().getServiceLevel() == null) {
|
||||
onlyPriority = true;
|
||||
}
|
||||
|
||||
if (isPriorityAndDurationAlreadyCorrect(newTaskImpl, oldTaskImpl)) {
|
||||
return newTaskImpl;
|
||||
}
|
||||
|
||||
if (newTaskImpl.getPlanned() == null && newTaskImpl.getDue() == null) {
|
||||
newTaskImpl.setPlanned(Instant.now());
|
||||
}
|
||||
|
||||
DurationPrioHolder durationPrioHolder = determineTaskPrioDuration(newTaskImpl, onlyPriority);
|
||||
newTaskImpl.setPriority(durationPrioHolder.getPriority());
|
||||
if (onlyPriority) {
|
||||
return newTaskImpl;
|
||||
}
|
||||
// classification update
|
||||
if (forRefreshOnClassificationUpdate) {
|
||||
newTaskImpl.setDue(newPlannedDueInstant(newTaskImpl, durationPrioHolder.getDuration(), true));
|
||||
return newTaskImpl;
|
||||
}
|
||||
// creation of new task
|
||||
if (oldTaskImpl == null) {
|
||||
return updatePlannedDueOnCreationOfNewTask(newTaskImpl, durationPrioHolder);
|
||||
} else {
|
||||
return updatePlannedDueOnTaskUpdate(newTaskImpl, oldTaskImpl, durationPrioHolder);
|
||||
}
|
||||
}
|
||||
|
||||
Instant newPlannedDueInstant(TaskImpl task, Duration duration, boolean fromPlannedToDue) {
|
||||
if (fromPlannedToDue) {
|
||||
long days = converter.convertWorkingDaysToDays(task.getPlanned(), duration.toDays());
|
||||
return task.getPlanned().plus(Duration.ofDays(days));
|
||||
} else {
|
||||
long days = converter.convertWorkingDaysToDays(task.getDue(), -duration.toDays());
|
||||
return task.getDue().plus(Duration.ofDays(days));
|
||||
}
|
||||
}
|
||||
|
||||
DurationPrioHolder determineTaskPrioDuration(TaskImpl newTaskImpl, boolean onlyPriority) {
|
||||
Set<ClassificationSummary> classificationsInvolved = getInvolvedClassifications(newTaskImpl);
|
||||
|
||||
List<ClassificationWithServiceLevelResolved> resolvedClassifications = new ArrayList<>();
|
||||
if (onlyPriority) {
|
||||
for (ClassificationSummary c : classificationsInvolved) {
|
||||
resolvedClassifications.add(
|
||||
new ClassificationWithServiceLevelResolved(c.getId(), MAX_DURATION, 0));
|
||||
}
|
||||
} else {
|
||||
resolvedClassifications =
|
||||
resolveDurationsInClassifications(new ArrayList<>(classificationsInvolved));
|
||||
}
|
||||
|
||||
return getFinalPrioDurationOfTask(resolvedClassifications, onlyPriority);
|
||||
}
|
||||
|
||||
Pair<List<MinimalTaskSummary>, BulkLog> filterTasksForExistenceAndAuthorization(
|
||||
List<String> argTaskIds) {
|
||||
BulkLog bulkLog = new BulkLog();
|
||||
// remove duplicates
|
||||
List<String> taskIds = argTaskIds.stream().distinct().collect(Collectors.toList());
|
||||
// get existing tasks
|
||||
List<MinimalTaskSummary> minimalTaskSummaries = taskMapper.findExistingTasks(taskIds, null);
|
||||
bulkLog.addAllErrors(addExceptionsForNonExistingTasksToBulkLog(taskIds, minimalTaskSummaries));
|
||||
Pair<List<MinimalTaskSummary>, BulkLog> filteredPair =
|
||||
filterTasksAuthorizedForAndLogErrorsForNotAuthorized(minimalTaskSummaries);
|
||||
bulkLog.addAllErrors(filteredPair.getRight());
|
||||
return new Pair<>(filteredPair.getLeft(), bulkLog);
|
||||
}
|
||||
|
||||
Pair<List<MinimalTaskSummary>, BulkLog> filterTasksAuthorizedForAndLogErrorsForNotAuthorized(
|
||||
List<MinimalTaskSummary> existingTasks) {
|
||||
BulkLog bulkLog = new BulkLog();
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
private TaskImpl updatePlannedDueOnTaskUpdate(
|
||||
TaskImpl newTaskImpl, TaskImpl oldTaskImpl, DurationPrioHolder durationPrioHolder)
|
||||
throws InvalidArgumentException {
|
||||
// case 1: no change of planned / due, but change of an attachment or classification
|
||||
if (newTaskImpl.getDue().equals(oldTaskImpl.getDue())
|
||||
&& newTaskImpl.getPlanned().equals(oldTaskImpl.getPlanned())) {
|
||||
newTaskImpl.setDue(newPlannedDueInstant(newTaskImpl, durationPrioHolder.getDuration(), true));
|
||||
} else if ((newTaskImpl.getDue().equals(oldTaskImpl.getDue()))) {
|
||||
// case 2: planned was changed
|
||||
newTaskImpl.setDue(newPlannedDueInstant(newTaskImpl, durationPrioHolder.getDuration(), true));
|
||||
} else { // case 3: due was changed
|
||||
Instant planned = newPlannedDueInstant(newTaskImpl, durationPrioHolder.getDuration(), false);
|
||||
if (newTaskImpl.getPlanned() != null && !planned.equals(newTaskImpl.getPlanned())) {
|
||||
throw new InvalidArgumentException(
|
||||
"Cannot update a task with given planned "
|
||||
+ "and due date not matching the service level");
|
||||
}
|
||||
newTaskImpl.setPlanned(planned);
|
||||
}
|
||||
return newTaskImpl;
|
||||
}
|
||||
|
||||
private TaskImpl updatePlannedDueOnCreationOfNewTask(
|
||||
TaskImpl newTaskImpl, DurationPrioHolder durationPrioHolder) throws InvalidArgumentException {
|
||||
if (newTaskImpl.getDue() != null) { // due is specified: calculate back and check correctnes
|
||||
Instant planned = newPlannedDueInstant(newTaskImpl, durationPrioHolder.getDuration(), false);
|
||||
if (newTaskImpl.getPlanned() != null && !planned.equals(newTaskImpl.getPlanned())) {
|
||||
throw new InvalidArgumentException(
|
||||
"Cannot create a task with given planned "
|
||||
+ "and due date not matching the service level");
|
||||
}
|
||||
newTaskImpl.setPlanned(planned);
|
||||
} else { // task.due is null: calculate forward from planned
|
||||
newTaskImpl.setDue(newPlannedDueInstant(newTaskImpl, durationPrioHolder.getDuration(), true));
|
||||
}
|
||||
return newTaskImpl;
|
||||
}
|
||||
|
||||
private BulkLog updatePlannedPropertyOfAffectedTasks(
|
||||
Instant planned, Map<Duration, List<TaskDuration>> tasksPerDuration) {
|
||||
BulkOperationResults<String, TaskanaException> bulkLog = new BulkOperationResults<>();
|
||||
BulkLog bulkLog = new BulkLog();
|
||||
TaskImpl referenceTask = new TaskImpl();
|
||||
referenceTask.setPlanned(planned);
|
||||
for (Map.Entry<Duration, List<TaskDuration>> entry : tasksPerDuration.entrySet()) {
|
||||
|
@ -119,32 +263,18 @@ class ServiceLevelHandler {
|
|||
referenceTask.setModified(Instant.now());
|
||||
long numTasksUpdated = taskMapper.updateTaskDueDates(taskIdsToUpdate, referenceTask);
|
||||
if (numTasksUpdated != taskIdsToUpdate.size()) {
|
||||
bulkLog.addAllErrors(
|
||||
BulkLog checkResult =
|
||||
checkResultsOfTasksUpdateAndAddErrorsToBulkLog(
|
||||
taskIdsToUpdate, referenceTask, numTasksUpdated));
|
||||
taskIdsToUpdate, referenceTask, numTasksUpdated);
|
||||
bulkLog.addAllErrors(checkResult);
|
||||
}
|
||||
}
|
||||
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<>();
|
||||
private BulkLog checkResultsOfTasksUpdateAndAddErrorsToBulkLog(
|
||||
List<String> taskIdsToUpdate, TaskImpl referenceTask, long numTasksUpdated) {
|
||||
BulkLog bulkLog = new BulkLog();
|
||||
long numErrors = taskIdsToUpdate.size() - numTasksUpdated;
|
||||
long numErrorsLogged = 0;
|
||||
if (numErrors > 0) {
|
||||
|
@ -162,7 +292,7 @@ class ServiceLevelHandler {
|
|||
if (numErrorsNotLogged != 0) {
|
||||
for (int i = 1; i <= numErrorsNotLogged; i++) {
|
||||
bulkLog.addError(
|
||||
String.format("UnknownTaskId%s", i),
|
||||
String.format("UnknownTaskId%s", Integer.valueOf(i)),
|
||||
new UpdateFailedException("Update of unknown task failed"));
|
||||
}
|
||||
}
|
||||
|
@ -230,7 +360,9 @@ class ServiceLevelHandler {
|
|||
List<ClassificationWithServiceLevelResolved> result = new ArrayList<>();
|
||||
for (ClassificationSummary classification : allInvolvedClassifications) {
|
||||
Duration serviceLevel = Duration.parse(classification.getServiceLevel());
|
||||
result.add(new ClassificationWithServiceLevelResolved(classification.getId(), serviceLevel));
|
||||
result.add(
|
||||
new ClassificationWithServiceLevelResolved(
|
||||
classification.getId(), serviceLevel, classification.getPriority()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -245,11 +377,9 @@ class ServiceLevelHandler {
|
|||
String[] taskIdsAuthorizedForArray = new String[existingTaskIdsAuthorizedFor.size()];
|
||||
taskIdsAuthorizedForArray = existingTaskIdsAuthorizedFor.toArray(taskIdsAuthorizedForArray);
|
||||
|
||||
if (existingTaskIdsAuthorizedFor.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
} else {
|
||||
return attachmentMapper.findAttachmentSummariesByTaskIds(taskIdsAuthorizedForArray);
|
||||
}
|
||||
return existingTaskIdsAuthorizedFor.isEmpty()
|
||||
? new ArrayList<>()
|
||||
: attachmentMapper.findAttachmentSummariesByTaskIds(taskIdsAuthorizedForArray);
|
||||
}
|
||||
|
||||
private List<ClassificationSummary> findAllInvolvedClassifications(
|
||||
|
@ -279,41 +409,92 @@ class ServiceLevelHandler {
|
|||
}
|
||||
}
|
||||
|
||||
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));
|
||||
private DurationPrioHolder getFinalPrioDurationOfTask(
|
||||
List<ClassificationWithServiceLevelResolved> cl, boolean onlyPriority) {
|
||||
Duration duration = MAX_DURATION;
|
||||
int priority = Integer.MIN_VALUE;
|
||||
for (ClassificationWithServiceLevelResolved classification : cl) {
|
||||
Duration actualDuration = classification.getDurationFromClassification();
|
||||
if (!onlyPriority && duration.compareTo(actualDuration) > 0) {
|
||||
duration = actualDuration;
|
||||
}
|
||||
taskIds.removeAll(taskIdsNotAuthorizedFor);
|
||||
List<MinimalTaskSummary> tasksAuthorizedFor =
|
||||
existingTasks.stream()
|
||||
.filter(t -> taskIds.contains(t.getTaskId()))
|
||||
.collect(Collectors.toList());
|
||||
return new Pair<>(tasksAuthorizedFor, bulkLog);
|
||||
if (classification.getPriority() > priority) {
|
||||
priority = classification.getPriority();
|
||||
}
|
||||
}
|
||||
return new DurationPrioHolder(duration, priority);
|
||||
}
|
||||
|
||||
private Set<ClassificationSummary> getInvolvedClassifications(TaskImpl taskImpl) {
|
||||
Set<ClassificationSummary> classifications =
|
||||
taskImpl.getAttachments() == null
|
||||
? new HashSet<>()
|
||||
: taskImpl.getAttachments().stream()
|
||||
.map(Attachment::getClassificationSummary)
|
||||
.collect(Collectors.toSet());
|
||||
classifications.add(taskImpl.getClassificationSummary());
|
||||
return classifications;
|
||||
}
|
||||
|
||||
private boolean isPriorityAndDurationAlreadyCorrect(TaskImpl newTaskImpl, TaskImpl oldTaskImpl) {
|
||||
if (oldTaskImpl != null) {
|
||||
final boolean isClassificationKeyChanged =
|
||||
newTaskImpl.getClassificationKey() != null
|
||||
&& (oldTaskImpl.getClassificationKey() == null
|
||||
|| !newTaskImpl
|
||||
.getClassificationKey()
|
||||
.equals(oldTaskImpl.getClassificationKey()));
|
||||
|
||||
final boolean isClassificationIdChanged =
|
||||
newTaskImpl.getClassificationId() != null
|
||||
&& (oldTaskImpl.getClassificationId() == null
|
||||
|| !newTaskImpl.getClassificationId().equals(oldTaskImpl.getClassificationId()));
|
||||
|
||||
return oldTaskImpl.getPlanned().equals(newTaskImpl.getPlanned())
|
||||
&& oldTaskImpl.getDue().equals(newTaskImpl.getDue())
|
||||
&& !isClassificationKeyChanged
|
||||
&& !isClassificationIdChanged
|
||||
&& areAttachmentsUnchanged(newTaskImpl, oldTaskImpl);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static class ClassificationWithServiceLevelResolved {
|
||||
private String classificationId;
|
||||
private Duration duration;
|
||||
private boolean areAttachmentsUnchanged(TaskImpl newTaskImpl, TaskImpl oldTaskImpl) {
|
||||
List<String> oldAttachmentIds =
|
||||
oldTaskImpl.getAttachments().stream()
|
||||
.map(AttachmentSummary::getId)
|
||||
.collect(Collectors.toList());
|
||||
List<String> newAttachmentIds =
|
||||
newTaskImpl.getAttachments().stream()
|
||||
.map(AttachmentSummary::getId)
|
||||
.collect(Collectors.toList());
|
||||
Set<String> oldClassificationIds =
|
||||
oldTaskImpl.getAttachments().stream()
|
||||
.map(Attachment::getClassificationSummary)
|
||||
.map(ClassificationSummary::getId)
|
||||
.collect(Collectors.toSet());
|
||||
Set<String> newClassificationIds =
|
||||
newTaskImpl.getAttachments().stream()
|
||||
.map(Attachment::getClassificationSummary)
|
||||
.map(ClassificationSummary::getId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
ClassificationWithServiceLevelResolved(String id, Duration serviceLevel) {
|
||||
return oldAttachmentIds.size() == newAttachmentIds.size()
|
||||
&& newAttachmentIds.containsAll(oldAttachmentIds)
|
||||
&& oldClassificationIds.size() == newClassificationIds.size()
|
||||
&& newClassificationIds.containsAll(oldClassificationIds);
|
||||
}
|
||||
|
||||
static class ClassificationWithServiceLevelResolved {
|
||||
private final int priority;
|
||||
private final String classificationId;
|
||||
private final Duration duration;
|
||||
|
||||
ClassificationWithServiceLevelResolved(String id, Duration serviceLevel, int priority) {
|
||||
this.classificationId = id;
|
||||
this.duration = serviceLevel;
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
String getClassificationId() {
|
||||
|
@ -323,12 +504,16 @@ class ServiceLevelHandler {
|
|||
Duration getDurationFromClassification() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
int getPriority() {
|
||||
return priority;
|
||||
}
|
||||
}
|
||||
|
||||
static class TaskDuration {
|
||||
|
||||
private String taskId;
|
||||
private Duration duration;
|
||||
private final String taskId;
|
||||
private final Duration duration;
|
||||
|
||||
TaskDuration(String id, Duration serviceLevel) {
|
||||
this.taskId = id;
|
||||
|
@ -343,4 +528,28 @@ class ServiceLevelHandler {
|
|||
return duration;
|
||||
}
|
||||
}
|
||||
|
||||
static class BulkLog extends BulkOperationResults<String, TaskanaException> {
|
||||
BulkLog() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
static class DurationPrioHolder {
|
||||
private Duration duration;
|
||||
private int priority;
|
||||
|
||||
DurationPrioHolder(Duration duration, int priority) {
|
||||
this.duration = duration;
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
Duration getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
int getPriority() {
|
||||
return priority;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package pro.taskana.task.internal;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
@ -31,7 +30,6 @@ import pro.taskana.common.api.exceptions.TaskanaException;
|
|||
import pro.taskana.common.internal.CustomPropertySelector;
|
||||
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.IdGenerator;
|
||||
import pro.taskana.common.internal.util.Pair;
|
||||
import pro.taskana.spi.history.api.events.task.ClaimCancelledEvent;
|
||||
|
@ -55,6 +53,7 @@ import pro.taskana.task.api.models.ObjectReference;
|
|||
import pro.taskana.task.api.models.Task;
|
||||
import pro.taskana.task.api.models.TaskComment;
|
||||
import pro.taskana.task.api.models.TaskSummary;
|
||||
import pro.taskana.task.internal.ServiceLevelHandler.BulkLog;
|
||||
import pro.taskana.task.internal.models.AttachmentImpl;
|
||||
import pro.taskana.task.internal.models.AttachmentSummaryImpl;
|
||||
import pro.taskana.task.internal.models.MinimalTaskSummary;
|
||||
|
@ -86,15 +85,12 @@ public class TaskServiceImpl implements TaskService {
|
|||
private static final String THE_WORKBASKET = "The workbasket ";
|
||||
private static final String TASK = "Task";
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(TaskServiceImpl.class);
|
||||
private static final String ID_PREFIX_ATTACHMENT = "TAI";
|
||||
private static final String ID_PREFIX_TASK = "TKI";
|
||||
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 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;
|
||||
|
@ -104,7 +100,8 @@ public class TaskServiceImpl implements TaskService {
|
|||
private HistoryEventProducer historyEventProducer;
|
||||
private TaskTransferrer taskTransferrer;
|
||||
private TaskCommentServiceImpl taskCommentService;
|
||||
private ServiceLevelHandler serviceLevelHander;
|
||||
private ServiceLevelHandler serviceLevelHandler;
|
||||
private AttachmentHandler attachmentHandler;
|
||||
|
||||
public TaskServiceImpl(
|
||||
InternalTaskanaEngine taskanaEngine,
|
||||
|
@ -112,12 +109,6 @@ public class TaskServiceImpl implements TaskService {
|
|||
TaskCommentMapper taskCommentMapper,
|
||||
AttachmentMapper attachmentMapper) {
|
||||
super();
|
||||
try {
|
||||
this.converter = DaysToWorkingDaysConverter.initialize();
|
||||
} catch (InvalidArgumentException e) {
|
||||
throw new SystemException(
|
||||
"Internal error. Cannot initialize DaysToWorkingDaysConverter", e.getCause());
|
||||
}
|
||||
this.taskanaEngine = taskanaEngine;
|
||||
this.taskMapper = taskMapper;
|
||||
this.workbasketService = taskanaEngine.getEngine().getWorkbasketService();
|
||||
|
@ -126,7 +117,8 @@ public class TaskServiceImpl implements TaskService {
|
|||
this.historyEventProducer = taskanaEngine.getHistoryEventProducer();
|
||||
this.taskTransferrer = new TaskTransferrer(taskanaEngine, taskMapper, this);
|
||||
this.taskCommentService = new TaskCommentServiceImpl(taskanaEngine, taskCommentMapper, this);
|
||||
this.serviceLevelHander = new ServiceLevelHandler(taskanaEngine, taskMapper, attachmentMapper);
|
||||
this.serviceLevelHandler = new ServiceLevelHandler(taskanaEngine, taskMapper, attachmentMapper);
|
||||
this.attachmentHandler = new AttachmentHandler(attachmentMapper, classificationService);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -223,9 +215,9 @@ public class TaskServiceImpl implements TaskService {
|
|||
Classification classification =
|
||||
this.classificationService.getClassification(classificationKey, workbasket.getDomain());
|
||||
task.setClassificationSummary(classification.asSummary());
|
||||
validateObjectReference(task.getPrimaryObjRef(), "primary ObjectReference", TASK);
|
||||
PrioDurationHolder prioDurationFromAttachments = handleAttachments(task);
|
||||
standardSettings(task, classification, prioDurationFromAttachments);
|
||||
attachmentHandler.validateObjectReference(
|
||||
task.getPrimaryObjRef(), "primary ObjectReference", TASK);
|
||||
standardSettings(task, classification);
|
||||
setCallbackStateOnTaskCreation(task);
|
||||
try {
|
||||
this.taskMapper.insert(task);
|
||||
|
@ -409,8 +401,8 @@ public class TaskServiceImpl implements TaskService {
|
|||
@Override
|
||||
public Task updateTask(Task task)
|
||||
throws InvalidArgumentException, TaskNotFoundException, ConcurrencyException,
|
||||
ClassificationNotFoundException, NotAuthorizedException, AttachmentPersistenceException,
|
||||
InvalidStateException {
|
||||
NotAuthorizedException, AttachmentPersistenceException, InvalidStateException,
|
||||
ClassificationNotFoundException {
|
||||
String userId = CurrentUserContext.getUserid();
|
||||
LOGGER.debug("entry to updateTask(task = {}, userId = {})", task, userId);
|
||||
TaskImpl newTaskImpl = (TaskImpl) task;
|
||||
|
@ -418,9 +410,7 @@ public class TaskServiceImpl implements TaskService {
|
|||
try {
|
||||
taskanaEngine.openConnection();
|
||||
oldTaskImpl = (TaskImpl) getTask(newTaskImpl.getId());
|
||||
PrioDurationHolder prioDurationFromAttachments =
|
||||
handleAttachmentsOnTaskUpdate(oldTaskImpl, newTaskImpl);
|
||||
standardUpdateActions(oldTaskImpl, newTaskImpl, prioDurationFromAttachments);
|
||||
standardUpdateActions(oldTaskImpl, newTaskImpl);
|
||||
|
||||
taskMapper.update(newTaskImpl);
|
||||
LOGGER.debug("Method updateTask() updated task '{}' for user '{}'.", task.getId(), userId);
|
||||
|
@ -532,7 +522,8 @@ public class TaskServiceImpl implements TaskService {
|
|||
selectionCriteria,
|
||||
customFieldsToUpdate);
|
||||
}
|
||||
validateObjectReference(selectionCriteria, "ObjectReference", "updateTasks call");
|
||||
attachmentHandler.validateObjectReference(
|
||||
selectionCriteria, "ObjectReference", "updateTasks call");
|
||||
validateCustomFields(customFieldsToUpdate);
|
||||
CustomPropertySelector fieldSelector = new CustomPropertySelector();
|
||||
TaskImpl updated = initUpdatedTask(customFieldsToUpdate, fieldSelector);
|
||||
|
@ -689,10 +680,14 @@ public class TaskServiceImpl implements TaskService {
|
|||
try {
|
||||
taskanaEngine.openConnection();
|
||||
// use only elements we are authorized for
|
||||
Pair<List<String>, BulkOperationResults<String, TaskanaException>> resultsPair =
|
||||
filterForAuthorizedTasks(taskIds);
|
||||
Pair<List<MinimalTaskSummary>, BulkLog> resultsPair =
|
||||
serviceLevelHandler.filterTasksForExistenceAndAuthorization(taskIds);
|
||||
// set the Owner of these tasks we are authorized for
|
||||
taskIds = resultsPair.getLeft();
|
||||
List<MinimalTaskSummary> existingMinimalTaskSummaries = resultsPair.getLeft();
|
||||
taskIds =
|
||||
existingMinimalTaskSummaries.stream()
|
||||
.map(MinimalTaskSummary::getTaskId)
|
||||
.collect(Collectors.toList());
|
||||
bulkLog.addAllErrors(resultsPair.getRight());
|
||||
if (taskIds.isEmpty()) {
|
||||
return bulkLog;
|
||||
|
@ -702,13 +697,7 @@ public class TaskServiceImpl implements TaskService {
|
|||
return bulkLog;
|
||||
} else {
|
||||
// check the outcome
|
||||
List<MinimalTaskSummary> existingMinimalTaskSummaries =
|
||||
taskMapper.findExistingTasks(taskIds, null);
|
||||
// add exceptions for non existing tasks
|
||||
bulkLog.addAllErrors(
|
||||
serviceLevelHander.addExceptionsForNonExistingTasks(
|
||||
taskIds, existingMinimalTaskSummaries));
|
||||
// add exceptions of all remaining tasks whose owners were not set
|
||||
existingMinimalTaskSummaries = taskMapper.findExistingTasks(taskIds, null);
|
||||
bulkLog.addAllErrors(
|
||||
addExceptionsForTasksWhoseOwnerWasNotSet(owner, existingMinimalTaskSummaries));
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
|
@ -739,7 +728,7 @@ public class TaskServiceImpl implements TaskService {
|
|||
}
|
||||
try {
|
||||
taskanaEngine.openConnection();
|
||||
return serviceLevelHander.setPlannedPropertyOfTasksImpl(planned, argTaskIds);
|
||||
return serviceLevelHandler.setPlannedPropertyOfTasksImpl(planned, argTaskIds);
|
||||
} finally {
|
||||
LOGGER.debug("exit from setPlannedPropertyOfTasks");
|
||||
taskanaEngine.returnConnection();
|
||||
|
@ -780,7 +769,8 @@ public class TaskServiceImpl implements TaskService {
|
|||
return affectedTaskIds;
|
||||
}
|
||||
|
||||
public void refreshPriorityAndDueDate(String taskId) throws ClassificationNotFoundException {
|
||||
public void refreshPriorityAndDueDateOnClassificationUpdate(String taskId)
|
||||
throws ClassificationNotFoundException {
|
||||
LOGGER.debug("entry to refreshPriorityAndDueDate(taskId = {})", taskId);
|
||||
TaskImpl task;
|
||||
BulkOperationResults<String, Exception> bulkLog = new BulkOperationResults<>();
|
||||
|
@ -796,19 +786,21 @@ public class TaskServiceImpl implements TaskService {
|
|||
if (attachmentImpls == null) {
|
||||
attachmentImpls = new ArrayList<>();
|
||||
}
|
||||
List<Attachment> attachments = augmentAttachmentsByClassification(attachmentImpls, bulkLog);
|
||||
List<Attachment> attachments =
|
||||
attachmentHandler.augmentAttachmentsByClassification(attachmentImpls, bulkLog);
|
||||
task.setAttachments(attachments);
|
||||
|
||||
Classification classification =
|
||||
classificationService.getClassification(task.getClassificationSummary().getId());
|
||||
task.setClassificationSummary(classification.asSummary());
|
||||
PrioDurationHolder prioDurationFromAttachments =
|
||||
handleAttachmentsOnClassificationUpdate(task);
|
||||
|
||||
updatePrioDueDateOnClassificationUpdate(task, prioDurationFromAttachments);
|
||||
ClassificationSummary classificationSummary =
|
||||
classificationService
|
||||
.getClassification(task.getClassificationKey(), task.getDomain())
|
||||
.asSummary();
|
||||
task.setClassificationSummary(classificationSummary);
|
||||
task = serviceLevelHandler.updatePrioPlannedDueOfTask(task, null, true);
|
||||
|
||||
task.setModified(Instant.now());
|
||||
taskMapper.update(task);
|
||||
} catch (InvalidArgumentException e) {
|
||||
LOGGER.error("Internal System error. This situation should not happen. Caught exceptio ", e);
|
||||
} finally {
|
||||
taskanaEngine.returnConnection();
|
||||
LOGGER.debug("exit from refreshPriorityAndDueDate(). ");
|
||||
|
@ -895,7 +887,7 @@ public class TaskServiceImpl implements TaskService {
|
|||
|
||||
for (MinimalTaskSummary taskSummary : existingMinimalTaskSummaries) {
|
||||
if (!owner.equals(taskSummary.getOwner())) { // owner was not set
|
||||
if (!taskSummary.getTaskState().equals(TaskState.READY)) { // due to invalid state
|
||||
if (!TaskState.READY.equals(taskSummary.getTaskState())) { // due to invalid state
|
||||
bulkLog.addError(
|
||||
taskSummary.getTaskId(),
|
||||
new InvalidStateException(
|
||||
|
@ -914,27 +906,6 @@ public class TaskServiceImpl implements TaskService {
|
|||
return bulkLog;
|
||||
}
|
||||
|
||||
private Pair<List<String>, BulkOperationResults<String, TaskanaException>>
|
||||
filterForAuthorizedTasks(List<String> taskIds) {
|
||||
BulkOperationResults<String, TaskanaException> bulkLog = new BulkOperationResults<>();
|
||||
List<String> accessIds = CurrentUserContext.getAccessIds();
|
||||
List<String> tasksAuthorizedFor = new ArrayList<>(taskIds);
|
||||
// check authorization only for non-admin users
|
||||
if (!taskanaEngine.getEngine().isUserInRole(TaskanaRole.ADMIN)) {
|
||||
List<String> tasksNotAuthorizedFor =
|
||||
taskMapper.filterTaskIdsNotAuthorizedFor(taskIds, accessIds);
|
||||
tasksAuthorizedFor.removeAll(tasksNotAuthorizedFor);
|
||||
String currentUserId = CurrentUserContext.getUserid();
|
||||
for (String taskId : tasksNotAuthorizedFor) {
|
||||
bulkLog.addError(
|
||||
taskId,
|
||||
new NotAuthorizedException(
|
||||
String.format("Current user not authorized for task %s.", taskId), currentUserId));
|
||||
}
|
||||
}
|
||||
return new Pair<>(tasksAuthorizedFor, bulkLog);
|
||||
}
|
||||
|
||||
private Task claim(String taskId, boolean forceClaim)
|
||||
throws TaskNotFoundException, InvalidStateException, InvalidOwnerException,
|
||||
NotAuthorizedException {
|
||||
|
@ -1178,8 +1149,7 @@ public class TaskServiceImpl implements TaskService {
|
|||
}
|
||||
}
|
||||
|
||||
private void standardSettings(
|
||||
TaskImpl task, Classification classification, PrioDurationHolder prioDurationFromAttachments)
|
||||
private void standardSettings(TaskImpl task, Classification classification)
|
||||
throws InvalidArgumentException {
|
||||
LOGGER.debug("entry to standardSettings()");
|
||||
final Instant now = Instant.now();
|
||||
|
@ -1206,40 +1176,11 @@ public class TaskServiceImpl implements TaskService {
|
|||
}
|
||||
|
||||
// null in case of manual tasks
|
||||
if (classification == null) {
|
||||
if (task.getPlanned() == null) {
|
||||
task.setPlanned(now);
|
||||
}
|
||||
} else {
|
||||
// do some Classification specific stuff (servicelevel).
|
||||
// get duration in days from planned to due
|
||||
PrioDurationHolder finalPrioDuration =
|
||||
getNewPrioDuration(
|
||||
prioDurationFromAttachments,
|
||||
classification.getPriority(),
|
||||
classification.getServiceLevel());
|
||||
Duration finalDuration = finalPrioDuration.getLeft();
|
||||
if (finalDuration != null && !MAX_DURATION.equals(finalDuration)) {
|
||||
// if we have a due date we need to go x days backwards,
|
||||
// else we take the planned date (or now as fallback) and add x Days
|
||||
if (task.getDue() != null) {
|
||||
long days = converter.convertWorkingDaysToDays(task.getDue(), -finalDuration.toDays());
|
||||
// days < 0 -> so we ne need to add, not substract
|
||||
Instant planned = task.getDue().plus(Duration.ofDays(days));
|
||||
if (task.getPlanned() != null && !task.getPlanned().equals(planned)) {
|
||||
throw new InvalidArgumentException(
|
||||
"Cannot create a task with given planned "
|
||||
+ "and due date not matching the service level");
|
||||
}
|
||||
task.setPlanned(planned);
|
||||
} else {
|
||||
task.setPlanned(task.getPlanned() == null ? now : task.getPlanned());
|
||||
long days = converter.convertWorkingDaysToDays(task.getPlanned(), finalDuration.toDays());
|
||||
Instant due = task.getPlanned().plus(Duration.ofDays(days));
|
||||
task.setDue(due);
|
||||
}
|
||||
}
|
||||
task.setPriority(finalPrioDuration.getRight());
|
||||
if (task.getPlanned() == null && (classification == null || task.getDue() == null)) {
|
||||
task.setPlanned(now);
|
||||
}
|
||||
if (classification != null) {
|
||||
task = serviceLevelHandler.updatePrioPlannedDueOfTask(task, null, false);
|
||||
}
|
||||
|
||||
if (task.getName() == null && classification != null) {
|
||||
|
@ -1249,19 +1190,7 @@ public class TaskServiceImpl implements TaskService {
|
|||
if (task.getDescription() == null && classification != null) {
|
||||
task.setDescription(classification.getDescription());
|
||||
}
|
||||
|
||||
// insert Attachments if needed
|
||||
List<Attachment> attachments = task.getAttachments();
|
||||
if (attachments != null) {
|
||||
for (Attachment attachment : attachments) {
|
||||
AttachmentImpl attachmentImpl = (AttachmentImpl) attachment;
|
||||
attachmentImpl.setId(IdGenerator.generateWithPrefix(ID_PREFIX_ATTACHMENT));
|
||||
attachmentImpl.setTaskId(task.getId());
|
||||
attachmentImpl.setCreated(now);
|
||||
attachmentImpl.setModified(now);
|
||||
attachmentMapper.insert(attachmentImpl);
|
||||
}
|
||||
}
|
||||
attachmentHandler.insertNewAttachmentsOnTaskCreation(task, now);
|
||||
LOGGER.debug("exit from standardSettings()");
|
||||
}
|
||||
|
||||
|
@ -1632,86 +1561,11 @@ public class TaskServiceImpl implements TaskService {
|
|||
.list();
|
||||
}
|
||||
|
||||
private void validateObjectReference(ObjectReference objRef, String objRefType, String objName)
|
||||
throws InvalidArgumentException {
|
||||
LOGGER.debug("entry to validateObjectReference()");
|
||||
// check that all values in the ObjectReference are set correctly
|
||||
if (objRef == null) {
|
||||
throw new InvalidArgumentException(objRefType + " of " + objName + " must not be null");
|
||||
} else if (objRef.getCompany() == null || objRef.getCompany().length() == 0) {
|
||||
throw new InvalidArgumentException(
|
||||
"Company of " + objRefType + " of " + objName + MUST_NOT_BE_EMPTY);
|
||||
} else if (objRef.getSystem() == null || objRef.getSystem().length() == 0) {
|
||||
throw new InvalidArgumentException(
|
||||
"System of " + objRefType + " of " + objName + MUST_NOT_BE_EMPTY);
|
||||
} else if (objRef.getSystemInstance() == null || objRef.getSystemInstance().length() == 0) {
|
||||
throw new InvalidArgumentException(
|
||||
"SystemInstance of " + objRefType + " of " + objName + MUST_NOT_BE_EMPTY);
|
||||
} else if (objRef.getType() == null || objRef.getType().length() == 0) {
|
||||
throw new InvalidArgumentException(
|
||||
"Type of " + objRefType + " of " + objName + MUST_NOT_BE_EMPTY);
|
||||
} else if (objRef.getValue() == null || objRef.getValue().length() == 0) {
|
||||
throw new InvalidArgumentException(
|
||||
"Value of" + objRefType + " of " + objName + MUST_NOT_BE_EMPTY);
|
||||
}
|
||||
LOGGER.debug("exit from validateObjectReference()");
|
||||
}
|
||||
|
||||
private PrioDurationHolder handleAttachments(TaskImpl task) throws InvalidArgumentException {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("entry to handleAttachments(task = {})", task);
|
||||
}
|
||||
|
||||
List<Attachment> attachments = task.getAttachments();
|
||||
if (attachments == null || attachments.isEmpty()) {
|
||||
return new PrioDurationHolder(null, Integer.MIN_VALUE);
|
||||
}
|
||||
|
||||
PrioDurationHolder actualPrioDuration = new PrioDurationHolder(MAX_DURATION, Integer.MIN_VALUE);
|
||||
|
||||
Iterator<Attachment> i = attachments.iterator();
|
||||
while (i.hasNext()) {
|
||||
Attachment attachment = i.next();
|
||||
if (attachment == null) {
|
||||
i.remove();
|
||||
} else {
|
||||
actualPrioDuration = handleNonNullAttachment(actualPrioDuration, attachment);
|
||||
}
|
||||
}
|
||||
if (MAX_DURATION.equals(actualPrioDuration.getLeft())) {
|
||||
actualPrioDuration = new PrioDurationHolder(null, actualPrioDuration.getRight());
|
||||
}
|
||||
|
||||
LOGGER.debug("exit from handleAttachments(), returning {}", actualPrioDuration);
|
||||
return actualPrioDuration;
|
||||
}
|
||||
|
||||
private PrioDurationHolder handleNonNullAttachment(
|
||||
PrioDurationHolder actualPrioDuration, Attachment attachment)
|
||||
throws InvalidArgumentException {
|
||||
ObjectReference objRef = attachment.getObjectReference();
|
||||
validateObjectReference(objRef, "ObjectReference", "Attachment");
|
||||
if (attachment.getClassificationSummary() == null) {
|
||||
throw new InvalidArgumentException(
|
||||
"Classification of attachment " + attachment + " must not be null");
|
||||
} else {
|
||||
ClassificationSummary classificationSummary = attachment.getClassificationSummary();
|
||||
if (classificationSummary != null) {
|
||||
actualPrioDuration =
|
||||
getNewPrioDuration(
|
||||
actualPrioDuration,
|
||||
classificationSummary.getPriority(),
|
||||
classificationSummary.getServiceLevel());
|
||||
}
|
||||
}
|
||||
return actualPrioDuration;
|
||||
}
|
||||
|
||||
private void standardUpdateActions(
|
||||
TaskImpl oldTaskImpl, TaskImpl newTaskImpl, PrioDurationHolder prioDurationFromAttachments)
|
||||
throws InvalidArgumentException, ConcurrencyException, ClassificationNotFoundException,
|
||||
InvalidStateException {
|
||||
validateObjectReference(newTaskImpl.getPrimaryObjRef(), "primary ObjectReference", TASK);
|
||||
private void standardUpdateActions(TaskImpl oldTaskImpl, TaskImpl newTaskImpl)
|
||||
throws InvalidArgumentException, ConcurrencyException, InvalidStateException,
|
||||
AttachmentPersistenceException, ClassificationNotFoundException {
|
||||
attachmentHandler.validateObjectReference(
|
||||
newTaskImpl.getPrimaryObjRef(), "primary ObjectReference", TASK);
|
||||
// TODO: not safe to rely only on different timestamps.
|
||||
// With fast execution below 1ms there will be no concurrencyException
|
||||
if (oldTaskImpl.getModified() != null
|
||||
|
@ -1739,6 +1593,16 @@ public class TaskServiceImpl implements TaskService {
|
|||
newTaskImpl.setPlanned(oldTaskImpl.getPlanned());
|
||||
}
|
||||
|
||||
if (newTaskImpl.getClassificationSummary() == null) {
|
||||
newTaskImpl.setClassificationSummary(oldTaskImpl.getClassificationSummary());
|
||||
}
|
||||
|
||||
attachmentHandler.insertAndDeleteAttachmentsOnTaskUpdate(newTaskImpl, oldTaskImpl);
|
||||
|
||||
updateClassificationSummary(newTaskImpl, oldTaskImpl);
|
||||
|
||||
newTaskImpl = serviceLevelHandler.updatePrioPlannedDueOfTask(newTaskImpl, oldTaskImpl, false);
|
||||
|
||||
// if no business process id is provided, use the id of the old task.
|
||||
if (newTaskImpl.getBusinessProcessId() == null) {
|
||||
newTaskImpl.setBusinessProcessId(oldTaskImpl.getBusinessProcessId());
|
||||
|
@ -1751,393 +1615,24 @@ public class TaskServiceImpl implements TaskService {
|
|||
String.format(TASK_WITH_ID_IS_NOT_READY, oldTaskImpl.getId(), oldTaskImpl.getState()));
|
||||
}
|
||||
|
||||
updateClassificationRelatedProperties(oldTaskImpl, newTaskImpl, prioDurationFromAttachments);
|
||||
|
||||
newTaskImpl.setModified(Instant.now());
|
||||
}
|
||||
|
||||
private void updateClassificationRelatedProperties(
|
||||
TaskImpl oldTaskImpl, TaskImpl newTaskImpl, PrioDurationHolder prioDurationFromAttachments)
|
||||
private void updateClassificationSummary(TaskImpl newTaskImpl, TaskImpl oldTaskImpl)
|
||||
throws ClassificationNotFoundException {
|
||||
LOGGER.debug("entry to updateClassificationRelatedProperties()");
|
||||
// insert Classification specifications if Classification is given.
|
||||
ClassificationSummary oldClassificationSummary = oldTaskImpl.getClassificationSummary();
|
||||
ClassificationSummary newClassificationSummary = newTaskImpl.getClassificationSummary();
|
||||
if (newClassificationSummary == null) {
|
||||
newClassificationSummary = oldClassificationSummary;
|
||||
}
|
||||
|
||||
if (newClassificationSummary
|
||||
== null) { // newClassification is null -> take prio and duration from attachments
|
||||
updateTaskPrioDurationFromAttachments(newTaskImpl, prioDurationFromAttachments);
|
||||
} else {
|
||||
updateTaskPrioDurationFromClassification(
|
||||
newTaskImpl,
|
||||
prioDurationFromAttachments,
|
||||
oldClassificationSummary,
|
||||
newClassificationSummary);
|
||||
}
|
||||
|
||||
LOGGER.debug("exit from updateClassificationRelatedProperties()");
|
||||
}
|
||||
|
||||
private void updateTaskPrioDurationFromClassification(
|
||||
TaskImpl newTaskImpl,
|
||||
PrioDurationHolder prioDurationFromAttachments,
|
||||
ClassificationSummary oldClassificationSummary,
|
||||
ClassificationSummary newClassificationSummary)
|
||||
throws ClassificationNotFoundException {
|
||||
LOGGER.debug("entry to updateTaskPrioDurationFromClassification()");
|
||||
Classification newClassification = null;
|
||||
if (!oldClassificationSummary.getKey().equals(newClassificationSummary.getKey())) {
|
||||
newClassification =
|
||||
Classification newClassification =
|
||||
this.classificationService.getClassification(
|
||||
newClassificationSummary.getKey(), newTaskImpl.getWorkbasketSummary().getDomain());
|
||||
newClassificationSummary = newClassification.asSummary();
|
||||
newTaskImpl.setClassificationSummary(newClassificationSummary);
|
||||
}
|
||||
|
||||
Duration minDuration = calculateDuration(prioDurationFromAttachments, newClassificationSummary);
|
||||
if (minDuration != null) {
|
||||
|
||||
long days =
|
||||
converter.convertWorkingDaysToDays(newTaskImpl.getPlanned(), minDuration.toDays());
|
||||
Instant due = newTaskImpl.getPlanned().plus(Duration.ofDays(days));
|
||||
|
||||
newTaskImpl.setDue(due);
|
||||
}
|
||||
|
||||
if (newTaskImpl.getName() == null) {
|
||||
newTaskImpl.setName(newClassificationSummary.getName());
|
||||
}
|
||||
|
||||
if (newTaskImpl.getDescription() == null && newClassification != null) {
|
||||
newTaskImpl.setDescription(newClassification.getDescription());
|
||||
}
|
||||
|
||||
int newPriority =
|
||||
Math.max(newClassificationSummary.getPriority(), prioDurationFromAttachments.getRight());
|
||||
newTaskImpl.setPriority(newPriority);
|
||||
LOGGER.debug("exit from updateTaskPrioDurationFromClassification()");
|
||||
}
|
||||
|
||||
private PrioDurationHolder handleAttachmentsOnTaskUpdate(
|
||||
TaskImpl oldTaskImpl, TaskImpl newTaskImpl) throws AttachmentPersistenceException {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(
|
||||
"entry to handleAttachmentsOnTaskUpdate(oldTaskImpl = {}, newTaskImpl = {})",
|
||||
oldTaskImpl,
|
||||
newTaskImpl);
|
||||
}
|
||||
|
||||
PrioDurationHolder prioDuration = new PrioDurationHolder(MAX_DURATION, Integer.MIN_VALUE);
|
||||
|
||||
// Iterator for removing invalid current values directly. OldAttachments can be ignored.
|
||||
Iterator<Attachment> i = newTaskImpl.getAttachments().iterator();
|
||||
while (i.hasNext()) {
|
||||
Attachment attachment = i.next();
|
||||
if (attachment != null) {
|
||||
prioDuration =
|
||||
handlePrioDurationOfOneAttachmentOnTaskUpdate(
|
||||
oldTaskImpl, newTaskImpl, prioDuration, attachment);
|
||||
} else {
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE, when an Attachment was only represented before
|
||||
deleteAttachmentOnTaskUpdate(oldTaskImpl, newTaskImpl);
|
||||
if (MAX_DURATION.equals(prioDuration.getLeft())) {
|
||||
prioDuration = new PrioDurationHolder(null, prioDuration.getRight());
|
||||
}
|
||||
|
||||
LOGGER.debug("exit from handleAttachmentsOnTaskUpdate()");
|
||||
return prioDuration;
|
||||
}
|
||||
|
||||
private void deleteAttachmentOnTaskUpdate(TaskImpl oldTaskImpl, TaskImpl newTaskImpl) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(
|
||||
"entry to deleteAttachmentOnTaskUpdate(oldTaskImpl = {}, newTaskImpl = {})",
|
||||
oldTaskImpl,
|
||||
newTaskImpl);
|
||||
}
|
||||
|
||||
for (Attachment oldAttachment : oldTaskImpl.getAttachments()) {
|
||||
if (oldAttachment != null) {
|
||||
boolean isRepresented = false;
|
||||
for (Attachment newAttachment : newTaskImpl.getAttachments()) {
|
||||
if (newAttachment != null && oldAttachment.getId().equals(newAttachment.getId())) {
|
||||
isRepresented = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isRepresented) {
|
||||
attachmentMapper.deleteAttachment(oldAttachment.getId());
|
||||
LOGGER.debug(
|
||||
"TaskService.updateTask() for TaskId={} DELETED an Attachment={}.",
|
||||
newTaskImpl.getId(),
|
||||
oldAttachment);
|
||||
}
|
||||
}
|
||||
}
|
||||
LOGGER.debug("exit from deleteAttachmentOnTaskUpdate()");
|
||||
}
|
||||
|
||||
private PrioDurationHolder handlePrioDurationOfOneAttachmentOnTaskUpdate(
|
||||
TaskImpl oldTaskImpl,
|
||||
TaskImpl newTaskImpl,
|
||||
PrioDurationHolder prioDuration,
|
||||
Attachment attachment)
|
||||
throws AttachmentPersistenceException {
|
||||
LOGGER.debug("entry to handlePrioDurationOfOneAttachmentOnTaskUpdate()");
|
||||
boolean wasAlreadyPresent = false;
|
||||
if (attachment.getId() != null) {
|
||||
for (Attachment oldAttachment : oldTaskImpl.getAttachments()) {
|
||||
if (oldAttachment != null && attachment.getId().equals(oldAttachment.getId())) {
|
||||
wasAlreadyPresent = true;
|
||||
if (!attachment.equals(oldAttachment)) {
|
||||
prioDuration =
|
||||
handlePrioDurationOfOneNewAttachmentOnTaskUpdate(
|
||||
newTaskImpl, prioDuration, attachment);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ADD, when ID not set or not found in elements
|
||||
if (!wasAlreadyPresent) {
|
||||
prioDuration = handleNewAttachmentOnTaskUpdate(newTaskImpl, prioDuration, attachment);
|
||||
}
|
||||
|
||||
LOGGER.debug(
|
||||
"exit from handlePrioDurationOfOneAttachmentOnTaskUpdate(), returning {}", prioDuration);
|
||||
return prioDuration;
|
||||
}
|
||||
|
||||
private PrioDurationHolder handlePrioDurationOfOneNewAttachmentOnTaskUpdate(
|
||||
TaskImpl newTaskImpl, PrioDurationHolder prioDuration, Attachment attachment) {
|
||||
LOGGER.debug("entry to handlePrioDurationOfOneNewAttachmentOnTaskUpdate()");
|
||||
AttachmentImpl temp = (AttachmentImpl) attachment;
|
||||
|
||||
ClassificationSummary classification = attachment.getClassificationSummary();
|
||||
if (classification != null) {
|
||||
prioDuration =
|
||||
getNewPrioDuration(
|
||||
prioDuration, classification.getPriority(), classification.getServiceLevel());
|
||||
}
|
||||
|
||||
temp.setModified(Instant.now());
|
||||
attachmentMapper.update(temp);
|
||||
LOGGER.debug(
|
||||
"TaskService.updateTask() for TaskId={} UPDATED an Attachment={}.",
|
||||
newTaskImpl.getId(),
|
||||
attachment);
|
||||
LOGGER.debug(
|
||||
"exit from handlePrioDurationOfOneNewAttachmentOnTaskUpdate(), returning {}", prioDuration);
|
||||
return prioDuration;
|
||||
}
|
||||
|
||||
private PrioDurationHolder handleNewAttachmentOnTaskUpdate(
|
||||
TaskImpl newTaskImpl, PrioDurationHolder prioDuration, Attachment attachment)
|
||||
throws AttachmentPersistenceException {
|
||||
LOGGER.debug("entry to handleNewAttachmentOnTaskUpdate()");
|
||||
AttachmentImpl attachmentImpl = (AttachmentImpl) attachment;
|
||||
initAttachment(attachmentImpl, newTaskImpl);
|
||||
ClassificationSummary classification = attachment.getClassificationSummary();
|
||||
if (classification != null) {
|
||||
prioDuration =
|
||||
getNewPrioDuration(
|
||||
prioDuration, classification.getPriority(), classification.getServiceLevel());
|
||||
}
|
||||
|
||||
try {
|
||||
attachmentMapper.insert(attachmentImpl);
|
||||
LOGGER.debug(
|
||||
"TaskService.updateTask() for TaskId={} INSERTED an Attachment={}.",
|
||||
newTaskImpl.getId(),
|
||||
attachmentImpl);
|
||||
} catch (PersistenceException e) {
|
||||
throw new AttachmentPersistenceException(
|
||||
"Cannot insert the Attachement "
|
||||
+ attachmentImpl.getId()
|
||||
+ " for Task "
|
||||
+ newTaskImpl.getId()
|
||||
+ " because it already exists.",
|
||||
e.getCause());
|
||||
}
|
||||
LOGGER.debug("exit from handleNewAttachmentOnTaskUpdate(), returning {}", prioDuration);
|
||||
return prioDuration;
|
||||
}
|
||||
|
||||
private PrioDurationHolder handleAttachmentsOnClassificationUpdate(Task task) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("entry to handleAttachmentsOnClassificationUpdate(task = {})", task);
|
||||
}
|
||||
|
||||
PrioDurationHolder prioDuration = new PrioDurationHolder(MAX_DURATION, Integer.MIN_VALUE);
|
||||
|
||||
// Iterator for removing invalid current values directly. OldAttachments can be ignored.
|
||||
for (Attachment attachment : task.getAttachments()) {
|
||||
if (attachment != null) {
|
||||
ClassificationSummary classification = attachment.getClassificationSummary();
|
||||
if (classification != null) {
|
||||
prioDuration =
|
||||
getNewPrioDuration(
|
||||
prioDuration, classification.getPriority(), classification.getServiceLevel());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (MAX_DURATION.equals(prioDuration.getLeft())) {
|
||||
prioDuration = new PrioDurationHolder(null, prioDuration.getRight());
|
||||
}
|
||||
|
||||
LOGGER.debug("exit from handleAttachmentsOnClassificationUpdate(), returning {}", prioDuration);
|
||||
return prioDuration;
|
||||
}
|
||||
|
||||
private PrioDurationHolder getNewPrioDuration(
|
||||
PrioDurationHolder prioDurationHolder,
|
||||
int prioFromClassification,
|
||||
String serviceLevelFromClassification) {
|
||||
LOGGER.debug(
|
||||
"entry to getNewPrioDuration(prioDurationHolder = {}, prioFromClassification = {}, "
|
||||
+ "serviceLevelFromClassification = {})",
|
||||
prioDurationHolder,
|
||||
prioFromClassification,
|
||||
serviceLevelFromClassification);
|
||||
Duration minDuration = prioDurationHolder.getLeft();
|
||||
int maxPrio = prioDurationHolder.getRight();
|
||||
|
||||
if (serviceLevelFromClassification != null) {
|
||||
Duration currentDuration = Duration.parse(serviceLevelFromClassification);
|
||||
if (prioDurationHolder.getLeft() != null) {
|
||||
if (prioDurationHolder.getLeft().compareTo(currentDuration) > 0) {
|
||||
minDuration = currentDuration;
|
||||
}
|
||||
} else {
|
||||
minDuration = currentDuration;
|
||||
}
|
||||
}
|
||||
if (prioFromClassification > maxPrio) {
|
||||
maxPrio = prioFromClassification;
|
||||
}
|
||||
|
||||
PrioDurationHolder pair = new PrioDurationHolder(minDuration, maxPrio);
|
||||
LOGGER.debug("exit from getNewPrioDuration(), returning {}", pair);
|
||||
return pair;
|
||||
}
|
||||
|
||||
private void initAttachment(AttachmentImpl attachment, Task newTask) {
|
||||
LOGGER.debug("entry to initAttachment()");
|
||||
if (attachment.getId() == null) {
|
||||
attachment.setId(IdGenerator.generateWithPrefix(ID_PREFIX_ATTACHMENT));
|
||||
}
|
||||
if (attachment.getCreated() == null) {
|
||||
attachment.setCreated(Instant.now());
|
||||
}
|
||||
if (attachment.getModified() == null) {
|
||||
attachment.setModified(attachment.getCreated());
|
||||
}
|
||||
if (attachment.getTaskId() == null) {
|
||||
attachment.setTaskId(newTask.getId());
|
||||
}
|
||||
LOGGER.debug("exit from initAttachment()");
|
||||
}
|
||||
|
||||
private void updatePrioDueDateOnClassificationUpdate(
|
||||
TaskImpl task, PrioDurationHolder prioDurationFromAttachments) {
|
||||
LOGGER.debug("entry to updatePrioDueDateOnClassificationUpdate()");
|
||||
ClassificationSummary classificationSummary = task.getClassificationSummary();
|
||||
|
||||
if (classificationSummary
|
||||
== null) { // classification is null -> take prio and duration from attachments
|
||||
updateTaskPrioDurationFromAttachments(task, prioDurationFromAttachments);
|
||||
} else {
|
||||
updateTaskPrioDurationFromClassificationAndAttachments(
|
||||
task, prioDurationFromAttachments, classificationSummary);
|
||||
}
|
||||
|
||||
LOGGER.debug("exit from updatePrioDueDateOnClassificationUpdate()");
|
||||
}
|
||||
|
||||
private void updateTaskPrioDurationFromClassificationAndAttachments(
|
||||
TaskImpl task,
|
||||
PrioDurationHolder prioDurationFromAttachments,
|
||||
ClassificationSummary classificationSummary) {
|
||||
LOGGER.debug("entry to updateTaskPrioDurationFromClassificationAndAttachments()");
|
||||
|
||||
Duration minDuration = calculateDuration(prioDurationFromAttachments, classificationSummary);
|
||||
if (minDuration != null) {
|
||||
long days = converter.convertWorkingDaysToDays(task.getPlanned(), minDuration.toDays());
|
||||
Instant due = task.getPlanned().plus(Duration.ofDays(days));
|
||||
|
||||
task.setDue(due);
|
||||
}
|
||||
|
||||
int newPriority =
|
||||
Math.max(classificationSummary.getPriority(), prioDurationFromAttachments.getRight());
|
||||
task.setPriority(newPriority);
|
||||
LOGGER.debug("exit from updateTaskPrioDurationFromClassificationAndAttachments()");
|
||||
}
|
||||
|
||||
private void updateTaskPrioDurationFromAttachments(
|
||||
TaskImpl task, PrioDurationHolder prioDurationFromAttachments) {
|
||||
LOGGER.debug("entry to updateTaskPrioDurationFromAttachments()");
|
||||
if (prioDurationFromAttachments.getLeft() != null) {
|
||||
long days =
|
||||
converter.convertWorkingDaysToDays(
|
||||
task.getPlanned(), prioDurationFromAttachments.getLeft().toDays());
|
||||
Instant due = task.getPlanned().plus(Duration.ofDays(days));
|
||||
task.setDue(due);
|
||||
}
|
||||
if (prioDurationFromAttachments.getRight() > Integer.MIN_VALUE) {
|
||||
task.setPriority(prioDurationFromAttachments.getRight());
|
||||
}
|
||||
LOGGER.debug("exit from updateTaskPrioDurationFromAttachments()");
|
||||
}
|
||||
|
||||
private List<Attachment> augmentAttachmentsByClassification(
|
||||
List<AttachmentImpl> attachmentImpls, BulkOperationResults<String, Exception> bulkLog) {
|
||||
LOGGER.debug("entry to augmentAttachmentsByClassification()");
|
||||
List<Attachment> result = new ArrayList<>();
|
||||
if (attachmentImpls == null || attachmentImpls.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
List<ClassificationSummary> classifications =
|
||||
classificationService
|
||||
.createClassificationQuery()
|
||||
.idIn(
|
||||
attachmentImpls.stream()
|
||||
.map(t -> t.getClassificationSummary().getId())
|
||||
.distinct()
|
||||
.toArray(String[]::new))
|
||||
.list();
|
||||
for (AttachmentImpl att : attachmentImpls) {
|
||||
ClassificationSummary classificationSummary =
|
||||
classifications.stream()
|
||||
.filter(cl -> cl.getId().equals(att.getClassificationSummary().getId()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (classificationSummary == null) {
|
||||
String id = att.getClassificationSummary().getId();
|
||||
bulkLog.addError(
|
||||
att.getClassificationSummary().getId(),
|
||||
new ClassificationNotFoundException(
|
||||
id,
|
||||
String.format(
|
||||
"When processing task updates due to change "
|
||||
+ "of classification, the classification with id %s was not found",
|
||||
id)));
|
||||
} else {
|
||||
att.setClassificationSummary(classificationSummary);
|
||||
result.add(att);
|
||||
}
|
||||
}
|
||||
|
||||
LOGGER.debug("exit from augmentAttachmentsByClassification()");
|
||||
return result;
|
||||
}
|
||||
|
||||
private void createTasksCompletedEvents(List<TaskSummary> taskSummaries) {
|
||||
|
@ -2146,11 +1641,4 @@ public class TaskServiceImpl implements TaskService {
|
|||
historyEventProducer.createEvent(
|
||||
new CompletedEvent(task, CurrentUserContext.getUserid())));
|
||||
}
|
||||
|
||||
private static class PrioDurationHolder extends Pair<Duration, Integer> {
|
||||
|
||||
PrioDurationHolder(Duration left, Integer right) {
|
||||
super(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -254,6 +254,10 @@ public class TaskImpl extends TaskSummaryImpl implements Task {
|
|||
}
|
||||
}
|
||||
|
||||
public String getClassificationId() {
|
||||
return classificationSummary == null ? null : classificationSummary.getId();
|
||||
}
|
||||
|
||||
protected boolean canEqual(Object other) {
|
||||
return (other instanceof TaskImpl);
|
||||
}
|
||||
|
|
|
@ -59,8 +59,8 @@ public class UpdateObjectsUseUtcTimeStampsAccTest extends AbstractAccTest {
|
|||
Task task = taskService.getTask("TKI:000000000000000000000000000000000000");
|
||||
TaskImpl ti = (TaskImpl) task;
|
||||
task.setPlanned(Instant.now().plus(Duration.ofHours(17)));
|
||||
task.setDue(Instant.now().plus(Duration.ofHours(27)));
|
||||
ti.setCompleted(Instant.now().plus(Duration.ofHours(27)));
|
||||
task.setDue(Instant.now().plus(Duration.ofHours(41)));
|
||||
ti.setCompleted(Instant.now().plus(Duration.ofHours(40)));
|
||||
|
||||
TimeZone originalZone = TimeZone.getDefault();
|
||||
Task updatedTask = taskService.updateTask(task);
|
||||
|
|
|
@ -10,6 +10,8 @@ import java.time.temporal.ChronoUnit;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
@ -28,6 +30,7 @@ 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;
|
||||
import pro.taskana.task.api.models.TaskSummary;
|
||||
|
||||
/** Acceptance test for all "create task" scenarios. */
|
||||
@ExtendWith(JaasExtension.class)
|
||||
|
@ -256,4 +259,39 @@ public class SetPlannedAccTest extends AbstractAccTest {
|
|||
taskanaEngine.getTaskService().setPlannedPropertyOfTasks(planned, new ArrayList<>());
|
||||
assertThat(results.containsErrors()).isFalse();
|
||||
}
|
||||
|
||||
@WithAccessId(
|
||||
userName = "admin",
|
||||
groupNames = {"group_2"})
|
||||
@Test
|
||||
void testSetPlannedPropertyOnAllTasks()
|
||||
throws NotAuthorizedException, TaskNotFoundException, SQLException {
|
||||
Instant planned = getInstant("2020-05-03T07:00:00");
|
||||
List<TaskSummary> allTasks = taskService.createTaskQuery().list();
|
||||
// Now update each task with updateTask() and new planned
|
||||
final List<TaskSummary> individuallyUpdatedTasks = new ArrayList<>();
|
||||
allTasks.forEach(t -> individuallyUpdatedTasks.add(getUpdatedTaskSummary(t, planned)));
|
||||
// reset DB and do the same with bulk update
|
||||
resetDb(false);
|
||||
List<String> taskIds = allTasks.stream().map(TaskSummary::getId).collect(Collectors.toList());
|
||||
BulkOperationResults<String, TaskanaException> bulkLog =
|
||||
taskService.setPlannedPropertyOfTasks(planned, taskIds);
|
||||
// check that there was no error and compare the result of the 2 operations
|
||||
assertThat(bulkLog.containsErrors()).isFalse();
|
||||
Map<String, Instant> bulkUpdatedTaskMap =
|
||||
taskService.createTaskQuery().list().stream()
|
||||
.collect(Collectors.toMap(TaskSummary::getId, TaskSummary::getDue));
|
||||
individuallyUpdatedTasks.forEach(
|
||||
t -> assertThat(t.getDue().equals(bulkUpdatedTaskMap.get(t.getId()))));
|
||||
}
|
||||
|
||||
private TaskSummary getUpdatedTaskSummary(TaskSummary t, Instant planned) {
|
||||
try {
|
||||
Task task = taskService.getTask(t.getId());
|
||||
task.setPlanned(planned);
|
||||
return taskService.updateTask(task);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
@ -336,4 +336,21 @@ class UpdateTaskAccTest extends AbstractAccTest {
|
|||
|
||||
assertThat(retrievedUpdatedTask.getCallbackInfo()).isEqualTo(callbackInfo);
|
||||
}
|
||||
|
||||
@WithAccessId(
|
||||
userName = "user_1_2",
|
||||
groupNames = {"group_1"})
|
||||
@Test
|
||||
void testUpdatePlannedAndDue()
|
||||
throws NotAuthorizedException, TaskNotFoundException, ClassificationNotFoundException,
|
||||
InvalidArgumentException, InvalidStateException, ConcurrencyException,
|
||||
AttachmentPersistenceException {
|
||||
TaskService taskService = taskanaEngine.getTaskService();
|
||||
Task task = taskService.getTask("TKI:000000000000000000000000000000000030");
|
||||
task.setPlanned(Instant.now());
|
||||
task.setPlanned(getInstant("2020-04-21T07:00:00"));
|
||||
task.setDue(getInstant("2020-04-21T10:00:00"));
|
||||
assertThatThrownBy(() -> taskService.updateTask(task))
|
||||
.isInstanceOf(InvalidArgumentException.class);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue