TSK-1143 UpdateTask planned - comments from Holger
This commit is contained in:
parent
1291287419
commit
7deb925411
|
@ -479,8 +479,8 @@ public interface TaskService {
|
|||
|
||||
/**
|
||||
* 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.
|
||||
* affected by this method. On each task, the corresponding due date is set according to the
|
||||
* shortest service level in the classifications 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.
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
package pro.taskana.task.api.models;
|
||||
|
||||
import java.util.Objects;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import pro.taskana.common.api.exceptions.InvalidArgumentException;
|
||||
|
||||
/** ObjectReference entity. */
|
||||
public class ObjectReference {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ObjectReference.class);
|
||||
private String id;
|
||||
private String company;
|
||||
private String system;
|
||||
|
@ -60,6 +64,33 @@ public class ObjectReference {
|
|||
this.value = value;
|
||||
}
|
||||
|
||||
public static void validate(ObjectReference objectReference, String objRefType, String objName)
|
||||
throws InvalidArgumentException {
|
||||
LOGGER.debug("entry to validateObjectReference()");
|
||||
// check that all values in the ObjectReference are set correctly
|
||||
if (objectReference == null) {
|
||||
throw new InvalidArgumentException(
|
||||
String.format("ObectReferenc %s of %s must not be null.", objRefType, objName));
|
||||
} else if (objectReference.getCompany() == null || objectReference.getCompany().length() == 0) {
|
||||
throw new InvalidArgumentException(
|
||||
String.format("Company of %s of %s must not be empty", objRefType, objName));
|
||||
} else if (objectReference.getSystem() == null || objectReference.getSystem().length() == 0) {
|
||||
throw new InvalidArgumentException(
|
||||
String.format("System of %s of %s must not be empty", objRefType, objName));
|
||||
} else if (objectReference.getSystemInstance() == null
|
||||
|| objectReference.getSystemInstance().length() == 0) {
|
||||
throw new InvalidArgumentException(
|
||||
String.format("SystemInstance of %s of %s must not be empty", objRefType, objName));
|
||||
} else if (objectReference.getType() == null || objectReference.getType().length() == 0) {
|
||||
throw new InvalidArgumentException(
|
||||
String.format("Type of %s of %s must not be empty", objRefType, objName));
|
||||
} else if (objectReference.getValue() == null || objectReference.getValue().length() == 0) {
|
||||
throw new InvalidArgumentException(
|
||||
String.format("Value of %s of %s must not be empty", objRefType, objName));
|
||||
}
|
||||
LOGGER.debug("exit from validateObjectReference()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, company, system, systemInstance, type, value);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package pro.taskana.task.internal;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -37,9 +36,8 @@ public class AttachmentHandler {
|
|||
this.classificationService = classificationService;
|
||||
}
|
||||
|
||||
public List<Attachment> augmentAttachmentsByClassification(
|
||||
List<AttachmentImpl> attachmentImpls,
|
||||
BulkOperationResults<String, Exception> bulkLog) {
|
||||
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()) {
|
||||
|
@ -148,18 +146,15 @@ public class AttachmentHandler {
|
|||
}
|
||||
}
|
||||
|
||||
void insertNewAttachmentsOnTaskCreation(TaskImpl task, Instant now)
|
||||
void insertNewAttachmentsOnTaskCreation(TaskImpl task)
|
||||
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);
|
||||
initAttachment(attachmentImpl, task);
|
||||
ObjectReference objRef = attachmentImpl.getObjectReference();
|
||||
validateObjectReference(objRef, "ObjectReference", "Attachment");
|
||||
ObjectReference.validate(objRef, "ObjectReference", "Attachment");
|
||||
attachmentMapper.insert(attachmentImpl);
|
||||
}
|
||||
}
|
||||
|
@ -210,11 +205,9 @@ public class AttachmentHandler {
|
|||
attachmentImpl);
|
||||
} catch (PersistenceException e) {
|
||||
throw new AttachmentPersistenceException(
|
||||
"Cannot insert the Attachement "
|
||||
+ attachmentImpl.getId()
|
||||
+ " for Task "
|
||||
+ newTaskImpl.getId()
|
||||
+ " because it already exists.",
|
||||
String.format(
|
||||
"Cannot insert the Attachement %s for Task %s because it already exists.",
|
||||
attachmentImpl.getId(), newTaskImpl.getId()),
|
||||
e.getCause());
|
||||
}
|
||||
LOGGER.debug("exit from insertNewAttachmentOnTaskUpdate(), returning");
|
||||
|
@ -226,7 +219,7 @@ public class AttachmentHandler {
|
|||
attachment.setId(IdGenerator.generateWithPrefix(ID_PREFIX_ATTACHMENT));
|
||||
}
|
||||
if (attachment.getCreated() == null) {
|
||||
attachment.setCreated(Instant.now());
|
||||
attachment.setCreated(newTask.getModified());
|
||||
}
|
||||
if (attachment.getModified() == null) {
|
||||
attachment.setModified(attachment.getCreated());
|
||||
|
@ -237,28 +230,4 @@ public class AttachmentHandler {
|
|||
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()");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@ public interface AttachmentMapper {
|
|||
@Result(property = "channel", column = "CHANNEL"),
|
||||
@Result(property = "received", column = "RECEIVED")
|
||||
})
|
||||
List<AttachmentSummaryImpl> findAttachmentSummariesByTaskIds(@Param("taskIds") String[] taskIds);
|
||||
List<AttachmentSummaryImpl> findAttachmentSummariesByTaskIds(@Param("taskIds") List<String> taskIds);
|
||||
|
||||
@Delete("DELETE FROM ATTACHMENT WHERE ID=#{attachmentId}")
|
||||
void deleteAttachment(@Param("attachmentId") String attachmentId);
|
||||
|
|
|
@ -83,10 +83,10 @@ class ServiceLevelHandler {
|
|||
findAllInvolvedClassifications(existingTasksAuthorizedFor, attachments);
|
||||
List<ClassificationWithServiceLevelResolved> allInvolvedClassificationsWithDuration =
|
||||
resolveDurationsInClassifications(allInvolvedClassifications);
|
||||
Map<Duration, List<TaskDuration>> tasksPerDuration =
|
||||
getTasksPerDuration(
|
||||
Map<Duration, List<String>> durationToTaskIdsMap =
|
||||
getDurationToTaskIdsMap(
|
||||
existingTasksAuthorizedFor, attachments, allInvolvedClassificationsWithDuration);
|
||||
BulkLog updateResult = updatePlannedPropertyOfAffectedTasks(planned, tasksPerDuration);
|
||||
BulkLog updateResult = updatePlannedPropertyOfAffectedTasks(planned, durationToTaskIdsMap);
|
||||
bulkLog.addAllErrors(updateResult);
|
||||
|
||||
return bulkLog;
|
||||
|
@ -214,21 +214,31 @@ class ServiceLevelHandler {
|
|||
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())) {
|
||||
if (newTaskImpl.getPlanned() == null && newTaskImpl.getDue() == null) {
|
||||
newTaskImpl.setPlanned(oldTaskImpl.getPlanned());
|
||||
}
|
||||
// case 1: no change of planned / due, but potentially change of an attachment or classification
|
||||
if (oldTaskImpl.getDue().equals(newTaskImpl.getDue())
|
||||
&& oldTaskImpl.getPlanned().equals(newTaskImpl.getPlanned())) {
|
||||
newTaskImpl.setDue(newPlannedDueInstant(newTaskImpl, durationPrioHolder.getDuration(), true));
|
||||
} else if ((newTaskImpl.getDue().equals(oldTaskImpl.getDue()))) {
|
||||
} else if (oldTaskImpl.getDue().equals(newTaskImpl.getDue())
|
||||
&& newTaskImpl.getPlanned() != null) {
|
||||
// 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");
|
||||
if (newTaskImpl.getDue() == null) {
|
||||
newTaskImpl.setDue(
|
||||
newPlannedDueInstant(newTaskImpl, durationPrioHolder.getDuration(), true));
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
newTaskImpl.setPlanned(planned);
|
||||
}
|
||||
return newTaskImpl;
|
||||
}
|
||||
|
@ -250,17 +260,16 @@ class ServiceLevelHandler {
|
|||
}
|
||||
|
||||
private BulkLog updatePlannedPropertyOfAffectedTasks(
|
||||
Instant planned, Map<Duration, List<TaskDuration>> tasksPerDuration) {
|
||||
Instant planned, Map<Duration, List<String>> durationToTaskIdsMap) {
|
||||
BulkLog bulkLog = new BulkLog();
|
||||
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());
|
||||
referenceTask.setModified(Instant.now());
|
||||
for (Map.Entry<Duration, List<String>> entry : durationToTaskIdsMap.entrySet()) {
|
||||
List<String> taskIdsToUpdate = entry.getValue();
|
||||
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 checkResult =
|
||||
|
@ -300,17 +309,17 @@ class ServiceLevelHandler {
|
|||
return bulkLog;
|
||||
}
|
||||
|
||||
private Map<Duration, List<TaskDuration>> getTasksPerDuration(
|
||||
private Map<Duration, List<String>> getDurationToTaskIdsMap(
|
||||
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 =
|
||||
Map<String, Set<String>> taskIdToClassificationIdsMap =
|
||||
findAllClassificationIdsPerTask(minimalTaskSummariesAuthorizedFor, attachments);
|
||||
// Map classificationId -> Duration
|
||||
Map<String, Duration> durationPerClassificationId =
|
||||
Map<String, Duration> classificationIdToDurationMap =
|
||||
allInvolvedClassificationsWithServiceLevelResolved.stream()
|
||||
.collect(
|
||||
Collectors.toMap(
|
||||
|
@ -318,15 +327,19 @@ class ServiceLevelHandler {
|
|||
ClassificationWithServiceLevelResolved::getDurationFromClassification));
|
||||
for (MinimalTaskSummary task : minimalTaskSummariesAuthorizedFor) {
|
||||
Duration duration =
|
||||
determineMinimalDurationForTasks(
|
||||
classificationIdsPerTaskId.get(task.getTaskId()), durationPerClassificationId);
|
||||
determineMinimalDurationForATask(
|
||||
taskIdToClassificationIdsMap.get(task.getTaskId()), classificationIdToDurationMap);
|
||||
TaskDuration taskDuration = new TaskDuration(task.getTaskId(), duration);
|
||||
resultingTaskDurations.add(taskDuration);
|
||||
}
|
||||
return resultingTaskDurations.stream().collect(groupingBy(TaskDuration::getDuration));
|
||||
return resultingTaskDurations.stream()
|
||||
.collect(
|
||||
groupingBy(
|
||||
TaskDuration::getDuration,
|
||||
Collectors.mapping(TaskDuration::getTaskId, Collectors.toList())));
|
||||
}
|
||||
|
||||
private Duration determineMinimalDurationForTasks(
|
||||
private Duration determineMinimalDurationForATask(
|
||||
Set<String> classificationIds, Map<String, Duration> durationPerClassificationId) {
|
||||
Duration result = MAX_DURATION;
|
||||
for (String classificationId : classificationIds) {
|
||||
|
@ -341,7 +354,7 @@ class ServiceLevelHandler {
|
|||
// 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<>();
|
||||
Map<String, Set<String>> resultingTaskIdToClassificationIdsMap = new HashMap<>();
|
||||
for (MinimalTaskSummary task : minimalTaskSummaries) {
|
||||
Set<String> classificationIds =
|
||||
attachments.stream()
|
||||
|
@ -350,9 +363,9 @@ class ServiceLevelHandler {
|
|||
.map(ClassificationSummary::getId)
|
||||
.collect(Collectors.toSet());
|
||||
classificationIds.add(task.getClassificationId());
|
||||
result.put(task.getTaskId(), classificationIds);
|
||||
resultingTaskIdToClassificationIdsMap.put(task.getTaskId(), classificationIds);
|
||||
}
|
||||
return result;
|
||||
return resultingTaskIdToClassificationIdsMap;
|
||||
}
|
||||
|
||||
private List<ClassificationWithServiceLevelResolved> resolveDurationsInClassifications(
|
||||
|
@ -374,12 +387,9 @@ class ServiceLevelHandler {
|
|||
.map(MinimalTaskSummary::getTaskId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
String[] taskIdsAuthorizedForArray = new String[existingTaskIdsAuthorizedFor.size()];
|
||||
taskIdsAuthorizedForArray = existingTaskIdsAuthorizedFor.toArray(taskIdsAuthorizedForArray);
|
||||
|
||||
return existingTaskIdsAuthorizedFor.isEmpty()
|
||||
? new ArrayList<>()
|
||||
: attachmentMapper.findAttachmentSummariesByTaskIds(taskIdsAuthorizedForArray);
|
||||
: attachmentMapper.findAttachmentSummariesByTaskIds(existingTaskIdsAuthorizedFor);
|
||||
}
|
||||
|
||||
private List<ClassificationSummary> findAllInvolvedClassifications(
|
||||
|
|
|
@ -215,8 +215,7 @@ public class TaskServiceImpl implements TaskService {
|
|||
Classification classification =
|
||||
this.classificationService.getClassification(classificationKey, workbasket.getDomain());
|
||||
task.setClassificationSummary(classification.asSummary());
|
||||
attachmentHandler.validateObjectReference(
|
||||
task.getPrimaryObjRef(), "primary ObjectReference", TASK);
|
||||
ObjectReference.validate(task.getPrimaryObjRef(), "primary ObjectReference", TASK);
|
||||
standardSettings(task, classification);
|
||||
setCallbackStateOnTaskCreation(task);
|
||||
try {
|
||||
|
@ -410,6 +409,10 @@ public class TaskServiceImpl implements TaskService {
|
|||
try {
|
||||
taskanaEngine.openConnection();
|
||||
oldTaskImpl = (TaskImpl) getTask(newTaskImpl.getId());
|
||||
|
||||
newTaskImpl = checkConcurrencyAndSetModified(newTaskImpl, oldTaskImpl);
|
||||
|
||||
attachmentHandler.insertAndDeleteAttachmentsOnTaskUpdate(newTaskImpl, oldTaskImpl);
|
||||
standardUpdateActions(oldTaskImpl, newTaskImpl);
|
||||
|
||||
taskMapper.update(newTaskImpl);
|
||||
|
@ -522,8 +525,7 @@ public class TaskServiceImpl implements TaskService {
|
|||
selectionCriteria,
|
||||
customFieldsToUpdate);
|
||||
}
|
||||
attachmentHandler.validateObjectReference(
|
||||
selectionCriteria, "ObjectReference", "updateTasks call");
|
||||
ObjectReference.validate(selectionCriteria, "ObjectReference", "updateTasks call");
|
||||
validateCustomFields(customFieldsToUpdate);
|
||||
CustomPropertySelector fieldSelector = new CustomPropertySelector();
|
||||
TaskImpl updated = initUpdatedTask(customFieldsToUpdate, fieldSelector);
|
||||
|
@ -840,16 +842,16 @@ public class TaskServiceImpl implements TaskService {
|
|||
return result;
|
||||
}
|
||||
|
||||
String[] taskIdArray =
|
||||
taskSummaries.stream().map(TaskSummaryImpl::getId).distinct().toArray(String[]::new);
|
||||
List<String> taskIds =
|
||||
taskSummaries.stream().map(TaskSummaryImpl::getId).distinct().collect(Collectors.toList());
|
||||
|
||||
if (taskIdArray.length == 0) {
|
||||
taskIdArray = null;
|
||||
if (taskIds.isEmpty()) {
|
||||
taskIds = null;
|
||||
}
|
||||
LOGGER.debug(
|
||||
"augmentTaskSummariesByContainedSummaries() about to query for attachmentSummaries ");
|
||||
List<AttachmentSummaryImpl> attachmentSummaries =
|
||||
attachmentMapper.findAttachmentSummariesByTaskIds(taskIdArray);
|
||||
attachmentMapper.findAttachmentSummariesByTaskIds(taskIds);
|
||||
|
||||
List<ClassificationSummary> classifications =
|
||||
findClassificationsForTasksAndAttachments(taskSummaries, attachmentSummaries);
|
||||
|
@ -862,23 +864,22 @@ public class TaskServiceImpl implements TaskService {
|
|||
return result;
|
||||
}
|
||||
|
||||
private Duration calculateDuration(
|
||||
PrioDurationHolder prioDurationFromAttachments,
|
||||
ClassificationSummary newClassificationSummary) {
|
||||
if (newClassificationSummary.getServiceLevel() == null) {
|
||||
return null;
|
||||
|
||||
|
||||
private TaskImpl checkConcurrencyAndSetModified(TaskImpl newTaskImpl, TaskImpl oldTaskImpl)
|
||||
throws ConcurrencyException {
|
||||
// TODO: not safe to rely only on different timestamps.
|
||||
// With fast execution below 1ms there will be no concurrencyException
|
||||
if (oldTaskImpl.getModified() != null
|
||||
&& !oldTaskImpl.getModified().equals(newTaskImpl.getModified())
|
||||
|| oldTaskImpl.getClaimed() != null
|
||||
&& !oldTaskImpl.getClaimed().equals(newTaskImpl.getClaimed())
|
||||
|| oldTaskImpl.getState() != null
|
||||
&& !oldTaskImpl.getState().equals(newTaskImpl.getState())) {
|
||||
throw new ConcurrencyException("The task has already been updated by another user");
|
||||
}
|
||||
Duration minDuration = prioDurationFromAttachments.getLeft();
|
||||
Duration durationFromClassification =
|
||||
Duration.parse(newClassificationSummary.getServiceLevel());
|
||||
if (minDuration != null) {
|
||||
if (minDuration.compareTo(durationFromClassification) > 0) {
|
||||
minDuration = durationFromClassification;
|
||||
}
|
||||
} else {
|
||||
minDuration = durationFromClassification;
|
||||
}
|
||||
return minDuration;
|
||||
newTaskImpl.setModified(Instant.now());
|
||||
return newTaskImpl;
|
||||
}
|
||||
|
||||
private BulkOperationResults<String, TaskanaException> addExceptionsForTasksWhoseOwnerWasNotSet(
|
||||
|
@ -1190,7 +1191,7 @@ public class TaskServiceImpl implements TaskService {
|
|||
if (task.getDescription() == null && classification != null) {
|
||||
task.setDescription(classification.getDescription());
|
||||
}
|
||||
attachmentHandler.insertNewAttachmentsOnTaskCreation(task, now);
|
||||
attachmentHandler.insertNewAttachmentsOnTaskCreation(task);
|
||||
LOGGER.debug("exit from standardSettings()");
|
||||
}
|
||||
|
||||
|
@ -1205,8 +1206,9 @@ public class TaskServiceImpl implements TaskService {
|
|||
} catch (Exception e) {
|
||||
LOGGER.warn(
|
||||
"Attempted to determine callback state from {} and caught exception", value, e);
|
||||
|
||||
throw new InvalidArgumentException(
|
||||
"Attempted to set callback state for task " + task.getId(), e);
|
||||
String.format("Attempted to set callback state for task %s.", task.getId()), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1562,20 +1564,8 @@ public class TaskServiceImpl implements TaskService {
|
|||
}
|
||||
|
||||
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
|
||||
&& !oldTaskImpl.getModified().equals(newTaskImpl.getModified())
|
||||
|| oldTaskImpl.getClaimed() != null
|
||||
&& !oldTaskImpl.getClaimed().equals(newTaskImpl.getClaimed())
|
||||
|| oldTaskImpl.getState() != null
|
||||
&& !oldTaskImpl.getState().equals(newTaskImpl.getState())) {
|
||||
throw new ConcurrencyException("The task has already been updated by another user");
|
||||
}
|
||||
throws InvalidArgumentException, InvalidStateException, ClassificationNotFoundException {
|
||||
ObjectReference.validate(newTaskImpl.getPrimaryObjRef(), "primary ObjectReference", TASK);
|
||||
|
||||
if (oldTaskImpl.getExternalId() == null
|
||||
|| !(oldTaskImpl.getExternalId().equals(newTaskImpl.getExternalId()))) {
|
||||
|
@ -1589,16 +1579,6 @@ public class TaskServiceImpl implements TaskService {
|
|||
"A task's Workbasket cannot be changed via update of the task");
|
||||
}
|
||||
|
||||
if (newTaskImpl.getPlanned() == null) {
|
||||
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);
|
||||
|
@ -1614,8 +1594,6 @@ public class TaskServiceImpl implements TaskService {
|
|||
throw new InvalidStateException(
|
||||
String.format(TASK_WITH_ID_IS_NOT_READY, oldTaskImpl.getId(), oldTaskImpl.getState()));
|
||||
}
|
||||
|
||||
newTaskImpl.setModified(Instant.now());
|
||||
}
|
||||
|
||||
private void updateClassificationSummary(TaskImpl newTaskImpl, TaskImpl oldTaskImpl)
|
||||
|
|
|
@ -57,10 +57,11 @@ public class UpdateObjectsUseUtcTimeStampsAccTest extends AbstractAccTest {
|
|||
InvalidStateException {
|
||||
TaskService taskService = taskanaEngine.getTaskService();
|
||||
Task task = taskService.getTask("TKI:000000000000000000000000000000000000");
|
||||
Instant now = Instant.now();
|
||||
TaskImpl ti = (TaskImpl) task;
|
||||
task.setPlanned(Instant.now().plus(Duration.ofHours(17)));
|
||||
task.setDue(Instant.now().plus(Duration.ofHours(41)));
|
||||
ti.setCompleted(Instant.now().plus(Duration.ofHours(40)));
|
||||
task.setPlanned(now.plus(Duration.ofHours(17)));
|
||||
task.setDue(now.plus(Duration.ofHours(41))); // SL = 1D -> Due must be planned + 24 h
|
||||
ti.setCompleted(now.plus(Duration.ofHours(27)));
|
||||
|
||||
TimeZone originalZone = TimeZone.getDefault();
|
||||
Task updatedTask = taskService.updateTask(task);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package acceptance.task;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
import acceptance.AbstractAccTest;
|
||||
import java.sql.SQLException;
|
||||
|
@ -35,7 +36,7 @@ import pro.taskana.task.api.models.TaskSummary;
|
|||
/** Acceptance test for all "create task" scenarios. */
|
||||
@ExtendWith(JaasExtension.class)
|
||||
@SuppressWarnings({"checkstyle:LineLength"})
|
||||
public class SetPlannedAccTest extends AbstractAccTest {
|
||||
public class ServiceLevelPriorityAccTest extends AbstractAccTest {
|
||||
private TaskService taskService;
|
||||
|
||||
@BeforeEach
|
||||
|
@ -260,6 +261,130 @@ public class SetPlannedAccTest extends AbstractAccTest {
|
|||
assertThat(results.containsErrors()).isFalse();
|
||||
}
|
||||
|
||||
// +-----------------------------------------+------------------------------------------+------+
|
||||
// |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 |
|
||||
// +-----------------------------------------+------------------------------------------+------+
|
||||
@WithAccessId(
|
||||
userName = "admin",
|
||||
groupNames = {"group_2"})
|
||||
@Test
|
||||
void testSetPlannedPropertyOnSingleTaskWithBulkUpdate()
|
||||
throws NotAuthorizedException, TaskNotFoundException, InvalidArgumentException,
|
||||
ConcurrencyException, InvalidStateException, ClassificationNotFoundException,
|
||||
AttachmentPersistenceException {
|
||||
String taskId = "TKI:000000000000000000000000000000000002";
|
||||
Instant planned = getInstant("2020-05-03T07:00:00");
|
||||
// test bulk operation setPlanned...
|
||||
BulkOperationResults<String, TaskanaException> results =
|
||||
taskanaEngine.getTaskService().setPlannedPropertyOfTasks(planned, Arrays.asList(taskId));
|
||||
Task task = taskService.getTask(taskId);
|
||||
assertThat(results.containsErrors()).isFalse();
|
||||
DaysToWorkingDaysConverter converter = DaysToWorkingDaysConverter.initialize();
|
||||
long days = converter.convertWorkingDaysToDays(task.getPlanned(), 1);
|
||||
assertThat(task.getDue()).isEqualTo(planned.plus(Duration.ofDays(days)));
|
||||
}
|
||||
|
||||
@WithAccessId(
|
||||
userName = "admin",
|
||||
groupNames = {"group_2"})
|
||||
@Test
|
||||
void testSetPlannedPropertyOnSingleTaskWitHTaskUpdate()
|
||||
throws NotAuthorizedException, TaskNotFoundException, InvalidArgumentException,
|
||||
ConcurrencyException, InvalidStateException, ClassificationNotFoundException,
|
||||
AttachmentPersistenceException {
|
||||
String taskId = "TKI:000000000000000000000000000000000002";
|
||||
Instant planned = getInstant("2020-05-03T07:00:00");
|
||||
DaysToWorkingDaysConverter converter = DaysToWorkingDaysConverter.initialize();
|
||||
Task task = taskService.getTask(taskId);
|
||||
// test update of planned date via updateTask()
|
||||
task.setPlanned(task.getPlanned().plus(Duration.ofDays(3)));
|
||||
task = taskService.updateTask(task);
|
||||
long days = converter.convertWorkingDaysToDays(task.getPlanned(), 1);
|
||||
assertThat(task.getDue()).isEqualTo(task.getPlanned().plus(Duration.ofDays(days)));
|
||||
}
|
||||
|
||||
@WithAccessId(
|
||||
userName = "admin",
|
||||
groupNames = {"group_2"})
|
||||
@Test
|
||||
void testSetDuePropertyOnSingleTask()
|
||||
throws NotAuthorizedException, TaskNotFoundException, InvalidArgumentException,
|
||||
ConcurrencyException, InvalidStateException, ClassificationNotFoundException,
|
||||
AttachmentPersistenceException {
|
||||
String taskId = "TKI:000000000000000000000000000000000002";
|
||||
Instant planned = getInstant("2020-05-03T07:00:00");
|
||||
Task task = taskService.getTask(taskId);
|
||||
|
||||
// test update of due that fails
|
||||
task.setDue(planned.plus(Duration.ofDays(8)));
|
||||
Task finalTask = task;
|
||||
assertThatThrownBy(
|
||||
() -> {
|
||||
taskService.updateTask(finalTask);
|
||||
})
|
||||
.isInstanceOf(InvalidArgumentException.class);
|
||||
|
||||
// update due and planned as expected.
|
||||
task = taskService.getTask(taskId);
|
||||
task.setDue(planned.plus(Duration.ofDays(3)));
|
||||
DaysToWorkingDaysConverter converter = DaysToWorkingDaysConverter.initialize();
|
||||
long days = converter.convertWorkingDaysToDays(task.getDue(), -1);
|
||||
task.setPlanned(task.getDue().plus(Duration.ofDays(-1)));
|
||||
task = taskService.updateTask(task);
|
||||
days = converter.convertWorkingDaysToDays(task.getDue(), -1);
|
||||
assertThat(task.getPlanned()).isEqualTo(task.getDue().plus(Duration.ofDays(days)));
|
||||
|
||||
// update due and planned as expected.
|
||||
task = taskService.getTask(taskId);
|
||||
task.setDue(planned.plus(Duration.ofDays(3)));
|
||||
task.setPlanned(null);
|
||||
task = taskService.updateTask(task);
|
||||
days = converter.convertWorkingDaysToDays(task.getDue(), -1);
|
||||
assertThat(task.getPlanned()).isEqualTo(task.getDue().plus(Duration.ofDays(days)));
|
||||
}
|
||||
|
||||
@WithAccessId(
|
||||
userName = "admin",
|
||||
groupNames = {"group_2"})
|
||||
@Test
|
||||
void testSetPlannedPropertyOnSingleTaskUpdateWithNulls()
|
||||
throws NotAuthorizedException, TaskNotFoundException, InvalidArgumentException,
|
||||
ConcurrencyException, InvalidStateException, ClassificationNotFoundException,
|
||||
AttachmentPersistenceException {
|
||||
String taskId = "TKI:000000000000000000000000000000000002";
|
||||
final Instant planned = getInstant("2020-05-03T07:00:00");
|
||||
Task task = taskService.getTask(taskId);
|
||||
|
||||
task.setPlanned(null);
|
||||
task = taskService.updateTask(task);
|
||||
DaysToWorkingDaysConverter converter = DaysToWorkingDaysConverter.initialize();
|
||||
long days = converter.convertWorkingDaysToDays(task.getPlanned(), 1);
|
||||
assertThat(task.getDue()).isEqualTo(task.getPlanned().plus(Duration.ofDays(days)));
|
||||
|
||||
task.setDue(null);
|
||||
task = taskService.updateTask(task);
|
||||
days = converter.convertWorkingDaysToDays(task.getPlanned(), 1);
|
||||
assertThat(task.getDue()).isEqualTo(task.getPlanned().plus(Duration.ofDays(days)));
|
||||
|
||||
task.setPlanned(planned.plus(Duration.ofDays(13)));
|
||||
task.setDue(null);
|
||||
task = taskService.updateTask(task);
|
||||
days = converter.convertWorkingDaysToDays(task.getPlanned(), 1);
|
||||
assertThat(task.getDue()).isEqualTo(task.getPlanned().plus(Duration.ofDays(days)));
|
||||
|
||||
task.setDue(planned.plus(Duration.ofDays(13)));
|
||||
task.setPlanned(null);
|
||||
task = taskService.updateTask(task);
|
||||
days = converter.convertWorkingDaysToDays(task.getDue(), -1);
|
||||
assertThat(task.getDue()).isEqualTo(planned.plus(Duration.ofDays(13)));
|
||||
assertThat(task.getPlanned()).isEqualTo(task.getDue().plus(Duration.ofDays(days)));
|
||||
}
|
||||
|
||||
@WithAccessId(
|
||||
userName = "admin",
|
||||
groupNames = {"group_2"})
|
|
@ -83,26 +83,32 @@ class UpdateTaskAccTest extends AbstractAccTest {
|
|||
assertThatThrownBy(() -> taskService.updateTask(task))
|
||||
.isInstanceOf(InvalidArgumentException.class);
|
||||
|
||||
task.setPrimaryObjRef(
|
||||
Task task1 = taskService.getTask("TKI:000000000000000000000000000000000000");
|
||||
task1.setPrimaryObjRef(
|
||||
createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", null));
|
||||
assertThatThrownBy(() -> taskService.updateTask(task))
|
||||
assertThatThrownBy(() -> taskService.updateTask(task1))
|
||||
.isInstanceOf(InvalidArgumentException.class);
|
||||
|
||||
task.setPrimaryObjRef(
|
||||
Task task2 = taskService.getTask("TKI:000000000000000000000000000000000000");
|
||||
task2.setPrimaryObjRef(
|
||||
createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", null, "1234567"));
|
||||
assertThatThrownBy(() -> taskService.updateTask(task))
|
||||
assertThatThrownBy(() -> taskService.updateTask(task2))
|
||||
.isInstanceOf(InvalidArgumentException.class);
|
||||
|
||||
task.setPrimaryObjRef(createObjectReference("COMPANY_A", "SYSTEM_A", null, "VNR", "1234567"));
|
||||
assertThatThrownBy(() -> taskService.updateTask(task))
|
||||
Task task3 = taskService.getTask("TKI:000000000000000000000000000000000000");
|
||||
task3.setPrimaryObjRef(createObjectReference("COMPANY_A", "SYSTEM_A", null, "VNR", "1234567"));
|
||||
assertThatThrownBy(() -> taskService.updateTask(task3))
|
||||
.isInstanceOf(InvalidArgumentException.class);
|
||||
|
||||
task.setPrimaryObjRef(createObjectReference("COMPANY_A", null, "INSTANCE_A", "VNR", "1234567"));
|
||||
assertThatThrownBy(() -> taskService.updateTask(task))
|
||||
Task task4 = taskService.getTask("TKI:000000000000000000000000000000000000");
|
||||
task4.setPrimaryObjRef(
|
||||
createObjectReference("COMPANY_A", null, "INSTANCE_A", "VNR", "1234567"));
|
||||
assertThatThrownBy(() -> taskService.updateTask(task4))
|
||||
.isInstanceOf(InvalidArgumentException.class);
|
||||
|
||||
task.setPrimaryObjRef(createObjectReference(null, "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"));
|
||||
assertThatThrownBy(() -> taskService.updateTask(task))
|
||||
Task task5 = taskService.getTask("TKI:000000000000000000000000000000000000");
|
||||
task5.setPrimaryObjRef(createObjectReference(null, "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"));
|
||||
assertThatThrownBy(() -> taskService.updateTask(task5))
|
||||
.isInstanceOf(InvalidArgumentException.class);
|
||||
}
|
||||
|
||||
|
@ -347,7 +353,6 @@ class UpdateTaskAccTest extends AbstractAccTest {
|
|||
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))
|
||||
|
|
|
@ -86,6 +86,8 @@ class UpdateTaskAttachmentsAccTest extends AbstractAccTest {
|
|||
equalTo("12345678901234567890123456789012345678901234567890"));
|
||||
assertEquals(99, task.getPriority());
|
||||
assertEquals(task.getDue(), task.getPlanned().plus(Duration.ofDays(1)));
|
||||
|
||||
task.getAttachments().forEach(at -> assertEquals(at.getModified(), task.getModified()));
|
||||
}
|
||||
|
||||
@WithAccessId(
|
||||
|
@ -109,6 +111,7 @@ class UpdateTaskAttachmentsAccTest extends AbstractAccTest {
|
|||
task = taskService.updateTask(task);
|
||||
|
||||
assertEquals(1, task.getAttachments().size());
|
||||
task.getAttachments().forEach(at -> assertEquals(at.getModified(), task.getModified()));
|
||||
}
|
||||
|
||||
@WithAccessId(
|
||||
|
@ -327,7 +330,6 @@ class UpdateTaskAttachmentsAccTest extends AbstractAccTest {
|
|||
assertThat(task.getAttachments().size(), equalTo(attachmentCount));
|
||||
assertThat(task.getAttachments().get(0).getChannel(), equalTo(newChannel));
|
||||
assertEquals(999, task.getPriority());
|
||||
|
||||
DaysToWorkingDaysConverter converter = DaysToWorkingDaysConverter.initialize(Instant.now());
|
||||
long calendarDays = converter.convertWorkingDaysToDays(task.getDue(), 1);
|
||||
|
||||
|
@ -377,6 +379,7 @@ class UpdateTaskAttachmentsAccTest extends AbstractAccTest {
|
|||
int custAttSize = att.getCustomAttributes().size();
|
||||
if ("ROHRPOST".equals(channel)) {
|
||||
rohrpostFound = true;
|
||||
assertEquals(att.getModified(), task.getModified());
|
||||
} else if ("E-MAIL".equals(channel)) {
|
||||
emailFound = true;
|
||||
} else {
|
||||
|
@ -482,7 +485,7 @@ class UpdateTaskAttachmentsAccTest extends AbstractAccTest {
|
|||
task = taskService.updateTask(task);
|
||||
assertThat(task.getAttachments().size(), equalTo(1));
|
||||
assertThat(task.getAttachments().get(0).getChannel(), equalTo("DHL"));
|
||||
|
||||
task.getAttachments().forEach(at -> assertEquals(at.getModified(), task.getModified()));
|
||||
// setup environment for 2nd version of replacement (list.add call)
|
||||
task.getAttachments().add(attachment2);
|
||||
task = taskService.updateTask(task);
|
||||
|
@ -540,7 +543,9 @@ class UpdateTaskAttachmentsAccTest extends AbstractAccTest {
|
|||
|
||||
assertNotNull(createdTask.getId());
|
||||
assertThat(createdTask.getCreator(), equalTo(CurrentUserContext.getUserid()));
|
||||
|
||||
createdTask
|
||||
.getAttachments()
|
||||
.forEach(at -> assertEquals(at.getModified(), createdTask.getModified()));
|
||||
Task readTask = taskService.getTask(createdTask.getId());
|
||||
assertNotNull(readTask);
|
||||
assertThat(readTask.getCreator(), equalTo(CurrentUserContext.getUserid()));
|
||||
|
@ -597,6 +602,7 @@ class UpdateTaskAttachmentsAccTest extends AbstractAccTest {
|
|||
.findFirst()
|
||||
.orElse(null);
|
||||
assertNotNull(updatedAttachment);
|
||||
assertEquals(updatedAttachment.getModified(),updatedTask.getModified());
|
||||
assertEquals("TEST_VALUE", updatedAttachment.getCustomAttributes().get("TEST_KEY"));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue