Compare commits

...

5 Commits

Author SHA1 Message Date
Mustapha Zorgati 62e5814cfa Updated poms to version 6.3.3-SNAPSHOT 2023-09-25 14:07:41 +00:00
Jörg Heffner 08ef4cae57 Closes #2377 - add configuration for lock expiration period 2023-09-25 15:22:05 +02:00
Mustapha Zorgati 0565e5905c Updated poms to version 6.3.2-SNAPSHOT 2023-08-01 16:32:32 +00:00
Mustapha Zorgati 0ffa2f0c87 Closes #2347: Configuration to enable working day calculation instead of working time calculation
* added configuration property for detailed working time calculation
* added WorkingDayCalculatorImpl to replicate pre 6.0.0 behaviour
* bugfix ServiceLevelHandler comparing classificationKeys

---------

Co-authored-by: arolfes <arolfes@users.noreply.github.com>
2023-08-01 18:06:49 +02:00
arolfes 3336cbba4e Updated poms to version 6.3.1-SNAPSHOT 2023-08-01 17:24:15 +02:00
61 changed files with 3396 additions and 271 deletions

View File

@ -11,7 +11,7 @@
<parent>
<groupId>pro.taskana</groupId>
<artifactId>taskana-parent</artifactId>
<version>6.2.1-SNAPSHOT</version>
<version>6.3.3-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

View File

@ -12,7 +12,7 @@
<parent>
<groupId>pro.taskana</groupId>
<artifactId>taskana-parent</artifactId>
<version>6.2.1-SNAPSHOT</version>
<version>6.3.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -10,7 +10,7 @@
<parent>
<groupId>pro.taskana</groupId>
<artifactId>taskana-common-parent</artifactId>
<version>6.2.1-SNAPSHOT</version>
<version>6.3.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -29,6 +29,7 @@ INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000021', 'ETI:0000000
INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000022', 'ETI:000000000000000000000000000000000022', '2018-01-29 15:55:22', null , null , '2018-01-29 15:55:22', null , '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , -1 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000022' , 'DOC_0000000000000000022' , null , '00' , 'PASystem' , '00' , 'SDNR' , '11223344' , false , false , null , 'NONE' , null , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , 'abc' , '' , '' , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 );
INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000023', 'ETI:000000000000000000000000000000000023', '2018-01-29 15:55:23', null , null , '2018-01-29 15:55:23', null , '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , -1 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000023' , 'DOC_0000000000000000023' , null , '00' , 'PASystem' , '00' , 'SDNR' , '11223344' , false , false , null , 'NONE' , null , '' , '' , '' , '' , '' , '' , '' , 'lnp' , '' , '' , '' , '' , '' , 'abc' , '' , '' , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 );
INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000024', 'ETI:000000000000000000000000000000000024', '2018-01-29 15:55:24', null , null , '2018-01-29 15:55:24', null , '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , -1 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000024' , 'DOC_0000000000000000024' , null , '00' , 'PASystem' , '00' , 'SDNR' , '11223344' , false , false , null , 'NONE' , null , '' , '' , '' , '' , '' , '' , '' , '' , null , '' , '' , '' , '' , 'abc' , '' , '' , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 );
INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000201', 'ETI:000000000000000000000000000000000201', '2023-07-31 15:55:01', '2023-07-31 15:55:00', null , '2023-07-31 15:55:01', null , '2023-07-31 15:55:00', '2023-08-02 15:55:00', 'Task201' , 'creator_user_id' , 'Lorem ipsum was n Quatsch dolor sit amet.', 'Some custom Note' , 2 , -1 , 'READY' , 'EXTERN' , 'L110102' , 'CLI:100000000000000000000000000000000002', 'WBI:100000000000000000000000000000000006' , 'USER-1-1' , 'DOMAIN_A', 'BPI21' , 'PBPI21' , 'user-1-1' , 'MyCompany1', 'MySystem1', 'MyInstance1' , 'MyType1', 'MyValue1' , true , false , null , 'NONE' , null , 'pqr' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , 'abc' , '' , '' , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 );
-- TASK TABLE (ID , EXTERNAL_ID , CREATED , CLAIMED , COMPLETED , modified , received , planned , due , name , creator , description , note , priority, manual_priority, state , classification_category , classification_key, classification_id , workbasket_id , workbasket_key, domain , business_process_id, parent_business_process_id, owner , por_company , por_system , por_system_instance, por_type , por_value , is_read, is_transferred,callback_info , callback_state , custom_attributes ,custom1 ,custom2, ,custom3, ,custom4 ,custom5 ,custom6 ,custom7 ,custom8 ,custom9 ,custom10 ,custom11, ,custom12 ,custom13 ,custom14 ,custom15 ,custom16 , custom-int-1, custom-int-2, custom-int-3, custom-int-4, custom-int-5, custom-int-6, custom-int-7, custom-int-8
-- Tasks for WorkOnTaskAccTest
INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000025', 'ETI:000000000000000000000000000000000025', '2018-01-29 15:55:24', null , null , '2018-01-29 15:55:24', '2018-01-29 15:55:24', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , -1 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000007' , 'USER-1-2' , 'DOMAIN_A', 'PI_0000000000025' , 'DOC_0000000000000000025' , null , 'abcd00' , 'PASystem' , '00' , 'SDNR' , '98765432' , false , false , null , 'NONE' , null , '' , '' , '' , '' , '' , '' , '' , '' , '' , 'ert' , 'ert' , 'ert' , 'ert' , 'abc' , 'ert' , 'ert' , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 );

View File

@ -11,7 +11,7 @@
<parent>
<groupId>pro.taskana</groupId>
<artifactId>taskana-common-parent</artifactId>
<version>6.2.1-SNAPSHOT</version>
<version>6.3.3-SNAPSHOT</version>
</parent>
<dependencies>

View File

@ -11,7 +11,7 @@
<parent>
<groupId>pro.taskana</groupId>
<artifactId>taskana-common-parent</artifactId>
<version>6.2.1-SNAPSHOT</version>
<version>6.3.3-SNAPSHOT</version>
</parent>
<properties>

View File

@ -11,7 +11,7 @@
<parent>
<groupId>pro.taskana</groupId>
<artifactId>taskana-common-parent</artifactId>
<version>6.2.1-SNAPSHOT</version>
<version>6.3.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -11,7 +11,7 @@
<parent>
<groupId>pro.taskana</groupId>
<artifactId>taskana-common-parent</artifactId>
<version>6.2.1-SNAPSHOT</version>
<version>6.3.3-SNAPSHOT</version>
</parent>
<dependencies>

View File

@ -0,0 +1,118 @@
package pro.taskana.common.internal.workingtime;
import java.time.DayOfWeek;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.stream.LongStream;
import pro.taskana.common.api.WorkingTimeCalculator;
import pro.taskana.common.api.exceptions.InvalidArgumentException;
import pro.taskana.common.api.exceptions.SystemException;
public class WorkingDayCalculatorImpl implements WorkingTimeCalculator {
private final ZoneId zoneId;
private final HolidaySchedule holidaySchedule;
public WorkingDayCalculatorImpl(HolidaySchedule holidaySchedule, ZoneId zoneId) {
this.holidaySchedule = holidaySchedule;
this.zoneId = zoneId;
}
@Override
public Instant subtractWorkingTime(Instant workStart, Duration workingTime)
throws InvalidArgumentException {
long days = convertWorkingDaysToDays(workStart, -workingTime.toDays(), ZeroDirection.SUB_DAYS);
return workStart.plus(Duration.ofDays(days));
}
@Override
public Instant addWorkingTime(Instant workStart, Duration workingTime)
throws InvalidArgumentException {
long days = convertWorkingDaysToDays(workStart, workingTime.toDays(), ZeroDirection.ADD_DAYS);
return workStart.plus(Duration.ofDays(days));
}
@Override
public Duration workingTimeBetween(Instant first, Instant second)
throws InvalidArgumentException {
long days = Duration.between(first, second).abs().toDays();
Instant firstInstant = first.isBefore(second) ? first : second;
long workingDaysBetween =
LongStream.range(1, days)
.mapToObj(day -> isWorkingDay(firstInstant.plus(day, ChronoUnit.DAYS)))
.filter(t -> t)
.count();
return Duration.ofDays(workingDaysBetween);
}
@Override
public boolean isWorkingDay(Instant instant) {
return !isWeekend(instant) && !isHoliday(instant);
}
@Override
public boolean isWeekend(Instant instant) {
DayOfWeek dayOfWeek = toDayOfWeek(instant);
return dayOfWeek == DayOfWeek.SATURDAY || dayOfWeek == DayOfWeek.SUNDAY;
}
@Override
public boolean isHoliday(Instant instant) {
return holidaySchedule.isHoliday(toLocalDate(instant));
}
@Override
public boolean isGermanHoliday(Instant instant) {
return holidaySchedule.isGermanHoliday(toLocalDate(instant));
}
private long convertWorkingDaysToDays(
final Instant startTime, long numberOfDays, ZeroDirection zeroDirection) {
if (startTime == null) {
throw new SystemException(
"Internal Error: convertWorkingDaysToDays was called with a null startTime");
}
int direction = calculateDirection(numberOfDays, zeroDirection);
long limit = Math.abs(numberOfDays);
return LongStream.iterate(0, i -> i + direction)
.filter(day -> isWorkingDay(startTime.plus(day, ChronoUnit.DAYS)))
.skip(limit)
.findFirst()
.orElse(0);
}
private int calculateDirection(long numberOfDays, ZeroDirection zeroDirection) {
if (numberOfDays == 0) {
return zeroDirection.getDirection();
} else {
return numberOfDays >= 0 ? 1 : -1;
}
}
private LocalDate toLocalDate(Instant instant) {
return LocalDate.ofInstant(instant, zoneId);
}
private DayOfWeek toDayOfWeek(Instant instant) {
return toLocalDate(instant).getDayOfWeek();
}
private enum ZeroDirection {
SUB_DAYS(-1),
ADD_DAYS(1);
private final int direction;
ZeroDirection(int direction) {
this.direction = direction;
}
public int getDirection() {
return direction;
}
}
}

View File

@ -12,7 +12,7 @@
<parent>
<groupId>pro.taskana</groupId>
<artifactId>taskana-parent</artifactId>
<version>6.2.1-SNAPSHOT</version>
<version>6.3.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -11,7 +11,7 @@
<parent>
<groupId>pro.taskana.history</groupId>
<artifactId>taskana-history-parent</artifactId>
<version>6.2.1-SNAPSHOT</version>
<version>6.3.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -11,7 +11,7 @@
<parent>
<groupId>pro.taskana.history</groupId>
<artifactId>taskana-history-parent</artifactId>
<version>6.2.1-SNAPSHOT</version>
<version>6.3.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -14,6 +14,7 @@ import java.util.Set;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pro.taskana.TaskanaConfiguration;
import pro.taskana.common.api.ScheduledJob;
import pro.taskana.common.api.TaskanaEngine;
import pro.taskana.common.api.TimeInterval;
@ -52,6 +53,10 @@ public class HistoryCleanupJob extends AbstractTaskanaJob {
super(taskanaEngine, txProvider, scheduledJob, true);
}
public static Duration getLockExpirationPeriod(TaskanaConfiguration taskanaConfiguration) {
return taskanaConfiguration.getSimpleHistoryCleanupJobLockExpirationPeriod();
}
@Override
public void execute() {
Instant createdBefore = Instant.now().minus(minimumAge);

View File

@ -11,7 +11,7 @@
<parent>
<groupId>pro.taskana.history</groupId>
<artifactId>taskana-history-parent</artifactId>
<version>6.2.1-SNAPSHOT</version>
<version>6.3.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -12,7 +12,7 @@
<parent>
<groupId>pro.taskana</groupId>
<artifactId>taskana-parent</artifactId>
<version>6.2.1-SNAPSHOT</version>
<version>6.3.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -11,7 +11,7 @@
<parent>
<groupId>pro.taskana</groupId>
<artifactId>taskana-lib-parent</artifactId>
<version>6.2.1-SNAPSHOT</version>
<version>6.3.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -10,7 +10,7 @@
<parent>
<groupId>pro.taskana</groupId>
<artifactId>taskana-lib-parent</artifactId>
<version>6.2.1-SNAPSHOT</version>
<version>6.3.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -10,7 +10,7 @@
<parent>
<groupId>pro.taskana</groupId>
<artifactId>taskana-lib-parent</artifactId>
<version>6.2.1-SNAPSHOT</version>
<version>6.3.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -62,6 +62,7 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.platform.commons.support.AnnotationSupport;
import pro.taskana.TaskanaConfiguration;
import pro.taskana.common.api.TaskanaEngine;
import pro.taskana.common.api.WorkingTimeCalculator;
import pro.taskana.common.api.exceptions.ErrorCode;
import pro.taskana.common.api.exceptions.TaskanaException;
import pro.taskana.common.api.exceptions.TaskanaRuntimeException;
@ -71,7 +72,6 @@ import pro.taskana.common.internal.TaskanaEngineImpl;
import pro.taskana.common.internal.jobs.JobScheduler;
import pro.taskana.common.internal.logging.LoggingAspect;
import pro.taskana.common.internal.workingtime.HolidaySchedule;
import pro.taskana.common.internal.workingtime.WorkingTimeCalculatorImpl;
import pro.taskana.testapi.TaskanaIntegrationTest;
/**
@ -437,7 +437,7 @@ class ArchitectureTest {
.that()
.areNotAssignableFrom(ArchitectureTest.class)
.and()
.areNotAssignableTo(WorkingTimeCalculatorImpl.class)
.areNotAssignableTo(WorkingTimeCalculator.class)
.and()
.areNotAssignableTo(TaskanaEngineImpl.class)
.and()

View File

@ -251,6 +251,7 @@ class TaskanaConfigurationTest {
Map<String, List<String>> expectedClassificationCategories =
Map.of("TYPE_A", List.of("CATEGORY_A"), "TYPE_B", List.of("CATEGORY_B"));
// working time configuration
boolean expectedUseDetailedWorkingTimeCalculation = false;
Map<DayOfWeek, Set<LocalTimeInterval>> expectedWorkingTimeSchedule =
Map.of(DayOfWeek.MONDAY, Set.of(new LocalTimeInterval(LocalTime.MIN, LocalTime.NOON)));
ZoneId expectedWorkingTimeScheduleTimeZone = ZoneId.ofOffset("UTC", ZoneOffset.ofHours(4));
@ -269,21 +270,27 @@ class TaskanaConfigurationTest {
int expectedJobBatchSize = 50;
Instant expectedJobFirstJun = Instant.MIN;
Duration expectedJobRunEvery = Duration.ofDays(2);
Duration expectedJobLockExpirationPeriod = Duration.ofDays(2);
boolean expectedTaskCleanupJobEnabled = false;
Duration expectedTaskCleanupJobMinimumAge = Duration.ofDays(1);
boolean expectedTaskCleanupJobAllCompletedSameParentBusiness = false;
Duration expectedTaskCleanupJobLockExpirationPeriod = Duration.ofDays(2);
boolean expectedWorkbasketCleanupJobEnabled = false;
Duration expectedWorkbasketCleanupJobLockExpirationPeriod = Duration.ofDays(2);
boolean expectedSimpleHistoryCleanupJobEnabled = true;
int expectedSimpleHistoryCleanupJobBatchSize = 16;
Duration expectedSimpleHistoryCleanupJobMinimumAge = Duration.ofHours(3);
boolean expectedSimpleHistoryCleanupJobAllCompletedSameParentBusiness = false;
Duration expectedSimpleHistoryCleanupJobLockExpirationPeriod = Duration.ofDays(2);
boolean expectedTaskUpdatePriorityJobEnabled = true;
int expectedPriorityJobBatchSize = 49;
Instant expectedPriorityJobFirstRun = Instant.MIN.plus(1, ChronoUnit.DAYS);
Duration expectedTaskUpdatePriorityJobRunEvery = Duration.ofMinutes(17);
Duration expectedTaskUpdatePriorityJobLockExpirationPeriod = Duration.ofDays(2);
boolean expectedUserInfoRefreshJobEnabled = true;
Instant expectedUserRefreshJobFirstRun = Instant.MIN.plus(2, ChronoUnit.DAYS);
Duration expectedUserRefreshJobRunEvery = Duration.ofDays(5);
Duration expectedUserRefreshJobLockExpirationPeriod = Duration.ofDays(2);
Set<String> expectedJobSchedulerCustomJobs = Set.of("Job_A", "Job_B");
// user configuration
boolean expectedAddAdditionalUserInfo = true;
@ -308,6 +315,7 @@ class TaskanaConfigurationTest {
.classificationTypes(expectedClassificationTypes)
.classificationCategoriesByType(expectedClassificationCategories)
// working time configuration
.useWorkingTimeCalculation(expectedUseDetailedWorkingTimeCalculation)
.workingTimeSchedule(expectedWorkingTimeSchedule)
.workingTimeScheduleTimeZone(expectedWorkingTimeScheduleTimeZone)
.customHolidays(expectedCustomHolidays)
@ -327,23 +335,32 @@ class TaskanaConfigurationTest {
.jobBatchSize(expectedJobBatchSize)
.jobFirstRun(expectedJobFirstJun)
.jobRunEvery(expectedJobRunEvery)
.jobLockExpirationPeriod(expectedJobLockExpirationPeriod)
.taskCleanupJobEnabled(expectedTaskCleanupJobEnabled)
.taskCleanupJobMinimumAge(expectedTaskCleanupJobMinimumAge)
.taskCleanupJobAllCompletedSameParentBusiness(
expectedTaskCleanupJobAllCompletedSameParentBusiness)
.taskCleanupJobLockExpirationPeriod(expectedTaskCleanupJobLockExpirationPeriod)
.workbasketCleanupJobEnabled(expectedWorkbasketCleanupJobEnabled)
.workbasketCleanupJobLockExpirationPeriod(
expectedWorkbasketCleanupJobLockExpirationPeriod)
.simpleHistoryCleanupJobEnabled(expectedSimpleHistoryCleanupJobEnabled)
.simpleHistoryCleanupJobBatchSize(expectedSimpleHistoryCleanupJobBatchSize)
.simpleHistoryCleanupJobMinimumAge(expectedSimpleHistoryCleanupJobMinimumAge)
.simpleHistoryCleanupJobAllCompletedSameParentBusiness(
expectedSimpleHistoryCleanupJobAllCompletedSameParentBusiness)
.simpleHistoryCleanupJobLockExpirationPeriod(
expectedSimpleHistoryCleanupJobLockExpirationPeriod)
.taskUpdatePriorityJobEnabled(expectedTaskUpdatePriorityJobEnabled)
.taskUpdatePriorityJobBatchSize(expectedPriorityJobBatchSize)
.taskUpdatePriorityJobFirstRun(expectedPriorityJobFirstRun)
.taskUpdatePriorityJobRunEvery(expectedTaskUpdatePriorityJobRunEvery)
.taskUpdatePriorityJobLockExpirationPeriod(
expectedTaskUpdatePriorityJobLockExpirationPeriod)
.userInfoRefreshJobEnabled(expectedUserInfoRefreshJobEnabled)
.userRefreshJobFirstRun(expectedUserRefreshJobFirstRun)
.userRefreshJobRunEvery(expectedUserRefreshJobRunEvery)
.userRefreshJobLockExpirationPeriod(expectedUserRefreshJobLockExpirationPeriod)
.customJobs(expectedJobSchedulerCustomJobs)
// user configuration
.addAdditionalUserInfo(expectedAddAdditionalUserInfo)
@ -368,6 +385,8 @@ class TaskanaConfigurationTest {
assertThat(configuration.getClassificationCategoriesByType())
.isEqualTo(expectedClassificationCategories);
// working time configuration
assertThat(configuration.isUseWorkingTimeCalculation())
.isEqualTo(expectedUseDetailedWorkingTimeCalculation);
assertThat(configuration.getWorkingTimeSchedule()).isEqualTo(expectedWorkingTimeSchedule);
assertThat(configuration.getWorkingTimeScheduleTimeZone())
.isEqualTo(expectedWorkingTimeScheduleTimeZone);
@ -442,6 +461,7 @@ class TaskanaConfigurationTest {
.classificationCategoriesByType(
Map.of("typeA", List.of("categoryA"), "typeB", List.of("categoryB")))
// working time configuration
.useWorkingTimeCalculation(false)
.workingTimeSchedule(
Map.of(
DayOfWeek.MONDAY,
@ -462,21 +482,27 @@ class TaskanaConfigurationTest {
.jobBatchSize(50)
.jobFirstRun(Instant.MIN)
.jobRunEvery(Duration.ofDays(2))
.jobLockExpirationPeriod(Duration.ofDays(2))
.taskCleanupJobEnabled(false)
.taskCleanupJobMinimumAge(Duration.ofDays(1))
.taskCleanupJobAllCompletedSameParentBusiness(false)
.taskCleanupJobLockExpirationPeriod(Duration.ofDays(6))
.workbasketCleanupJobEnabled(false)
.workbasketCleanupJobLockExpirationPeriod(Duration.ofDays(7))
.simpleHistoryCleanupJobEnabled(true)
.simpleHistoryCleanupJobBatchSize(16)
.simpleHistoryCleanupJobMinimumAge(Duration.ofHours(3))
.simpleHistoryCleanupJobAllCompletedSameParentBusiness(false)
.simpleHistoryCleanupJobLockExpirationPeriod(Duration.ofDays(9))
.taskUpdatePriorityJobEnabled(true)
.taskUpdatePriorityJobBatchSize(49)
.taskUpdatePriorityJobFirstRun(Instant.MIN.plus(1, ChronoUnit.DAYS))
.taskUpdatePriorityJobRunEvery(Duration.ofMinutes(17))
.taskUpdatePriorityJobLockExpirationPeriod(Duration.ofDays(10))
.userInfoRefreshJobEnabled(true)
.userRefreshJobFirstRun(Instant.MIN.plus(2, ChronoUnit.DAYS))
.userRefreshJobRunEvery(Duration.ofDays(5))
.userRefreshJobLockExpirationPeriod(Duration.ofDays(8))
.customJobs(Set.of("Job_A", "Job_B"))
// user configuration
.addAdditionalUserInfo(true)

View File

@ -46,6 +46,7 @@ import pro.taskana.task.api.models.Task;
import pro.taskana.task.internal.models.TaskImpl;
import pro.taskana.testapi.TaskanaInject;
import pro.taskana.testapi.TaskanaIntegrationTest;
import pro.taskana.testapi.builder.TaskBuilder;
import pro.taskana.testapi.builder.WorkbasketAccessItemBuilder;
import pro.taskana.testapi.security.WithAccessId;
import pro.taskana.workbasket.api.WorkbasketPermission;
@ -185,6 +186,50 @@ class UpdateClassificationAccTest {
@Nested
class UpdatePriorityAndServiceLevelTest {
@WithAccessId(user = "businessadmin")
@Test
void should_ChangeDueDate_When_ServiceLevelOfClassificationHasChanged() throws Exception {
Classification classification =
defaultTestClassification()
.priority(1)
.serviceLevel("P1D")
.buildAndStore(classificationService);
WorkbasketSummary workbasketSummary =
defaultTestWorkbasket().buildAndStoreAsSummary(workbasketService);
WorkbasketAccessItemBuilder.newWorkbasketAccessItem()
.workbasketId(workbasketSummary.getId())
.accessId(currentUserContext.getUserid())
.permission(WorkbasketPermission.OPEN)
.permission(WorkbasketPermission.READ)
.permission(WorkbasketPermission.READTASKS)
.permission(WorkbasketPermission.APPEND)
.buildAndStore(workbasketService, "businessadmin");
Task task = new TaskBuilder()
.classificationSummary(classification.asSummary())
.workbasketSummary(workbasketSummary)
.primaryObjRef(defaultTestObjectReference().build())
.planned(Instant.parse("2021-04-27T15:34:00.000Z"))
.due(null)
.buildAndStore(taskService);
classificationService.updateClassification(classification);
runAssociatedJobs();
// read again the task from DB
task = taskService.getTask(task.getId());
assertThat(task.getClassificationSummary().getServiceLevel()).isEqualTo("P1D");
assertThat(task.getDue()).isAfterOrEqualTo("2021-04-28T15:33:59.999Z");
classification.setServiceLevel("P3D");
classificationService.updateClassification(classification);
runAssociatedJobs();
// read again the task from DB
task = taskService.getTask(task.getId());
assertThat(task.getClassificationSummary().getServiceLevel()).isEqualTo("P3D");
assertThat(task.getDue()).isEqualTo("2021-04-30T15:33:59.999Z");
}
@WithAccessId(user = "businessadmin")
@Test
void should_NotThrowException_When_UpdatingClassificationWithEmptyServiceLevel()

View File

@ -0,0 +1,933 @@
package acceptance.classification.update;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static pro.taskana.testapi.DefaultTestEntities.defaultTestClassification;
import static pro.taskana.testapi.DefaultTestEntities.defaultTestObjectReference;
import static pro.taskana.testapi.DefaultTestEntities.defaultTestWorkbasket;
import static pro.taskana.testapi.builder.TaskBuilder.newTask;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestFactory;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.function.ThrowingConsumer;
import pro.taskana.TaskanaConfiguration.Builder;
import pro.taskana.classification.api.ClassificationCustomField;
import pro.taskana.classification.api.ClassificationService;
import pro.taskana.classification.api.exceptions.ClassificationNotFoundException;
import pro.taskana.classification.api.models.Classification;
import pro.taskana.classification.api.models.ClassificationSummary;
import pro.taskana.classification.internal.models.ClassificationImpl;
import pro.taskana.common.api.TaskanaEngine;
import pro.taskana.common.api.TaskanaRole;
import pro.taskana.common.api.WorkingTimeCalculator;
import pro.taskana.common.api.exceptions.ConcurrencyException;
import pro.taskana.common.api.exceptions.InvalidArgumentException;
import pro.taskana.common.api.exceptions.NotAuthorizedException;
import pro.taskana.common.api.security.CurrentUserContext;
import pro.taskana.common.internal.jobs.JobRunner;
import pro.taskana.common.internal.util.Pair;
import pro.taskana.task.api.TaskService;
import pro.taskana.task.api.models.Attachment;
import pro.taskana.task.api.models.Task;
import pro.taskana.task.internal.models.TaskImpl;
import pro.taskana.testapi.TaskanaConfigurationModifier;
import pro.taskana.testapi.TaskanaInject;
import pro.taskana.testapi.TaskanaIntegrationTest;
import pro.taskana.testapi.builder.TaskBuilder;
import pro.taskana.testapi.builder.WorkbasketAccessItemBuilder;
import pro.taskana.testapi.security.WithAccessId;
import pro.taskana.workbasket.api.WorkbasketPermission;
import pro.taskana.workbasket.api.WorkbasketService;
import pro.taskana.workbasket.api.models.WorkbasketSummary;
@TaskanaIntegrationTest
public class UpdateClassificationWithWorkingDayCalculatorAccTest
implements TaskanaConfigurationModifier {
@TaskanaInject ClassificationService classificationService;
@TaskanaInject TaskanaEngine taskanaEngine;
@TaskanaInject TaskService taskService;
@TaskanaInject WorkbasketService workbasketService;
@TaskanaInject WorkingTimeCalculator workingTimeCalculator;
@TaskanaInject CurrentUserContext currentUserContext;
@Override
public Builder modify(Builder builder) {
return builder
.workingTimeScheduleTimeZone(ZoneId.of("UTC"))
.useWorkingTimeCalculation(false); // switch to WorkingDayCalculatorImpl
}
@WithAccessId(user = "businessadmin")
@Test
void should_SetFieldsCorrectly_When_TryingToUpdateClassification() throws Exception {
Classification parentClassification =
defaultTestClassification().buildAndStore(classificationService);
Classification classification =
defaultTestClassification().type("TASK").buildAndStore(classificationService);
final Instant createdBefore = classification.getCreated();
final Instant modifiedBefore = classification.getModified();
classification.setApplicationEntryPoint("newEntrypoint");
classification.setCategory("PROCESS");
classification.setCustomField(ClassificationCustomField.CUSTOM_1, "newCustom1");
classification.setCustomField(ClassificationCustomField.CUSTOM_2, "newCustom2");
classification.setCustomField(ClassificationCustomField.CUSTOM_3, "newCustom3");
classification.setCustomField(ClassificationCustomField.CUSTOM_4, "newCustom4");
classification.setCustomField(ClassificationCustomField.CUSTOM_5, "newCustom5");
classification.setCustomField(ClassificationCustomField.CUSTOM_6, "newCustom6");
classification.setCustomField(ClassificationCustomField.CUSTOM_7, "newCustom7");
classification.setCustomField(ClassificationCustomField.CUSTOM_8, "newCustom8");
classification.setDescription("newDescription");
classification.setIsValidInDomain(false);
classification.setName("newName");
classification.setParentId(parentClassification.getId());
classification.setParentKey(parentClassification.getKey());
classification.setPriority(1000);
classification.setServiceLevel("P3D");
classificationService.updateClassification(classification);
Classification updatedClassification =
classificationService.getClassification(classification.getKey(), "DOMAIN_A");
ClassificationImpl expectedClassification =
(ClassificationImpl)
defaultTestClassification()
.type("TASK")
.applicationEntryPoint("newEntrypoint")
.category("PROCESS")
.customAttribute(ClassificationCustomField.CUSTOM_1, "newCustom1")
.customAttribute(ClassificationCustomField.CUSTOM_2, "newCustom2")
.customAttribute(ClassificationCustomField.CUSTOM_3, "newCustom3")
.customAttribute(ClassificationCustomField.CUSTOM_4, "newCustom4")
.customAttribute(ClassificationCustomField.CUSTOM_5, "newCustom5")
.customAttribute(ClassificationCustomField.CUSTOM_6, "newCustom6")
.customAttribute(ClassificationCustomField.CUSTOM_7, "newCustom7")
.customAttribute(ClassificationCustomField.CUSTOM_8, "newCustom8")
.description("newDescription")
.isValidInDomain(false)
.name("newName")
.parentId(parentClassification.getId())
.parentKey(parentClassification.getKey())
.priority(1000)
.serviceLevel("P3D")
.created(createdBefore)
.modified(updatedClassification.getModified())
.buildAndStore(classificationService);
expectedClassification.setKey(updatedClassification.getKey());
expectedClassification.setId(updatedClassification.getId());
assertThat(expectedClassification).hasNoNullFieldsOrProperties();
assertThat(modifiedBefore).isBefore(classification.getModified());
assertThat(updatedClassification).isEqualTo(expectedClassification);
}
private String createTaskWithExistingClassification(ClassificationSummary classificationSummary)
throws Exception {
WorkbasketSummary workbasketSummary =
defaultTestWorkbasket().buildAndStoreAsSummary(workbasketService);
WorkbasketAccessItemBuilder.newWorkbasketAccessItem()
.workbasketId(workbasketSummary.getId())
.accessId(currentUserContext.getUserid())
.permission(WorkbasketPermission.OPEN)
.permission(WorkbasketPermission.READ)
.permission(WorkbasketPermission.READTASKS)
.permission(WorkbasketPermission.APPEND)
.buildAndStore(workbasketService, "businessadmin");
return newTask()
.classificationSummary(classificationSummary)
.workbasketSummary(workbasketSummary)
.primaryObjRef(defaultTestObjectReference().build())
.buildAndStore(taskService)
.getId();
}
private List<String> createTasksWithExistingClassificationInAttachment(
ClassificationSummary classificationSummary, String serviceLevel, int priority, int amount)
throws Exception {
List<String> taskList = new ArrayList<>();
WorkbasketSummary workbasketSummary =
defaultTestWorkbasket().buildAndStoreAsSummary(workbasketService);
WorkbasketAccessItemBuilder.newWorkbasketAccessItem()
.workbasketId(workbasketSummary.getId())
.accessId(currentUserContext.getUserid())
.permission(WorkbasketPermission.OPEN)
.permission(WorkbasketPermission.READ)
.permission(WorkbasketPermission.READTASKS)
.permission(WorkbasketPermission.APPEND)
.buildAndStore(workbasketService, "businessadmin");
ClassificationSummary classificationSummaryWithSpecifiedServiceLevel =
defaultTestClassification()
.serviceLevel(serviceLevel)
.priority(priority)
.buildAndStoreAsSummary(classificationService);
for (int i = 0; i < amount; i++) {
Attachment attachment = taskService.newAttachment();
attachment.setClassificationSummary(classificationSummary);
attachment.setObjectReference(defaultTestObjectReference().build());
taskList.add(
newTask()
.classificationSummary(classificationSummaryWithSpecifiedServiceLevel)
.workbasketSummary(workbasketSummary)
.primaryObjRef(defaultTestObjectReference().build())
.attachments(attachment)
.buildAndStore(taskService)
.getId());
}
return taskList;
}
@TestInstance(Lifecycle.PER_CLASS)
@Nested
class UpdatePriorityAndServiceLevelTest {
@WithAccessId(user = "businessadmin")
@Test
void should_ChangeDueDate_When_ClassificationOfTaskHasChanged() throws Exception {
final Instant plannedDate = Instant.parse("2021-04-27T15:34:00.000Z");
final String expectedDue1 = plannedDate.plus(1, ChronoUnit.DAYS).toString();
final String expectedDue3 = plannedDate.plus(3, ChronoUnit.DAYS).toString();
final Classification classificationWithSL1 =
defaultTestClassification()
.priority(1)
.serviceLevel("P1D")
.buildAndStore(classificationService);
final Classification classificationWithSL3 =
defaultTestClassification()
.priority(1)
.serviceLevel("P3D")
.buildAndStore(classificationService);
WorkbasketSummary workbasketSummary =
defaultTestWorkbasket().buildAndStoreAsSummary(workbasketService);
WorkbasketAccessItemBuilder.newWorkbasketAccessItem()
.workbasketId(workbasketSummary.getId())
.accessId(currentUserContext.getUserid())
.permission(WorkbasketPermission.OPEN)
.permission(WorkbasketPermission.READ)
.permission(WorkbasketPermission.READTASKS)
.permission(WorkbasketPermission.EDITTASKS)
.permission(WorkbasketPermission.APPEND)
.buildAndStore(workbasketService, "businessadmin");
Task task =
new TaskBuilder()
.classificationSummary(classificationWithSL1.asSummary())
.workbasketSummary(workbasketSummary)
.primaryObjRef(defaultTestObjectReference().build())
.planned(plannedDate)
.due(null)
.buildAndStore(taskService);
classificationService.updateClassification(classificationWithSL1);
runAssociatedJobs();
// read again the task from DB
task = taskService.getTask(task.getId());
assertThat(task.getClassificationSummary().getServiceLevel()).isEqualTo("P1D");
assertThat(task.getDue()).isAfterOrEqualTo(expectedDue1);
task.setClassificationKey(classificationWithSL3.getKey());
taskService.updateTask(task);
// read again the task from DB
task = taskService.getTask(task.getId());
assertThat(task.getClassificationSummary().getServiceLevel()).isEqualTo("P3D");
assertThat(task.getDue()).isEqualTo(expectedDue3);
}
@WithAccessId(user = "businessadmin")
@Test
void should_ChangeDueDate_When_ServiceLevelOfClassificationHasChanged() throws Exception {
Classification classification =
defaultTestClassification()
.priority(1)
.serviceLevel("P1D")
.buildAndStore(classificationService);
WorkbasketSummary workbasketSummary =
defaultTestWorkbasket().buildAndStoreAsSummary(workbasketService);
WorkbasketAccessItemBuilder.newWorkbasketAccessItem()
.workbasketId(workbasketSummary.getId())
.accessId(currentUserContext.getUserid())
.permission(WorkbasketPermission.OPEN)
.permission(WorkbasketPermission.READ)
.permission(WorkbasketPermission.READTASKS)
.permission(WorkbasketPermission.APPEND)
.buildAndStore(workbasketService, "businessadmin");
Task task =
new TaskBuilder()
.classificationSummary(classification.asSummary())
.workbasketSummary(workbasketSummary)
.primaryObjRef(defaultTestObjectReference().build())
.planned(Instant.parse("2021-04-27T15:34:00.000Z"))
.due(null)
.buildAndStore(taskService);
classificationService.updateClassification(classification);
runAssociatedJobs();
// read again the task from DB
task = taskService.getTask(task.getId());
assertThat(task.getClassificationSummary().getServiceLevel()).isEqualTo("P1D");
assertThat(task.getDue()).isAfterOrEqualTo("2021-04-28T15:34:00.000Z");
classification.setServiceLevel("P3D");
classificationService.updateClassification(classification);
runAssociatedJobs();
// read again the task from DB
task = taskService.getTask(task.getId());
assertThat(task.getClassificationSummary().getServiceLevel()).isEqualTo("P3D");
assertThat(task.getDue()).isEqualTo("2021-04-30T15:34:00.000Z");
}
@WithAccessId(user = "businessadmin")
@Test
void should_NotThrowException_When_UpdatingClassificationWithEmptyServiceLevel()
throws Exception {
Classification classification =
defaultTestClassification().serviceLevel("P1D").buildAndStore(classificationService);
classification.setServiceLevel("");
assertThatCode(() -> classificationService.updateClassification(classification))
.doesNotThrowAnyException();
assertThat(classificationService.getClassification(classification.getId()).getServiceLevel())
.isEqualTo("P0D");
}
@WithAccessId(user = "businessadmin")
@TestFactory
Stream<DynamicTest>
should_SetDefaultServiceLevel_When_TryingToUpdateClassificationWithMissingServiceLevel()
throws Exception {
Classification classification =
defaultTestClassification().serviceLevel("P1D").buildAndStore(classificationService);
List<Pair<Classification, String>> inputList =
List.of(Pair.of(classification, null), Pair.of(classification, ""));
ThrowingConsumer<Pair<Classification, String>> test =
input -> {
input.getLeft().setServiceLevel(input.getRight());
classificationService.updateClassification(input.getLeft());
assertThat(
classificationService
.getClassification(input.getLeft().getId())
.getServiceLevel())
.isEqualTo("P0D");
};
return DynamicTest.stream(
inputList.iterator(), i -> String.format("for %s", i.getRight()), test);
}
@WithAccessId(user = "businessadmin")
@Test
void should_UpdateTaskServiceLevel_When_UpdateClassificationInTask() throws Exception {
final Instant before = Instant.now();
Classification classification =
defaultTestClassification()
.priority(1)
.serviceLevel("P13D")
.buildAndStore(classificationService);
final List<String> directLinkedTask =
List.of(createTaskWithExistingClassification(classification.asSummary()));
classification.setServiceLevel("P15D");
classificationService.updateClassification(classification);
runAssociatedJobs();
validateTaskProperties(before, directLinkedTask, taskService, workingTimeCalculator, 15, 1);
}
@WithAccessId(user = "businessadmin")
@Test
void should_UpdateTaskPriority_When_UpdateClassificationInTask() throws Exception {
final Instant before = Instant.now();
Classification classification =
defaultTestClassification()
.priority(1)
.serviceLevel("P13D")
.buildAndStore(classificationService);
final List<String> directLinkedTask =
List.of(createTaskWithExistingClassification(classification.asSummary()));
classification.setPriority(1000);
classificationService.updateClassification(classification);
runAssociatedJobs();
validateTaskProperties(
before, directLinkedTask, taskService, workingTimeCalculator, 13, 1000);
}
@WithAccessId(user = "businessadmin")
@Test
void should_UpdateTaskPriorityAndServiceLevel_When_UpdateClassificationInTask()
throws Exception {
final Instant before = Instant.now();
Classification classification =
defaultTestClassification()
.priority(1)
.serviceLevel("P13D")
.buildAndStore(classificationService);
final List<String> directLinkedTask =
List.of(createTaskWithExistingClassification(classification.asSummary()));
classification.setServiceLevel("P15D");
classification.setPriority(1000);
classificationService.updateClassification(classification);
runAssociatedJobs();
validateTaskProperties(
before, directLinkedTask, taskService, workingTimeCalculator, 15, 1000);
}
@WithAccessId(user = "businessadmin")
@TestFactory
Stream<DynamicTest> should_UpdateTaskServiceLevel_When_UpdateClassificationInAttachment() {
List<Pair<String, Integer>> inputs =
List.of(Pair.of("P5D", 2), Pair.of("P8D", 3), Pair.of("P16D", 4));
List<Pair<Integer, Integer>> outputs = List.of(Pair.of(1, 2), Pair.of(1, 3), Pair.of(1, 4));
List<Pair<Pair<String, Integer>, Pair<Integer, Integer>>> zippedTestInputList =
IntStream.range(0, inputs.size())
.mapToObj(i -> Pair.of(inputs.get(i), outputs.get(i)))
.collect(Collectors.toList());
ThrowingConsumer<Pair<Pair<String, Integer>, Pair<Integer, Integer>>> test =
input -> {
final Instant before = Instant.now();
Classification classification =
defaultTestClassification()
.priority(1)
.serviceLevel("P15D")
.buildAndStore(classificationService);
ClassificationSummary classificationSummary = classification.asSummary();
final List<String> indirectLinkedTasks =
createTasksWithExistingClassificationInAttachment(
classificationSummary,
input.getLeft().getLeft(),
input.getLeft().getRight(),
5);
classification.setServiceLevel("P1D");
classificationService.updateClassification(classification);
runAssociatedJobs();
validateTaskProperties(
before,
indirectLinkedTasks,
taskService,
workingTimeCalculator,
input.getRight().getLeft(),
input.getRight().getRight());
};
return DynamicTest.stream(
zippedTestInputList.iterator(),
i ->
String.format(
"for Task with ServiceLevel %s and Priority %s",
i.getLeft().getLeft(), i.getLeft().getRight()),
test);
}
@WithAccessId(user = "businessadmin")
@TestFactory
Stream<DynamicTest> should_NotUpdateTaskServiceLevel_When_UpdateClassificationInAttachment() {
List<Pair<String, Integer>> inputs =
List.of(Pair.of("P5D", 2), Pair.of("P8D", 3), Pair.of("P14D", 4));
List<Pair<Integer, Integer>> outputs = List.of(Pair.of(5, 2), Pair.of(8, 3), Pair.of(14, 4));
List<Pair<Pair<String, Integer>, Pair<Integer, Integer>>> zippedTestInputList =
IntStream.range(0, inputs.size())
.mapToObj(i -> Pair.of(inputs.get(i), outputs.get(i)))
.collect(Collectors.toList());
ThrowingConsumer<Pair<Pair<String, Integer>, Pair<Integer, Integer>>> test =
input -> {
final Instant before = Instant.now();
Classification classification =
defaultTestClassification()
.priority(1)
.serviceLevel("P1D")
.buildAndStore(classificationService);
ClassificationSummary classificationSummary = classification.asSummary();
final List<String> indirectLinkedTasks =
createTasksWithExistingClassificationInAttachment(
classificationSummary,
input.getLeft().getLeft(),
input.getLeft().getRight(),
5);
classification.setServiceLevel("P15D");
classificationService.updateClassification(classification);
runAssociatedJobs();
validateTaskProperties(
before,
indirectLinkedTasks,
taskService,
workingTimeCalculator,
input.getRight().getLeft(),
input.getRight().getRight());
};
return DynamicTest.stream(
zippedTestInputList.iterator(),
i ->
String.format(
"for Task with ServiceLevel %s and Priority %s",
i.getLeft().getLeft(), i.getLeft().getRight()),
test);
}
@WithAccessId(user = "businessadmin")
@TestFactory
Stream<DynamicTest> should_UpdateTaskPriority_When_UpdateClassificationInAttachment() {
List<Pair<String, Integer>> inputs =
List.of(Pair.of("P1D", 1), Pair.of("P8D", 2), Pair.of("P14D", 999));
List<Pair<Integer, Integer>> outputs =
List.of(Pair.of(1, 1000), Pair.of(8, 1000), Pair.of(14, 1000));
List<Pair<Pair<String, Integer>, Pair<Integer, Integer>>> zippedTestInputList =
IntStream.range(0, inputs.size())
.mapToObj(i -> Pair.of(inputs.get(i), outputs.get(i)))
.collect(Collectors.toList());
ThrowingConsumer<Pair<Pair<String, Integer>, Pair<Integer, Integer>>> test =
input -> {
final Instant before = Instant.now();
Classification classification =
defaultTestClassification()
.priority(1)
.serviceLevel("P13D")
.buildAndStore(classificationService);
ClassificationSummary classificationSummary = classification.asSummary();
final List<String> indirectLinkedTasks =
createTasksWithExistingClassificationInAttachment(
classificationSummary,
input.getLeft().getLeft(),
input.getLeft().getRight(),
5);
classification.setServiceLevel("P15D");
classification.setPriority(1000);
classificationService.updateClassification(classification);
runAssociatedJobs();
validateTaskProperties(
before,
indirectLinkedTasks,
taskService,
workingTimeCalculator,
input.getRight().getLeft(),
input.getRight().getRight());
};
return DynamicTest.stream(
zippedTestInputList.iterator(),
i ->
String.format(
"for Task with ServiceLevel %s and Priority %s",
i.getLeft().getLeft(), i.getLeft().getRight()),
test);
}
@WithAccessId(user = "businessadmin")
@TestFactory
Stream<DynamicTest> should_NotUpdateTaskPriority_When_UpdateClassificationInAttachment() {
List<Pair<String, Integer>> inputs =
List.of(Pair.of("P1D", 2), Pair.of("P8D", 3), Pair.of("P14D", 999));
List<Pair<Integer, Integer>> outputs =
List.of(Pair.of(1, 2), Pair.of(8, 3), Pair.of(14, 999));
List<Pair<Pair<String, Integer>, Pair<Integer, Integer>>> zippedTestInputList =
IntStream.range(0, inputs.size())
.mapToObj(i -> Pair.of(inputs.get(i), outputs.get(i)))
.collect(Collectors.toList());
ThrowingConsumer<Pair<Pair<String, Integer>, Pair<Integer, Integer>>> test =
input -> {
final Instant before = Instant.now();
Classification classification =
defaultTestClassification()
.priority(1000)
.serviceLevel("P13D")
.buildAndStore(classificationService);
ClassificationSummary classificationSummary = classification.asSummary();
final List<String> indirectLinkedTasks =
createTasksWithExistingClassificationInAttachment(
classificationSummary,
input.getLeft().getLeft(),
input.getLeft().getRight(),
5);
classification.setServiceLevel("P15D");
classification.setPriority(1);
classificationService.updateClassification(classification);
runAssociatedJobs();
validateTaskProperties(
before,
indirectLinkedTasks,
taskService,
workingTimeCalculator,
input.getRight().getLeft(),
input.getRight().getRight());
};
return DynamicTest.stream(
zippedTestInputList.iterator(),
i ->
String.format(
"for Task with ServiceLevel %s and Priority %s",
i.getLeft().getLeft(), i.getLeft().getRight()),
test);
}
@WithAccessId(user = "businessadmin")
@TestFactory
Stream<DynamicTest>
should_UpdateTaskPriorityAndServiceLevel_When_UpdateClassificationInAttachment() {
List<Pair<String, Integer>> inputs = List.of(Pair.of("P1D", 5), Pair.of("P14D", 98));
List<Pair<Integer, Integer>> outputs = List.of(Pair.of(1, 99), Pair.of(1, 99));
List<Pair<Pair<String, Integer>, Pair<Integer, Integer>>> zippedTestInputList =
IntStream.range(0, inputs.size())
.mapToObj(i -> Pair.of(inputs.get(i), outputs.get(i)))
.collect(Collectors.toList());
ThrowingConsumer<Pair<Pair<String, Integer>, Pair<Integer, Integer>>> test =
input -> {
final Instant before = Instant.now();
Classification classification =
defaultTestClassification()
.priority(1)
.serviceLevel("P13D")
.buildAndStore(classificationService);
ClassificationSummary classificationSummary = classification.asSummary();
final List<String> indirectLinkedTasks =
createTasksWithExistingClassificationInAttachment(
classificationSummary,
input.getLeft().getLeft(),
input.getLeft().getRight(),
3);
classification.setServiceLevel("P1D");
classification.setPriority(99);
classificationService.updateClassification(classification);
runAssociatedJobs();
validateTaskProperties(
before,
indirectLinkedTasks,
taskService,
workingTimeCalculator,
input.getRight().getLeft(),
input.getRight().getRight());
};
return DynamicTest.stream(
zippedTestInputList.iterator(),
i ->
String.format(
"for Task with ServiceLevel %s and Priority %s",
i.getLeft().getLeft(), i.getLeft().getRight()),
test);
}
@WithAccessId(user = "businessadmin")
@TestFactory
Stream<DynamicTest>
should_NotUpdateTaskPriorityAndServiceLevel_When_UpdateClassificationInAttachment() {
List<Pair<String, Integer>> inputs = List.of(Pair.of("P1D", 5), Pair.of("P14D", 98));
List<Pair<Integer, Integer>> outputs = List.of(Pair.of(1, 5), Pair.of(14, 98));
List<Pair<Pair<String, Integer>, Pair<Integer, Integer>>> zippedTestInputList =
IntStream.range(0, inputs.size())
.mapToObj(i -> Pair.of(inputs.get(i), outputs.get(i)))
.collect(Collectors.toList());
ThrowingConsumer<Pair<Pair<String, Integer>, Pair<Integer, Integer>>> test =
input -> {
final Instant before = Instant.now();
Classification classification =
defaultTestClassification()
.priority(1000)
.serviceLevel("P1D")
.buildAndStore(classificationService);
ClassificationSummary classificationSummary = classification.asSummary();
final List<String> indirectLinkedTasks =
createTasksWithExistingClassificationInAttachment(
classificationSummary,
input.getLeft().getLeft(),
input.getLeft().getRight(),
3);
classification.setServiceLevel("P15D");
classification.setPriority(1);
classificationService.updateClassification(classification);
runAssociatedJobs();
validateTaskProperties(
before,
indirectLinkedTasks,
taskService,
workingTimeCalculator,
input.getRight().getLeft(),
input.getRight().getRight());
};
return DynamicTest.stream(
zippedTestInputList.iterator(),
i ->
String.format(
"for Task with ServiceLevel %s and Priority %s",
i.getLeft().getLeft(), i.getLeft().getRight()),
test);
}
private void runAssociatedJobs() throws Exception {
Thread.sleep(10);
// run the ClassificationChangedJob
JobRunner runner = new JobRunner(taskanaEngine);
// run the TaskRefreshJob that was scheduled by the ClassificationChangedJob.
runner.runJobs();
Thread.sleep(
10); // otherwise the next runJobs call intermittently doesn't find the Job created
// by the previous step (it searches with DueDate < CurrentTime)
runner.runJobs();
}
private void validateTaskProperties(
Instant before,
List<String> tasksUpdated,
TaskService taskService,
WorkingTimeCalculator workingTimeCalculator,
int serviceLevel,
int priority)
throws Exception {
for (String taskId : tasksUpdated) {
Task task = taskService.getTask(taskId);
Instant expDue =
workingTimeCalculator.addWorkingTime(task.getPlanned(), Duration.ofDays(serviceLevel));
assertThat(task.getModified())
.describedAs("Task " + task.getId() + " has not been refreshed.")
.isAfter(before);
assertThat(task.getDue()).isEqualTo(expDue);
assertThat(task.getPriority()).isEqualTo(priority);
}
}
}
@TestInstance(Lifecycle.PER_CLASS)
@Nested
class UpdateClassificationExceptionTest {
/**
* This BeforeAll method is needed for this {@linkplain
* #should_ThrowException_When_UserIsNotAuthorized test} and {@linkplain
* #should_ThrowException_When_UserRoleIsNotAdminOrBusinessAdmin test} since it can't create an
* own classification.
*
* @throws Exception for errors in the building or reading process of entities.
*/
@WithAccessId(user = "businessadmin")
@BeforeAll
void createClassifications() throws Exception {
defaultTestClassification()
.key("BeforeAllClassification")
.buildAndStore(classificationService);
}
@Test
void should_ThrowException_When_UserIsNotAuthorized() throws Exception {
Classification classification =
classificationService.getClassification("BeforeAllClassification", "DOMAIN_A");
classification.setCustomField(ClassificationCustomField.CUSTOM_1, "newCustom1");
NotAuthorizedException expectedException =
new NotAuthorizedException(
currentUserContext.getUserid(), TaskanaRole.BUSINESS_ADMIN, TaskanaRole.ADMIN);
assertThatThrownBy(() -> classificationService.updateClassification(classification))
.usingRecursiveComparison()
.isEqualTo(expectedException);
}
@WithAccessId(user = "taskadmin")
@WithAccessId(user = "user-1-1")
@TestTemplate
void should_ThrowException_When_UserRoleIsNotAdminOrBusinessAdmin() throws Exception {
Classification classification =
classificationService.getClassification("BeforeAllClassification", "DOMAIN_A");
classification.setApplicationEntryPoint("updated EntryPoint");
classification.setName("updated Name");
NotAuthorizedException expectedException =
new NotAuthorizedException(
currentUserContext.getUserid(), TaskanaRole.BUSINESS_ADMIN, TaskanaRole.ADMIN);
assertThatThrownBy(() -> classificationService.updateClassification(classification))
.usingRecursiveComparison()
.isEqualTo(expectedException);
}
@WithAccessId(user = "businessadmin")
@Test
void should_ThrowException_When_UpdatingClassificationConcurrently() throws Exception {
Classification classification =
defaultTestClassification().buildAndStore(classificationService);
final Classification classificationSecondUpdate =
classificationService.getClassification(
classification.getKey(), classification.getDomain());
classification.setApplicationEntryPoint("Application Entry Point");
classification.setDescription("Description");
classification.setName("Name");
Thread.sleep(20); // to avoid identity of modified timestamps between classification and base
classificationService.updateClassification(classification);
classificationSecondUpdate.setName("Name again");
classificationSecondUpdate.setDescription("Description again");
ConcurrencyException expectedException =
new ConcurrencyException(classificationSecondUpdate.getId());
assertThatThrownBy(
() -> classificationService.updateClassification(classificationSecondUpdate))
.usingRecursiveComparison()
.isEqualTo(expectedException);
}
@WithAccessId(user = "businessadmin")
@Test
void should_ThrowException_When_TryingToUpdateClassificationWithInvalidParentId()
throws Exception {
Classification classification =
defaultTestClassification().buildAndStore(classificationService);
classification.setParentId("NON EXISTING ID");
ClassificationNotFoundException expectedException =
new ClassificationNotFoundException("NON EXISTING ID");
assertThatThrownBy(() -> classificationService.updateClassification(classification))
.usingRecursiveComparison()
.isEqualTo(expectedException);
}
@WithAccessId(user = "businessadmin")
@Test
void should_ThrowException_When_TryingToUpdateClassificationWithInvalidParentKey()
throws Exception {
Classification classification =
defaultTestClassification().buildAndStore(classificationService);
classification.setParentKey("NON EXISTING KEY");
ClassificationNotFoundException expectedException =
new ClassificationNotFoundException("NON EXISTING KEY", "DOMAIN_A");
assertThatThrownBy(() -> classificationService.updateClassification(classification))
.usingRecursiveComparison()
.isEqualTo(expectedException);
}
@WithAccessId(user = "businessadmin")
@Test
void should_ThrowException_When_TryingToUpdateClassificationWithOwnKeyAsParentKey()
throws Exception {
Classification classification =
defaultTestClassification().buildAndStore(classificationService);
classification.setParentKey(classification.getKey());
InvalidArgumentException expectedException =
new InvalidArgumentException(
String.format(
"The Classification '%s' has the same key and parent key",
classification.getName()));
assertThatThrownBy(() -> classificationService.updateClassification(classification))
.usingRecursiveComparison()
.isEqualTo(expectedException);
}
}
@TestInstance(Lifecycle.PER_CLASS)
@Nested
class UpdateClassificationCategoryTest {
Classification classification;
Task task;
Instant createdBefore;
Instant modifiedBefore;
@WithAccessId(user = "businessadmin")
@BeforeEach
void createClassificationAndTask() throws Exception {
classification =
defaultTestClassification()
.category("MANUAL")
.type("TASK")
.buildAndStore(classificationService);
createdBefore = classification.getCreated();
modifiedBefore = classification.getModified();
String taskId = createTaskWithExistingClassification(classification.asSummary());
task = taskService.getTask(taskId);
}
@WithAccessId(user = "businessadmin")
@Test
void should_UpdateTask_When_UpdatingClassificationCategory() throws Exception {
classification.setCategory("PROCESS");
classificationService.updateClassification(classification);
final Task updatedTask = taskService.getTask(task.getId());
TaskImpl expectedUpdatedTask = (TaskImpl) task.copy();
expectedUpdatedTask.setId(task.getId());
expectedUpdatedTask.setClassificationCategory("PROCESS");
expectedUpdatedTask.setClassificationSummary(
classificationService.getClassification(classification.getId()).asSummary());
expectedUpdatedTask.setExternalId(task.getExternalId());
assertThat(expectedUpdatedTask)
.usingRecursiveComparison()
.ignoringFields("modified")
.isEqualTo(updatedTask);
assertThat(expectedUpdatedTask.getModified()).isAfterOrEqualTo(modifiedBefore);
}
@WithAccessId(user = "businessadmin")
@Test
void should_UpdateClassification_When_UpdatingClassificationCategory() throws Exception {
classification.setCategory("PROCESS");
classificationService.updateClassification(classification);
Classification updatedClassification =
classificationService.getClassification(classification.getId());
assertThat(updatedClassification)
.usingRecursiveComparison()
.ignoringFields("modified")
.isEqualTo(classification);
assertThat(updatedClassification.getModified()).isAfterOrEqualTo(modifiedBefore);
}
}
}

View File

@ -5,18 +5,26 @@ import static pro.taskana.testapi.DefaultTestEntities.defaultTestClassification;
import static pro.taskana.testapi.DefaultTestEntities.defaultTestObjectReference;
import static pro.taskana.testapi.DefaultTestEntities.defaultTestWorkbasket;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import pro.taskana.TaskanaConfiguration.Builder;
import pro.taskana.classification.api.ClassificationService;
import pro.taskana.classification.api.models.ClassificationSummary;
import pro.taskana.common.api.BulkOperationResults;
import pro.taskana.common.api.TaskanaEngine;
import pro.taskana.common.api.WorkingTimeCalculator;
import pro.taskana.common.api.exceptions.TaskanaException;
import pro.taskana.task.api.TaskService;
import pro.taskana.task.api.models.Attachment;
import pro.taskana.task.api.models.ObjectReference;
import pro.taskana.task.api.models.TaskSummary;
import pro.taskana.testapi.TaskanaConfigurationModifier;
import pro.taskana.testapi.TaskanaInject;
import pro.taskana.testapi.TaskanaIntegrationTest;
import pro.taskana.testapi.builder.TaskAttachmentBuilder;
@ -37,172 +45,364 @@ class ServiceLevelOfAllTasksAccTest {
private static final String SMALL_CLASSIFICATION_SERVICE_LEVEL = "P2D";
private static final String GREAT_CLASSIFICATION_SERVICE_LEVEL = "P7D";
@TaskanaInject TaskService taskService;
@TaskanaInject WorkbasketService workbasketService;
@TaskanaInject ClassificationService classificationService;
@Nested
@TestInstance(Lifecycle.PER_CLASS)
class WithWorkingTimeCalculation {
@TaskanaInject TaskService taskService;
@TaskanaInject WorkbasketService workbasketService;
@TaskanaInject ClassificationService classificationService;
ClassificationSummary classificationSummarySmallServiceLevel;
ClassificationSummary classificationSummaryGreatServiceLevel;
Attachment attachmentSummarySmallServiceLevel;
Attachment attachmentSummaryGreatServiceLevel;
WorkbasketSummary defaultWorkbasketSummary;
ObjectReference defaultObjectReference;
ClassificationSummary classificationSummarySmallServiceLevel;
ClassificationSummary classificationSummaryGreatServiceLevel;
Attachment attachmentSummarySmallServiceLevel;
Attachment attachmentSummaryGreatServiceLevel;
WorkbasketSummary defaultWorkbasketSummary;
ObjectReference defaultObjectReference;
@WithAccessId(user = "businessadmin")
@BeforeAll
void setup() throws Exception {
classificationSummarySmallServiceLevel =
defaultTestClassification()
.serviceLevel(SMALL_CLASSIFICATION_SERVICE_LEVEL)
.buildAndStoreAsSummary(classificationService);
classificationSummaryGreatServiceLevel =
defaultTestClassification()
.serviceLevel(GREAT_CLASSIFICATION_SERVICE_LEVEL)
.buildAndStoreAsSummary(classificationService);
@WithAccessId(user = "businessadmin")
@BeforeAll
void setup() throws Exception {
classificationSummarySmallServiceLevel =
defaultTestClassification()
.serviceLevel(SMALL_CLASSIFICATION_SERVICE_LEVEL)
.buildAndStoreAsSummary(classificationService);
classificationSummaryGreatServiceLevel =
defaultTestClassification()
.serviceLevel(GREAT_CLASSIFICATION_SERVICE_LEVEL)
.buildAndStoreAsSummary(classificationService);
defaultObjectReference = defaultTestObjectReference().build();
defaultObjectReference = defaultTestObjectReference().build();
attachmentSummarySmallServiceLevel =
TaskAttachmentBuilder.newAttachment()
.classificationSummary(classificationSummarySmallServiceLevel)
.objectReference(defaultObjectReference)
.build();
attachmentSummaryGreatServiceLevel =
TaskAttachmentBuilder.newAttachment()
.classificationSummary(classificationSummaryGreatServiceLevel)
.objectReference(defaultObjectReference)
.build();
attachmentSummarySmallServiceLevel =
TaskAttachmentBuilder.newAttachment()
.classificationSummary(classificationSummarySmallServiceLevel)
.objectReference(defaultObjectReference)
.build();
attachmentSummaryGreatServiceLevel =
TaskAttachmentBuilder.newAttachment()
.classificationSummary(classificationSummaryGreatServiceLevel)
.objectReference(defaultObjectReference)
.build();
defaultWorkbasketSummary = defaultTestWorkbasket().buildAndStoreAsSummary(workbasketService);
WorkbasketAccessItemBuilder.newWorkbasketAccessItem()
.workbasketId(defaultWorkbasketSummary.getId())
.accessId("user-1-1")
.permission(WorkbasketPermission.OPEN)
.permission(WorkbasketPermission.READ)
.permission(WorkbasketPermission.READTASKS)
.permission(WorkbasketPermission.APPEND)
.buildAndStore(workbasketService);
defaultWorkbasketSummary = defaultTestWorkbasket().buildAndStoreAsSummary(workbasketService);
WorkbasketAccessItemBuilder.newWorkbasketAccessItem()
.workbasketId(defaultWorkbasketSummary.getId())
.accessId("user-1-1")
.permission(WorkbasketPermission.OPEN)
.permission(WorkbasketPermission.READ)
.permission(WorkbasketPermission.READTASKS)
.permission(WorkbasketPermission.APPEND)
.buildAndStore(workbasketService);
}
@WithAccessId(user = "user-1-1")
@Test
void should_SetPlannedOnMultipleTasks() throws Exception {
Instant planned = Instant.parse("2020-05-03T07:00:00.000Z");
TaskSummary task1 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.buildAndStoreAsSummary(taskService);
TaskSummary task2 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.attachments(attachmentSummaryGreatServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
TaskSummary task3 =
createDefaultTask()
.classificationSummary(classificationSummaryGreatServiceLevel)
.attachments(attachmentSummarySmallServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
List<String> taskIds = List.of(task1.getId(), task2.getId(), task3.getId());
BulkOperationResults<String, TaskanaException> bulkLog =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
assertThat(bulkLog.containsErrors()).isFalse();
List<TaskSummary> result =
taskService.createTaskQuery().idIn(task1.getId(), task2.getId(), task3.getId()).list();
assertThat(result).extracting(TaskSummary::getPlanned).containsOnly(planned);
}
@WithAccessId(user = "user-1-1")
@Test
void should_ChangeDue_When_SettingPlannedAndClassificationHasSmallerServiceLevel()
throws Exception {
Instant planned = Instant.parse("2020-05-04T07:00:00.000Z");
TaskSummary task1 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.attachments(attachmentSummaryGreatServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
TaskSummary task2 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.buildAndStoreAsSummary(taskService);
List<String> taskIds = List.of(task1.getId(), task2.getId());
BulkOperationResults<String, TaskanaException> bulkLog =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
assertThat(bulkLog.containsErrors()).isFalse();
List<TaskSummary> result =
taskService.createTaskQuery().idIn(task1.getId(), task2.getId()).list();
Instant expectedDue = Instant.parse("2020-05-06T06:59:59.999Z");
assertThat(result).extracting(TaskSummary::getDue).containsOnly(expectedDue);
}
@WithAccessId(user = "user-1-1")
@Test
void should_ChangeDue_When_SettingPlannedAndAttachmentHasSmallerOrEqualServiceLevel()
throws Exception {
Instant planned = Instant.parse("2020-05-04T07:00:00.000Z");
TaskSummary task1 =
createDefaultTask()
.classificationSummary(classificationSummaryGreatServiceLevel)
.attachments(attachmentSummarySmallServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
TaskSummary task2 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.attachments(attachmentSummarySmallServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
List<String> taskIds = List.of(task1.getId(), task2.getId());
BulkOperationResults<String, TaskanaException> bulkLog =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
assertThat(bulkLog.containsErrors()).isFalse();
List<TaskSummary> result =
taskService.createTaskQuery().idIn(task1.getId(), task2.getId()).list();
Instant expectedDue = Instant.parse("2020-05-06T06:59:59.999Z");
assertThat(result).extracting(TaskSummary::getDue).containsOnly(expectedDue);
}
@WithAccessId(user = "user-1-1")
@Test
void should_ChangeDue_When_SettingPlannedAndUsingDifferentServiceLevels() throws Exception {
Instant planned = Instant.parse("2020-05-04T07:00:00.000Z");
TaskSummary task1 =
createDefaultTask()
.classificationSummary(classificationSummaryGreatServiceLevel)
.attachments(attachmentSummarySmallServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
TaskSummary task2 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.attachments(attachmentSummaryGreatServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
TaskSummary task3 =
createDefaultTask()
.classificationSummary(classificationSummaryGreatServiceLevel)
.attachments(attachmentSummaryGreatServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
List<String> taskIds = List.of(task1.getId(), task2.getId(), task3.getId());
BulkOperationResults<String, TaskanaException> bulkLog =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
assertThat(bulkLog.containsErrors()).isFalse();
List<TaskSummary> result =
taskService.createTaskQuery().idIn(task1.getId(), task2.getId(), task3.getId()).list();
Instant expectedDueSmallServiceLevel = Instant.parse("2020-05-06T06:59:59.999Z");
Instant expectedDueGreatServiceLevel = Instant.parse("2020-05-13T06:59:59.999Z");
assertThat(result)
.extracting(TaskSummary::getDue)
.containsOnly(expectedDueSmallServiceLevel, expectedDueGreatServiceLevel);
}
private TaskBuilder createDefaultTask() {
return (TaskBuilder.newTask()
.workbasketSummary(defaultWorkbasketSummary)
.primaryObjRef(defaultObjectReference));
}
}
@WithAccessId(user = "user-1-1")
@Test
void should_SetPlannedOnMultipleTasks() throws Exception {
Instant planned = Instant.parse("2020-05-03T07:00:00.000Z");
TaskSummary task1 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.buildAndStoreAsSummary(taskService);
TaskSummary task2 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.attachments(attachmentSummaryGreatServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
TaskSummary task3 =
createDefaultTask()
.classificationSummary(classificationSummaryGreatServiceLevel)
.attachments(attachmentSummarySmallServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
List<String> taskIds = List.of(task1.getId(), task2.getId(), task3.getId());
@Nested
@TestInstance(Lifecycle.PER_CLASS)
class WithWorkingDaysCalculation implements TaskanaConfigurationModifier {
BulkOperationResults<String, TaskanaException> bulkLog =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
@TaskanaInject TaskanaEngine taskanaEngine;
@TaskanaInject TaskService taskService;
@TaskanaInject WorkbasketService workbasketService;
@TaskanaInject ClassificationService classificationService;
ClassificationSummary classificationSummarySmallServiceLevel;
ClassificationSummary classificationSummaryGreatServiceLevel;
Attachment attachmentSummarySmallServiceLevel;
Attachment attachmentSummaryGreatServiceLevel;
WorkbasketSummary defaultWorkbasketSummary;
ObjectReference defaultObjectReference;
WorkingTimeCalculator converter;
assertThat(bulkLog.containsErrors()).isFalse();
List<TaskSummary> result =
taskService.createTaskQuery().idIn(task1.getId(), task2.getId(), task3.getId()).list();
assertThat(result).extracting(TaskSummary::getPlanned).containsOnly(planned);
}
@Override
public Builder modify(Builder builder) {
return builder.useWorkingTimeCalculation(false);
}
@WithAccessId(user = "user-1-1")
@Test
void should_ChangeDue_When_SettingPlannedAndClassificationHasSmallerServiceLevel()
throws Exception {
Instant planned = Instant.parse("2020-05-04T07:00:00.000Z");
TaskSummary task1 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.attachments(attachmentSummaryGreatServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
TaskSummary task2 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.buildAndStoreAsSummary(taskService);
List<String> taskIds = List.of(task1.getId(), task2.getId());
@WithAccessId(user = "businessadmin")
@BeforeAll
void setup() throws Exception {
classificationSummarySmallServiceLevel =
defaultTestClassification()
.serviceLevel(SMALL_CLASSIFICATION_SERVICE_LEVEL)
.buildAndStoreAsSummary(classificationService);
classificationSummaryGreatServiceLevel =
defaultTestClassification()
.serviceLevel(GREAT_CLASSIFICATION_SERVICE_LEVEL)
.buildAndStoreAsSummary(classificationService);
BulkOperationResults<String, TaskanaException> bulkLog =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
defaultObjectReference = defaultTestObjectReference().build();
assertThat(bulkLog.containsErrors()).isFalse();
List<TaskSummary> result =
taskService.createTaskQuery().idIn(task1.getId(), task2.getId()).list();
Instant expectedDue = Instant.parse("2020-05-06T06:59:59.999Z");
assertThat(result).extracting(TaskSummary::getDue).containsOnly(expectedDue);
}
attachmentSummarySmallServiceLevel =
TaskAttachmentBuilder.newAttachment()
.classificationSummary(classificationSummarySmallServiceLevel)
.objectReference(defaultObjectReference)
.build();
attachmentSummaryGreatServiceLevel =
TaskAttachmentBuilder.newAttachment()
.classificationSummary(classificationSummaryGreatServiceLevel)
.objectReference(defaultObjectReference)
.build();
@WithAccessId(user = "user-1-1")
@Test
void should_ChangeDue_When_SettingPlannedAndAttachmentHasSmallerOrEqualServiceLevel()
throws Exception {
Instant planned = Instant.parse("2020-05-04T07:00:00.000Z");
TaskSummary task1 =
createDefaultTask()
.classificationSummary(classificationSummaryGreatServiceLevel)
.attachments(attachmentSummarySmallServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
TaskSummary task2 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.attachments(attachmentSummarySmallServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
List<String> taskIds = List.of(task1.getId(), task2.getId());
defaultWorkbasketSummary = defaultTestWorkbasket().buildAndStoreAsSummary(workbasketService);
WorkbasketAccessItemBuilder.newWorkbasketAccessItem()
.workbasketId(defaultWorkbasketSummary.getId())
.accessId("user-1-1")
.permission(WorkbasketPermission.OPEN)
.permission(WorkbasketPermission.READ)
.permission(WorkbasketPermission.READTASKS)
.permission(WorkbasketPermission.APPEND)
.buildAndStore(workbasketService);
converter = taskanaEngine.getWorkingTimeCalculator();
}
BulkOperationResults<String, TaskanaException> bulkLog =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
@WithAccessId(user = "user-1-1")
@Test
void should_SetPlannedOnMultipleTasks() throws Exception {
Instant planned = Instant.parse("2020-05-03T07:00:00.000Z");
TaskSummary task1 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.buildAndStoreAsSummary(taskService);
TaskSummary task2 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.attachments(attachmentSummaryGreatServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
TaskSummary task3 =
createDefaultTask()
.classificationSummary(classificationSummaryGreatServiceLevel)
.attachments(attachmentSummarySmallServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
List<String> taskIds = List.of(task1.getId(), task2.getId(), task3.getId());
assertThat(bulkLog.containsErrors()).isFalse();
List<TaskSummary> result =
taskService.createTaskQuery().idIn(task1.getId(), task2.getId()).list();
Instant expectedDue = Instant.parse("2020-05-06T06:59:59.999Z");
assertThat(result).extracting(TaskSummary::getDue).containsOnly(expectedDue);
}
BulkOperationResults<String, TaskanaException> bulkLog =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
@WithAccessId(user = "user-1-1")
@Test
void should_ChangeDue_When_SettingPlannedAndUsingDifferentServiceLevels() throws Exception {
Instant planned = Instant.parse("2020-05-04T07:00:00.000Z");
TaskSummary task1 =
createDefaultTask()
.classificationSummary(classificationSummaryGreatServiceLevel)
.attachments(attachmentSummarySmallServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
TaskSummary task2 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.attachments(attachmentSummaryGreatServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
TaskSummary task3 =
createDefaultTask()
.classificationSummary(classificationSummaryGreatServiceLevel)
.attachments(attachmentSummaryGreatServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
List<String> taskIds = List.of(task1.getId(), task2.getId(), task3.getId());
assertThat(bulkLog.containsErrors()).isFalse();
List<TaskSummary> result =
taskService.createTaskQuery().idIn(task1.getId(), task2.getId(), task3.getId()).list();
assertThat(result).extracting(TaskSummary::getPlanned).containsOnly(planned);
}
BulkOperationResults<String, TaskanaException> bulkLog =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
@WithAccessId(user = "user-1-1")
@Test
void should_ChangeDue_When_SettingPlannedAndClassificationHasSmallerServiceLevel()
throws Exception {
Instant planned = Instant.parse("2020-05-03T07:00:00.000Z");
TaskSummary task1 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.attachments(attachmentSummaryGreatServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
TaskSummary task2 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.buildAndStoreAsSummary(taskService);
List<String> taskIds = List.of(task1.getId(), task2.getId());
assertThat(bulkLog.containsErrors()).isFalse();
List<TaskSummary> result =
taskService.createTaskQuery().idIn(task1.getId(), task2.getId(), task3.getId()).list();
Instant expectedDueSmallServiceLevel = Instant.parse("2020-05-06T06:59:59.999Z");
Instant expectedDueGreatServiceLevel = Instant.parse("2020-05-13T06:59:59.999Z");
assertThat(result)
.extracting(TaskSummary::getDue)
.containsOnly(expectedDueSmallServiceLevel, expectedDueGreatServiceLevel);
}
BulkOperationResults<String, TaskanaException> bulkLog =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
private TaskBuilder createDefaultTask() {
return (TaskBuilder.newTask()
.workbasketSummary(defaultWorkbasketSummary)
.primaryObjRef(defaultObjectReference));
assertThat(bulkLog.containsErrors()).isFalse();
List<TaskSummary> result =
taskService.createTaskQuery().idIn(task1.getId(), task2.getId()).list();
assertThat(result)
.extracting(TaskSummary::getDue)
.containsOnly(
converter.addWorkingTime(
planned, Duration.parse(SMALL_CLASSIFICATION_SERVICE_LEVEL)));
}
@WithAccessId(user = "user-1-1")
@Test
void should_ChangeDue_When_SettingPlannedAndAttachmentHasSmallerOrEqualServiceLevel()
throws Exception {
Instant planned = Instant.parse("2020-05-03T07:00:00.000Z");
TaskSummary task1 =
createDefaultTask()
.classificationSummary(classificationSummaryGreatServiceLevel)
.attachments(attachmentSummarySmallServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
TaskSummary task2 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.attachments(attachmentSummarySmallServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
List<String> taskIds = List.of(task1.getId(), task2.getId());
BulkOperationResults<String, TaskanaException> bulkLog =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
assertThat(bulkLog.containsErrors()).isFalse();
List<TaskSummary> result =
taskService.createTaskQuery().idIn(task1.getId(), task2.getId()).list();
assertThat(result)
.extracting(TaskSummary::getDue)
.containsOnly(
converter.addWorkingTime(
planned, Duration.parse(SMALL_CLASSIFICATION_SERVICE_LEVEL)));
}
@WithAccessId(user = "user-1-1")
@Test
void should_ChangeDue_When_SettingPlannedAndUsingDifferentServiceLevels() throws Exception {
Instant planned = Instant.parse("2020-05-03T07:00:00.000Z");
TaskSummary task1 =
createDefaultTask()
.classificationSummary(classificationSummaryGreatServiceLevel)
.attachments(attachmentSummarySmallServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
TaskSummary task2 =
createDefaultTask()
.classificationSummary(classificationSummarySmallServiceLevel)
.attachments(attachmentSummaryGreatServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
TaskSummary task3 =
createDefaultTask()
.classificationSummary(classificationSummaryGreatServiceLevel)
.attachments(attachmentSummaryGreatServiceLevel.copy())
.buildAndStoreAsSummary(taskService);
List<String> taskIds = List.of(task1.getId(), task2.getId(), task3.getId());
BulkOperationResults<String, TaskanaException> bulkLog =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
assertThat(bulkLog.containsErrors()).isFalse();
List<TaskSummary> result =
taskService.createTaskQuery().idIn(task1.getId(), task2.getId(), task3.getId()).list();
assertThat(result)
.extracting(TaskSummary::getDue)
.containsOnly(
converter.addWorkingTime(planned, Duration.parse(SMALL_CLASSIFICATION_SERVICE_LEVEL)),
converter.addWorkingTime(
planned, Duration.parse(GREAT_CLASSIFICATION_SERVICE_LEVEL)));
}
private TaskBuilder createDefaultTask() {
return (TaskBuilder.newTask()
.workbasketSummary(defaultWorkbasketSummary)
.primaryObjRef(defaultObjectReference));
}
}
}

View File

@ -13,6 +13,7 @@ taskana.classification.types=TASK | document
taskana.classification.categories.task=EXTERNAL| manual| autoMAtic| Process
taskana.classification.categories.document=EXTERNAL
# working time configuration
taskana.workingTime.useWorkingTimeCalculation=false
taskana.workingTime.schedule.MONDAY=09:00-18:00
taskana.workingTime.schedule.TUESDAY=09:00-18:00
taskana.workingTime.schedule.WEDNESDAY=09:00-18:00
@ -34,21 +35,27 @@ taskana.jobs.maxRetries=4
taskana.jobs.batchSize=50
taskana.jobs.firstRunAt=2018-07-25T08:00:00Z
taskana.jobs.runEvery=P2D
taskana.jobs.lockExpirationPeriod=PT7M
taskana.jobs.cleanup.task.enable=false
taskana.jobs.cleanup.task.minimumAge=P15D
taskana.jobs.cleanup.task.allCompletedSameParentBusiness=false
taskana.jobs.cleanup.task.lockExpirationPeriod=PT4M
taskana.jobs.cleanup.workbasket.enable=false
taskana.jobs.cleanup.workbasket.lockExpirationPeriod=PT3M
taskana.jobs.cleanup.history.simple.enable=true
taskana.jobs.cleanup.history.simple.batchSize=50
taskana.jobs.cleanup.history.simple.minimumAge=P17D
taskana.jobs.cleanup.history.simple.allCompletedSameParentBusiness=false
taskana.jobs.cleanup.history.simple.lockExpirationPeriod=PT2M
taskana.jobs.priority.task.enable=true
taskana.jobs.priority.task.batchSize=50
taskana.jobs.priority.task.firstRunAt=2018-07-25T08:00:00Z
taskana.jobs.priority.task.runEvery=P3D
taskana.jobs.priority.task.lockExpirationPeriod=PT8M
taskana.jobs.refresh.user.enable=true
taskana.jobs.refresh.user.firstRunAt=2018-07-25T08:00:00Z
taskana.jobs.refresh.user.runEvery=P4D
taskana.jobs.refresh.user.lockExpirationPeriod=PT5M
taskana.jobs.customJobs=A | B | C
# user configuration
taskana.user.addAdditionalUserInfo=true

View File

@ -10,7 +10,7 @@
<parent>
<groupId>pro.taskana</groupId>
<artifactId>taskana-lib-parent</artifactId>
<version>6.2.1-SNAPSHOT</version>
<version>6.3.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -73,6 +73,7 @@ public class TaskanaConfiguration {
// endregion
// region working time configuration
private final boolean useWorkingTimeCalculation;
private final Map<DayOfWeek, Set<LocalTimeInterval>> workingTimeSchedule;
private final ZoneId workingTimeScheduleTimeZone;
private final Set<CustomHoliday> customHolidays;
@ -94,26 +95,30 @@ public class TaskanaConfiguration {
private final int jobBatchSize;
private final Instant jobFirstRun;
private final Duration jobRunEvery;
private final Duration jobLockExpirationPeriod;
private final boolean taskCleanupJobEnabled;
private final Duration taskCleanupJobMinimumAge;
private final boolean taskCleanupJobAllCompletedSameParentBusiness;
private final Duration taskCleanupJobLockExpirationPeriod;
private final boolean workbasketCleanupJobEnabled;
private final Duration workbasketCleanupJobLockExpirationPeriod;
private final boolean simpleHistoryCleanupJobEnabled;
private final int simpleHistoryCleanupJobBatchSize;
private final Duration simpleHistoryCleanupJobMinimumAge;
private final boolean simpleHistoryCleanupJobAllCompletedSameParentBusiness;
private final Duration simpleHistoryCleanupJobLockExpirationPeriod;
private final boolean taskUpdatePriorityJobEnabled;
private final int taskUpdatePriorityJobBatchSize;
private final Instant taskUpdatePriorityJobFirstRun;
private final Duration taskUpdatePriorityJobRunEvery;
private final Duration taskUpdatePriorityJobLockExpirationPeriod;
private final boolean userInfoRefreshJobEnabled;
private final Instant userRefreshJobFirstRun;
private final Duration userRefreshJobRunEvery;
private final Duration userRefreshJobLockExpirationPeriod;
private final Set<String> customJobs;
// endregion
@ -153,6 +158,7 @@ public class TaskanaConfiguration {
Collectors.toUnmodifiableMap(
Entry::getKey, e -> Collections.unmodifiableList(e.getValue())));
// working time configuration
this.useWorkingTimeCalculation = builder.useWorkingTimeCalculation;
this.workingTimeSchedule =
builder.workingTimeSchedule.entrySet().stream()
.collect(
@ -176,23 +182,32 @@ public class TaskanaConfiguration {
this.jobBatchSize = builder.jobBatchSize;
this.jobFirstRun = builder.jobFirstRun;
this.jobRunEvery = builder.jobRunEvery;
this.jobLockExpirationPeriod = builder.jobLockExpirationPeriod;
this.taskCleanupJobEnabled = builder.taskCleanupJobEnabled;
this.taskCleanupJobMinimumAge = builder.taskCleanupJobMinimumAge;
this.taskCleanupJobAllCompletedSameParentBusiness =
builder.taskCleanupJobAllCompletedSameParentBusiness;
this.taskCleanupJobLockExpirationPeriod = builder.taskCleanupJobLockExpirationPeriod;
this.workbasketCleanupJobEnabled = builder.workbasketCleanupJobEnabled;
this.workbasketCleanupJobLockExpirationPeriod =
builder.workbasketCleanupJobLockExpirationPeriod;
this.simpleHistoryCleanupJobEnabled = builder.simpleHistoryCleanupJobEnabled;
this.simpleHistoryCleanupJobBatchSize = builder.simpleHistoryCleanupJobBatchSize;
this.simpleHistoryCleanupJobMinimumAge = builder.simpleHistoryCleanupJobMinimumAge;
this.simpleHistoryCleanupJobAllCompletedSameParentBusiness =
builder.simpleHistoryCleanupJobAllCompletedSameParentBusiness;
this.simpleHistoryCleanupJobLockExpirationPeriod =
builder.simpleHistoryCleanupJobLockExpirationPeriod;
this.taskUpdatePriorityJobEnabled = builder.taskUpdatePriorityJobEnabled;
this.taskUpdatePriorityJobBatchSize = builder.taskUpdatePriorityJobBatchSize;
this.taskUpdatePriorityJobFirstRun = builder.taskUpdatePriorityJobFirstRun;
this.taskUpdatePriorityJobRunEvery = builder.taskUpdatePriorityJobRunEvery;
this.taskUpdatePriorityJobLockExpirationPeriod =
builder.taskUpdatePriorityJobLockExpirationPeriod;
this.userInfoRefreshJobEnabled = builder.userInfoRefreshJobEnabled;
this.userRefreshJobFirstRun = builder.userRefreshJobFirstRun;
this.userRefreshJobRunEvery = builder.userRefreshJobRunEvery;
this.userRefreshJobLockExpirationPeriod = builder.userRefreshJobLockExpirationPeriod;
this.customJobs = Collections.unmodifiableSet(builder.customJobs);
// user configuration
this.addAdditionalUserInfo = builder.addAdditionalUserInfo;
@ -262,6 +277,10 @@ public class TaskanaConfiguration {
return classificationTypes;
}
public boolean isUseWorkingTimeCalculation() {
return useWorkingTimeCalculation;
}
public Map<DayOfWeek, Set<LocalTimeInterval>> getWorkingTimeSchedule() {
return workingTimeSchedule;
}
@ -322,6 +341,10 @@ public class TaskanaConfiguration {
return jobRunEvery;
}
public Duration getJobLockExpirationPeriod() {
return jobLockExpirationPeriod;
}
public boolean isTaskCleanupJobEnabled() {
return taskCleanupJobEnabled;
}
@ -334,10 +357,18 @@ public class TaskanaConfiguration {
return taskCleanupJobAllCompletedSameParentBusiness;
}
public Duration getTaskCleanupJobLockExpirationPeriod() {
return taskCleanupJobLockExpirationPeriod;
}
public boolean isWorkbasketCleanupJobEnabled() {
return workbasketCleanupJobEnabled;
}
public Duration getWorkbasketCleanupJobLockExpirationPeriod() {
return workbasketCleanupJobLockExpirationPeriod;
}
public boolean isSimpleHistoryCleanupJobEnabled() {
return simpleHistoryCleanupJobEnabled;
}
@ -354,6 +385,10 @@ public class TaskanaConfiguration {
return simpleHistoryCleanupJobAllCompletedSameParentBusiness;
}
public Duration getSimpleHistoryCleanupJobLockExpirationPeriod() {
return simpleHistoryCleanupJobLockExpirationPeriod;
}
public boolean isTaskUpdatePriorityJobEnabled() {
return taskUpdatePriorityJobEnabled;
}
@ -370,6 +405,10 @@ public class TaskanaConfiguration {
return taskUpdatePriorityJobRunEvery;
}
public Duration getTaskUpdatePriorityJobLockExpirationPeriod() {
return taskUpdatePriorityJobLockExpirationPeriod;
}
public boolean isUserInfoRefreshJobEnabled() {
return userInfoRefreshJobEnabled;
}
@ -382,6 +421,10 @@ public class TaskanaConfiguration {
return userRefreshJobRunEvery;
}
public Duration getUserRefreshJobLockExpirationPeriod() {
return userRefreshJobLockExpirationPeriod;
}
public Set<String> getCustomJobs() {
return customJobs;
}
@ -412,6 +455,7 @@ public class TaskanaConfiguration {
// endregion
// region hashCode, equals + toString
@Override
public int hashCode() {
return Objects.hash(
@ -424,6 +468,7 @@ public class TaskanaConfiguration {
roleMap,
classificationTypes,
classificationCategoriesByType,
useWorkingTimeCalculation,
workingTimeSchedule,
workingTimeScheduleTimeZone,
customHolidays,
@ -439,21 +484,27 @@ public class TaskanaConfiguration {
jobBatchSize,
jobFirstRun,
jobRunEvery,
jobLockExpirationPeriod,
taskCleanupJobEnabled,
taskCleanupJobMinimumAge,
taskCleanupJobAllCompletedSameParentBusiness,
taskCleanupJobLockExpirationPeriod,
workbasketCleanupJobEnabled,
workbasketCleanupJobLockExpirationPeriod,
simpleHistoryCleanupJobEnabled,
simpleHistoryCleanupJobBatchSize,
simpleHistoryCleanupJobMinimumAge,
simpleHistoryCleanupJobAllCompletedSameParentBusiness,
simpleHistoryCleanupJobLockExpirationPeriod,
taskUpdatePriorityJobEnabled,
taskUpdatePriorityJobBatchSize,
taskUpdatePriorityJobFirstRun,
taskUpdatePriorityJobRunEvery,
taskUpdatePriorityJobLockExpirationPeriod,
userInfoRefreshJobEnabled,
userRefreshJobFirstRun,
userRefreshJobRunEvery,
userRefreshJobLockExpirationPeriod,
customJobs,
addAdditionalUserInfo,
minimalPermissionsToAssignDomains,
@ -473,6 +524,7 @@ public class TaskanaConfiguration {
return useManagedTransactions == other.useManagedTransactions
&& securityEnabled == other.securityEnabled
&& enforceServiceLevel == other.enforceServiceLevel
&& useWorkingTimeCalculation == other.useWorkingTimeCalculation
&& germanPublicHolidaysEnabled == other.germanPublicHolidaysEnabled
&& germanPublicHolidaysCorpusChristiEnabled
== other.germanPublicHolidaysCorpusChristiEnabled
@ -492,10 +544,10 @@ public class TaskanaConfiguration {
&& simpleHistoryCleanupJobAllCompletedSameParentBusiness
== other.simpleHistoryCleanupJobAllCompletedSameParentBusiness
&& taskUpdatePriorityJobEnabled == other.taskUpdatePriorityJobEnabled
&& useSpecificDb2Taskquery == other.useSpecificDb2Taskquery
&& taskUpdatePriorityJobBatchSize == other.taskUpdatePriorityJobBatchSize
&& userInfoRefreshJobEnabled == other.userInfoRefreshJobEnabled
&& addAdditionalUserInfo == other.addAdditionalUserInfo
&& useSpecificDb2Taskquery == other.useSpecificDb2Taskquery
&& Objects.equals(dataSource, other.dataSource)
&& Objects.equals(schemaName, other.schemaName)
&& Objects.equals(domains, other.domains)
@ -509,13 +561,27 @@ public class TaskanaConfiguration {
&& jobSchedulerPeriodTimeUnit == other.jobSchedulerPeriodTimeUnit
&& Objects.equals(jobFirstRun, other.jobFirstRun)
&& Objects.equals(jobRunEvery, other.jobRunEvery)
&& Objects.equals(jobLockExpirationPeriod, other.jobLockExpirationPeriod)
&& Objects.equals(taskCleanupJobMinimumAge, other.taskCleanupJobMinimumAge)
&& Objects.equals(
taskCleanupJobLockExpirationPeriod, other.taskCleanupJobLockExpirationPeriod)
&& Objects.equals(
workbasketCleanupJobLockExpirationPeriod,
other.workbasketCleanupJobLockExpirationPeriod)
&& Objects.equals(
simpleHistoryCleanupJobMinimumAge, other.simpleHistoryCleanupJobMinimumAge)
&& Objects.equals(
simpleHistoryCleanupJobLockExpirationPeriod,
other.simpleHistoryCleanupJobLockExpirationPeriod)
&& Objects.equals(taskUpdatePriorityJobFirstRun, other.taskUpdatePriorityJobFirstRun)
&& Objects.equals(taskUpdatePriorityJobRunEvery, other.taskUpdatePriorityJobRunEvery)
&& Objects.equals(
taskUpdatePriorityJobLockExpirationPeriod,
other.taskUpdatePriorityJobLockExpirationPeriod)
&& Objects.equals(userRefreshJobFirstRun, other.userRefreshJobFirstRun)
&& Objects.equals(userRefreshJobRunEvery, other.userRefreshJobRunEvery)
&& Objects.equals(
userRefreshJobLockExpirationPeriod, other.userRefreshJobLockExpirationPeriod)
&& Objects.equals(customJobs, other.customJobs)
&& Objects.equals(
minimalPermissionsToAssignDomains, other.minimalPermissionsToAssignDomains)
@ -524,12 +590,14 @@ public class TaskanaConfiguration {
@Override
public String toString() {
return "TaskanaConfiguration [dataSource="
return "TaskanaConfiguration{"
+ "dataSource="
+ dataSource
+ ", useManagedTransactions="
+ useManagedTransactions
+ ", schemaName="
+ ", schemaName='"
+ schemaName
+ '\''
+ ", securityEnabled="
+ securityEnabled
+ ", domains="
@ -542,6 +610,8 @@ public class TaskanaConfiguration {
+ classificationTypes
+ ", classificationCategoriesByType="
+ classificationCategoriesByType
+ ", useWorkingTimeCalculation="
+ useWorkingTimeCalculation
+ ", workingTimeSchedule="
+ workingTimeSchedule
+ ", workingTimeScheduleTimeZone="
@ -554,8 +624,9 @@ public class TaskanaConfiguration {
+ germanPublicHolidaysCorpusChristiEnabled
+ ", deleteHistoryEventsOnTaskDeletionEnabled="
+ deleteHistoryEventsOnTaskDeletionEnabled
+ ", logHistoryLoggerName="
+ ", logHistoryLoggerName='"
+ logHistoryLoggerName
+ '\''
+ ", jobSchedulerEnabled="
+ jobSchedulerEnabled
+ ", jobSchedulerInitialStartDelay="
@ -572,14 +643,20 @@ public class TaskanaConfiguration {
+ jobFirstRun
+ ", jobRunEvery="
+ jobRunEvery
+ ", jobLockExpirationPeriod="
+ jobLockExpirationPeriod
+ ", taskCleanupJobEnabled="
+ taskCleanupJobEnabled
+ ", taskCleanupJobMinimumAge="
+ taskCleanupJobMinimumAge
+ ", taskCleanupJobAllCompletedSameParentBusiness="
+ taskCleanupJobAllCompletedSameParentBusiness
+ ", taskCleanupJobLockExpirationPeriod="
+ taskCleanupJobLockExpirationPeriod
+ ", workbasketCleanupJobEnabled="
+ workbasketCleanupJobEnabled
+ ", workbasketCleanupJobLockExpirationPeriod="
+ workbasketCleanupJobLockExpirationPeriod
+ ", simpleHistoryCleanupJobEnabled="
+ simpleHistoryCleanupJobEnabled
+ ", simpleHistoryCleanupJobBatchSize="
@ -588,6 +665,8 @@ public class TaskanaConfiguration {
+ simpleHistoryCleanupJobMinimumAge
+ ", simpleHistoryCleanupJobAllCompletedSameParentBusiness="
+ simpleHistoryCleanupJobAllCompletedSameParentBusiness
+ ", simpleHistoryCleanupJobLockExpirationPeriod="
+ simpleHistoryCleanupJobLockExpirationPeriod
+ ", taskUpdatePriorityJobEnabled="
+ taskUpdatePriorityJobEnabled
+ ", taskUpdatePriorityJobBatchSize="
@ -596,12 +675,16 @@ public class TaskanaConfiguration {
+ taskUpdatePriorityJobFirstRun
+ ", taskUpdatePriorityJobRunEvery="
+ taskUpdatePriorityJobRunEvery
+ ", taskUpdatePriorityJobLockExpirationPeriod="
+ taskUpdatePriorityJobLockExpirationPeriod
+ ", userInfoRefreshJobEnabled="
+ userInfoRefreshJobEnabled
+ ", userRefreshJobFirstRun="
+ userRefreshJobFirstRun
+ ", userRefreshJobRunEvery="
+ userRefreshJobRunEvery
+ ", userRefreshJobLockExpirationPeriod="
+ userRefreshJobLockExpirationPeriod
+ ", customJobs="
+ customJobs
+ ", addAdditionalUserInfo="
@ -612,7 +695,7 @@ public class TaskanaConfiguration {
+ useSpecificDb2Taskquery
+ ", properties="
+ properties
+ "]";
+ '}';
}
// endregion
@ -650,6 +733,10 @@ public class TaskanaConfiguration {
// endregion
// region working time configuration
@TaskanaProperty("taskana.workingTime.useWorkingTimeCalculation")
private boolean useWorkingTimeCalculation = true;
@TaskanaProperty("taskana.workingTime.schedule")
private Map<DayOfWeek, Set<LocalTimeInterval>> workingTimeSchedule =
initDefaultWorkingTimeSchedule();
@ -700,6 +787,9 @@ public class TaskanaConfiguration {
@TaskanaProperty("taskana.jobs.runEvery")
private Duration jobRunEvery = Duration.ofDays(1);
@TaskanaProperty("taskana.jobs.lockExpirationPeriod")
private Duration jobLockExpirationPeriod = Duration.ofMinutes(30);
@TaskanaProperty("taskana.jobs.cleanup.task.enable")
private boolean taskCleanupJobEnabled = true;
@ -709,9 +799,15 @@ public class TaskanaConfiguration {
@TaskanaProperty("taskana.jobs.cleanup.task.allCompletedSameParentBusiness")
private boolean taskCleanupJobAllCompletedSameParentBusiness = true;
@TaskanaProperty("taskana.jobs.cleanup.task.lockExpirationPeriod")
private Duration taskCleanupJobLockExpirationPeriod = Duration.ofMinutes(30);
@TaskanaProperty("taskana.jobs.cleanup.workbasket.enable")
private boolean workbasketCleanupJobEnabled = true;
@TaskanaProperty("taskana.jobs.cleanup.workbasket.lockExpirationPeriod")
private Duration workbasketCleanupJobLockExpirationPeriod = Duration.ofMinutes(30);
@TaskanaProperty("taskana.jobs.cleanup.history.simple.enable")
private boolean simpleHistoryCleanupJobEnabled = false;
@ -724,6 +820,9 @@ public class TaskanaConfiguration {
@TaskanaProperty("taskana.jobs.cleanup.history.simple.allCompletedSameParentBusiness")
private boolean simpleHistoryCleanupJobAllCompletedSameParentBusiness = true;
@TaskanaProperty("taskana.jobs.cleanup.history.simple.lockExpirationPeriod")
private Duration simpleHistoryCleanupJobLockExpirationPeriod = Duration.ofMinutes(30);
@TaskanaProperty("taskana.jobs.priority.task.enable")
private boolean taskUpdatePriorityJobEnabled = false;
@ -736,6 +835,9 @@ public class TaskanaConfiguration {
@TaskanaProperty("taskana.jobs.priority.task.runEvery")
private Duration taskUpdatePriorityJobRunEvery = Duration.ofDays(1);
@TaskanaProperty("taskana.jobs.priority.task.lockExpirationPeriod")
private Duration taskUpdatePriorityJobLockExpirationPeriod = Duration.ofMinutes(30);
@TaskanaProperty("taskana.jobs.refresh.user.enable")
private boolean userInfoRefreshJobEnabled = false;
@ -745,6 +847,9 @@ public class TaskanaConfiguration {
@TaskanaProperty("taskana.jobs.refresh.user.runEvery")
private Duration userRefreshJobRunEvery = Duration.ofDays(1);
@TaskanaProperty("taskana.jobs.refresh.user.lockExpirationPeriod")
private Duration userRefreshJobLockExpirationPeriod = Duration.ofMinutes(30);
@TaskanaProperty("taskana.jobs.customJobs")
private Set<String> customJobs = new HashSet<>();
// endregion
@ -826,6 +931,7 @@ public class TaskanaConfiguration {
this.classificationTypes = conf.classificationTypes;
this.classificationCategoriesByType = conf.classificationCategoriesByType;
// working time configuration
this.useWorkingTimeCalculation = conf.useWorkingTimeCalculation;
this.workingTimeSchedule = conf.workingTimeSchedule;
this.workingTimeScheduleTimeZone = conf.workingTimeScheduleTimeZone;
this.customHolidays = conf.customHolidays;
@ -843,23 +949,31 @@ public class TaskanaConfiguration {
this.jobBatchSize = conf.jobBatchSize;
this.jobFirstRun = conf.jobFirstRun;
this.jobRunEvery = conf.jobRunEvery;
this.jobLockExpirationPeriod = conf.jobLockExpirationPeriod;
this.taskCleanupJobEnabled = conf.taskCleanupJobEnabled;
this.taskCleanupJobMinimumAge = conf.taskCleanupJobMinimumAge;
this.taskCleanupJobAllCompletedSameParentBusiness =
conf.taskCleanupJobAllCompletedSameParentBusiness;
this.taskCleanupJobLockExpirationPeriod = conf.taskCleanupJobLockExpirationPeriod;
this.workbasketCleanupJobEnabled = conf.workbasketCleanupJobEnabled;
this.workbasketCleanupJobLockExpirationPeriod = conf.workbasketCleanupJobLockExpirationPeriod;
this.simpleHistoryCleanupJobEnabled = conf.simpleHistoryCleanupJobEnabled;
this.simpleHistoryCleanupJobBatchSize = conf.simpleHistoryCleanupJobBatchSize;
this.simpleHistoryCleanupJobMinimumAge = conf.simpleHistoryCleanupJobMinimumAge;
this.simpleHistoryCleanupJobAllCompletedSameParentBusiness =
conf.simpleHistoryCleanupJobAllCompletedSameParentBusiness;
this.simpleHistoryCleanupJobLockExpirationPeriod =
conf.simpleHistoryCleanupJobLockExpirationPeriod;
this.taskUpdatePriorityJobEnabled = conf.taskUpdatePriorityJobEnabled;
this.taskUpdatePriorityJobBatchSize = conf.taskUpdatePriorityJobBatchSize;
this.taskUpdatePriorityJobFirstRun = conf.taskUpdatePriorityJobFirstRun;
this.taskUpdatePriorityJobRunEvery = conf.taskUpdatePriorityJobRunEvery;
this.taskUpdatePriorityJobLockExpirationPeriod =
conf.taskUpdatePriorityJobLockExpirationPeriod;
this.userInfoRefreshJobEnabled = conf.userInfoRefreshJobEnabled;
this.userRefreshJobFirstRun = conf.userRefreshJobFirstRun;
this.userRefreshJobRunEvery = conf.userRefreshJobRunEvery;
this.userRefreshJobLockExpirationPeriod = conf.userRefreshJobLockExpirationPeriod;
this.customJobs = conf.customJobs;
// user configuration
this.addAdditionalUserInfo = conf.addAdditionalUserInfo;
@ -964,6 +1078,11 @@ public class TaskanaConfiguration {
// region working time configuration
public Builder useWorkingTimeCalculation(boolean useWorkingTimeCalculation) {
this.useWorkingTimeCalculation = useWorkingTimeCalculation;
return this;
}
public Builder workingTimeSchedule(Map<DayOfWeek, Set<LocalTimeInterval>> workingTimeSchedule) {
this.workingTimeSchedule = workingTimeSchedule;
return this;
@ -1054,6 +1173,11 @@ public class TaskanaConfiguration {
return this;
}
public Builder jobLockExpirationPeriod(Duration jobLockExpirationPeriod) {
this.jobLockExpirationPeriod = jobLockExpirationPeriod;
return this;
}
public Builder taskCleanupJobMinimumAge(Duration taskCleanupJobMinimumAge) {
this.taskCleanupJobMinimumAge = taskCleanupJobMinimumAge;
return this;
@ -1066,11 +1190,22 @@ public class TaskanaConfiguration {
return this;
}
public Builder taskCleanupJobLockExpirationPeriod(Duration taskCleanupJobLockExpirationPeriod) {
this.taskCleanupJobLockExpirationPeriod = taskCleanupJobLockExpirationPeriod;
return this;
}
public Builder workbasketCleanupJobEnabled(boolean workbasketCleanupJobEnabled) {
this.workbasketCleanupJobEnabled = workbasketCleanupJobEnabled;
return this;
}
public Builder workbasketCleanupJobLockExpirationPeriod(
Duration workbasketCleanupJobLockExpirationPeriod) {
this.workbasketCleanupJobLockExpirationPeriod = workbasketCleanupJobLockExpirationPeriod;
return this;
}
public Builder simpleHistoryCleanupJobEnabled(boolean simpleHistoryCleanupJobEnabled) {
this.simpleHistoryCleanupJobEnabled = simpleHistoryCleanupJobEnabled;
return this;
@ -1093,6 +1228,13 @@ public class TaskanaConfiguration {
return this;
}
public Builder simpleHistoryCleanupJobLockExpirationPeriod(
Duration simpleHistoryCleanupJobLockExpirationPeriod) {
this.simpleHistoryCleanupJobLockExpirationPeriod =
simpleHistoryCleanupJobLockExpirationPeriod;
return this;
}
public Builder taskUpdatePriorityJobEnabled(boolean taskUpdatePriorityJobEnabled) {
this.taskUpdatePriorityJobEnabled = taskUpdatePriorityJobEnabled;
return this;
@ -1113,6 +1255,12 @@ public class TaskanaConfiguration {
return this;
}
public Builder taskUpdatePriorityJobLockExpirationPeriod(
Duration taskUpdatePriorityJobLockExpirationPeriod) {
this.taskUpdatePriorityJobLockExpirationPeriod = taskUpdatePriorityJobLockExpirationPeriod;
return this;
}
public Builder userInfoRefreshJobEnabled(boolean userInfoRefreshJobEnabled) {
this.userInfoRefreshJobEnabled = userInfoRefreshJobEnabled;
return this;
@ -1128,6 +1276,11 @@ public class TaskanaConfiguration {
return this;
}
public Builder userRefreshJobLockExpirationPeriod(Duration userRefreshJobLockExpirationPeriod) {
this.userRefreshJobLockExpirationPeriod = userRefreshJobLockExpirationPeriod;
return this;
}
public Builder customJobs(Set<String> customJobs) {
this.customJobs = customJobs;
return this;

View File

@ -1,18 +1,20 @@
package pro.taskana.common.internal;
import java.lang.reflect.InvocationTargetException;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pro.taskana.TaskanaConfiguration;
import pro.taskana.common.api.JobService;
import pro.taskana.common.api.ScheduledJob;
import pro.taskana.common.api.exceptions.SystemException;
/** Controls all job activities. */
public class JobServiceImpl implements JobService {
public static final int JOB_DEFAULT_PRIORITY = 50;
private static final Duration JOB_DEFAULT_LOCK_EXPIRATION_PERIOD = Duration.ofSeconds(60);
private static final Logger LOGGER = LoggerFactory.getLogger(JobServiceImpl.class);
private final JobMapper jobMapper;
@ -43,7 +45,27 @@ public class JobServiceImpl implements JobService {
public ScheduledJob lockJob(ScheduledJob job, String owner) {
job.setLockedBy(owner);
job.setLockExpires(Instant.now().plus(JOB_DEFAULT_LOCK_EXPIRATION_PERIOD));
Class<?> jobClass = null;
try {
jobClass = Thread.currentThread().getContextClassLoader().loadClass(job.getType());
job.setLockExpires(
Instant.now()
.plus(
(Duration)
jobClass
.getMethod("getLockExpirationPeriod", TaskanaConfiguration.class)
.invoke(null, taskanaEngineImpl.getEngine().getConfiguration())));
} catch (ClassNotFoundException | NoSuchMethodException e) {
throw new SystemException(
String.format(
"Job '%s' does not have a method matching ('getLockExpirationPeriod', %s",
jobClass, TaskanaConfiguration.class));
} catch (InvocationTargetException | IllegalAccessException e) {
throw new SystemException(
String.format(
"Caught Exception while invoking method 'getLockExpirationPeriod' by reflection"));
}
job.setRetryCount(job.getRetryCount() - 1);
taskanaEngineImpl.executeInDatabaseConnection(() -> jobMapper.update(job));
if (LOGGER.isDebugEnabled()) {

View File

@ -51,6 +51,7 @@ import pro.taskana.common.internal.persistence.MapTypeHandler;
import pro.taskana.common.internal.persistence.StringTypeHandler;
import pro.taskana.common.internal.security.CurrentUserContextImpl;
import pro.taskana.common.internal.workingtime.HolidaySchedule;
import pro.taskana.common.internal.workingtime.WorkingDayCalculatorImpl;
import pro.taskana.common.internal.workingtime.WorkingTimeCalculatorImpl;
import pro.taskana.monitor.api.MonitorService;
import pro.taskana.monitor.internal.MonitorMapper;
@ -128,11 +129,18 @@ public class TaskanaEngineImpl implements TaskanaEngine {
taskanaConfiguration.isGermanPublicHolidaysEnabled(),
taskanaConfiguration.isGermanPublicHolidaysCorpusChristiEnabled(),
taskanaConfiguration.getCustomHolidays());
workingTimeCalculator =
new WorkingTimeCalculatorImpl(
holidaySchedule,
taskanaConfiguration.getWorkingTimeSchedule(),
taskanaConfiguration.getWorkingTimeScheduleTimeZone());
if (taskanaConfiguration.isUseWorkingTimeCalculation()) {
workingTimeCalculator =
new WorkingTimeCalculatorImpl(
holidaySchedule,
taskanaConfiguration.getWorkingTimeSchedule(),
taskanaConfiguration.getWorkingTimeScheduleTimeZone());
} else {
workingTimeCalculator =
new WorkingDayCalculatorImpl(
holidaySchedule, taskanaConfiguration.getWorkingTimeScheduleTimeZone());
}
currentUserContext =
new CurrentUserContextImpl(TaskanaConfiguration.shouldUseLowerCaseForAccessIds());
createTransactionFactory(taskanaConfiguration.isUseManagedTransactions());

View File

@ -4,6 +4,7 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.time.Duration;
import java.time.Instant;
import pro.taskana.TaskanaConfiguration;
import pro.taskana.common.api.ScheduledJob;
import pro.taskana.common.api.TaskanaEngine;
import pro.taskana.common.api.exceptions.SystemException;
@ -42,8 +43,7 @@ public abstract class AbstractTaskanaJob implements TaskanaJob {
try {
jobClass = Thread.currentThread().getContextClassLoader().loadClass(job.getType());
} catch (ClassNotFoundException e) {
throw new SystemException(
String.format("Can't load class '%s'", job.getType()));
throw new SystemException(String.format("Can't load class '%s'", job.getType()));
}
return initTaskanaJob(engine, jobClass, txProvider, job);
@ -76,6 +76,46 @@ public abstract class AbstractTaskanaJob implements TaskanaJob {
job.scheduleNextJob();
}
public boolean isAsync() {
return async;
}
public Instant getFirstRun() {
return firstRun;
}
public Duration getRunEvery() {
return runEvery;
}
public static Duration getLockExpirationPeriod(TaskanaConfiguration taskanaConfiguration) {
return taskanaConfiguration.getJobLockExpirationPeriod();
}
protected abstract String getType();
protected abstract void execute() throws TaskanaException;
protected Instant getNextDueForJob() {
Instant nextRun = firstRun;
if (scheduledJob != null && scheduledJob.getDue() != null) {
nextRun = scheduledJob.getDue();
}
while (nextRun.isBefore(Instant.now())) {
nextRun = nextRun.plus(runEvery);
}
return nextRun;
}
protected void scheduleNextJob() {
ScheduledJob job = new ScheduledJob();
job.setType(getType());
job.setDue(getNextDueForJob());
taskanaEngineImpl.getJobService().createJob(job);
}
private static AbstractTaskanaJob initTaskanaJob(
TaskanaEngine taskanaEngine,
Class<?> jobClass,
@ -120,40 +160,4 @@ public abstract class AbstractTaskanaJob implements TaskanaJob {
}
return job;
}
public boolean isAsync() {
return async;
}
public Instant getFirstRun() {
return firstRun;
}
public Duration getRunEvery() {
return runEvery;
}
protected abstract String getType();
protected abstract void execute() throws TaskanaException;
protected Instant getNextDueForJob() {
Instant nextRun = firstRun;
if (scheduledJob != null && scheduledJob.getDue() != null) {
nextRun = scheduledJob.getDue();
}
while (nextRun.isBefore(Instant.now())) {
nextRun = nextRun.plus(runEvery);
}
return nextRun;
}
protected void scheduleNextJob() {
ScheduledJob job = new ScheduledJob();
job.setType(getType());
job.setDue(getNextDueForJob());
taskanaEngineImpl.getJobService().createJob(job);
}
}

View File

@ -303,7 +303,8 @@ class ServiceLevelHandler {
private Instant calculateDue(Instant planned, Duration duration) {
Instant dueExclusive = workingTimeCalculator.addWorkingTime(planned, duration);
if (!planned.equals(dueExclusive)) {
if (taskanaEngine.getEngine().getConfiguration().isUseWorkingTimeCalculation()
&& !planned.equals(dueExclusive)) {
// Calculation is exclusive, but we want due date to be inclusive. Hence, we subtract a
// millisecond
// If planned and dueExclusive are the same values, we don't want due to be before planned.
@ -320,7 +321,11 @@ class ServiceLevelHandler {
return due;
} else {
// due is inclusive, but calculation happens exclusive.
return workingTimeCalculator.subtractWorkingTime(due.plusMillis(1), duration);
Instant normalize =
taskanaEngine.getEngine().getConfiguration().isUseWorkingTimeCalculation()
? due.plusMillis(1)
: due;
return workingTimeCalculator.subtractWorkingTime(normalize, duration);
}
}
@ -328,9 +333,13 @@ class ServiceLevelHandler {
// plusMillis since due is inclusive, but calculation happens exclusive.
// minusMillis since we calculated a due date
// Without that some edge case fail (e.g. due is exactly the start of weekend)
return workingTimeCalculator
.subtractWorkingTime(due.plusMillis(1), Duration.ZERO)
.minusMillis(1);
if (taskanaEngine.getEngine().getConfiguration().isUseWorkingTimeCalculation()) {
return workingTimeCalculator
.subtractWorkingTime(due.plusMillis(1), Duration.ZERO)
.minusMillis(1);
}
return workingTimeCalculator.subtractWorkingTime(due, Duration.ZERO);
}
private Instant normalizePlanned(Instant instant) {
@ -625,9 +634,9 @@ class ServiceLevelHandler {
}
// TODO Do we need to compare Key and Id or could we simply compare ClassificationSummary only?
final boolean isClassificationKeyChanged =
Objects.equals(newTaskImpl.getClassificationKey(), oldTaskImpl.getClassificationKey());
!Objects.equals(newTaskImpl.getClassificationKey(), oldTaskImpl.getClassificationKey());
final boolean isClassificationIdChanged =
Objects.equals(newTaskImpl.getClassificationId(), oldTaskImpl.getClassificationId());
!Objects.equals(newTaskImpl.getClassificationId(), oldTaskImpl.getClassificationId());
final boolean isManualPriorityChanged =
newTaskImpl.getManualPriority() != oldTaskImpl.getManualPriority();

View File

@ -11,6 +11,7 @@ import java.util.Map;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pro.taskana.TaskanaConfiguration;
import pro.taskana.common.api.BaseQuery.SortDirection;
import pro.taskana.common.api.BulkOperationResults;
import pro.taskana.common.api.ScheduledJob;
@ -64,6 +65,10 @@ public class TaskCleanupJob extends AbstractTaskanaJob {
}
}
public static Duration getLockExpirationPeriod(TaskanaConfiguration taskanaConfiguration) {
return taskanaConfiguration.getTaskCleanupJobLockExpirationPeriod();
}
@Override
protected String getType() {
return TaskCleanupJob.class.getName();

View File

@ -2,8 +2,10 @@ package pro.taskana.task.internal.jobs;
import static pro.taskana.common.internal.util.CollectionUtil.partitionBasedOnSize;
import java.time.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pro.taskana.TaskanaConfiguration;
import pro.taskana.common.api.ScheduledJob;
import pro.taskana.common.api.TaskanaEngine;
import pro.taskana.common.api.exceptions.SystemException;
@ -45,6 +47,10 @@ public class TaskUpdatePriorityJob extends AbstractTaskanaJob {
}
}
public static Duration getLockExpirationPeriod(TaskanaConfiguration taskanaConfiguration) {
return taskanaConfiguration.getTaskUpdatePriorityJobLockExpirationPeriod();
}
public int getBatchSize() {
return batchSize;
}

View File

@ -1,8 +1,10 @@
package pro.taskana.workbasket.internal.jobs;
import java.time.Duration;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pro.taskana.TaskanaConfiguration;
import pro.taskana.common.api.BaseQuery;
import pro.taskana.common.api.BulkOperationResults;
import pro.taskana.common.api.ScheduledJob;
@ -48,6 +50,10 @@ public class WorkbasketCleanupJob extends AbstractTaskanaJob {
}
}
public static Duration getLockExpirationPeriod(TaskanaConfiguration taskanaConfiguration) {
return taskanaConfiguration.getWorkbasketCleanupJobLockExpirationPeriod();
}
@Override
protected String getType() {
return WorkbasketCleanupJob.class.getName();

View File

@ -1,6 +1,7 @@
package acceptance;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
@ -56,6 +57,14 @@ public abstract class AbstractAccTest {
.ifPresent(JobScheduler::stop);
}
protected static void initTaskanaEngine(TaskanaConfiguration configuration) throws SQLException {
taskanaConfiguration = configuration;
taskanaEngine =
TaskanaEngine.buildTaskanaEngine(taskanaConfiguration, ConnectionManagementMode.AUTOCOMMIT);
taskService = (TaskServiceImpl) taskanaEngine.getTaskService();
workingTimeCalculator = taskanaEngine.getWorkingTimeCalculator();
}
protected static void resetDb(boolean dropTables) throws Exception {
DataSource dataSource = DataSourceGenerator.getDataSource();

View File

@ -4,12 +4,18 @@ import static org.assertj.core.api.Assertions.assertThat;
import acceptance.AbstractAccTest;
import java.sql.Connection;
import java.sql.SQLException;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.stream.Stream;
import javax.sql.DataSource;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestFactory;
import org.junit.jupiter.api.function.ThrowingConsumer;
import org.mockito.Mockito;
import org.mockito.internal.stubbing.answers.CallsRealMethods;
import org.mockito.invocation.InvocationOnMock;
@ -20,13 +26,20 @@ import pro.taskana.common.api.exceptions.SystemException;
import pro.taskana.common.internal.JobServiceImpl;
import pro.taskana.common.internal.jobs.JobRunner;
import pro.taskana.common.internal.jobs.PlainJavaTransactionProvider;
import pro.taskana.common.internal.util.Pair;
import pro.taskana.common.test.config.DataSourceGenerator;
import pro.taskana.common.test.util.ParallelThreadHelper;
import pro.taskana.task.internal.jobs.TaskCleanupJob;
import pro.taskana.task.internal.jobs.TaskUpdatePriorityJob;
import pro.taskana.workbasket.internal.jobs.WorkbasketCleanupJob;
@Disabled
class JobRunnerAccTest extends AbstractAccTest {
private static final Duration TASK_CLEANUP_JOB_LOCK_EXPIRATION_PERIOD = Duration.ofMinutes(4);
private static final Duration WORKBASKET_CLEANUP_JOB_LOCK_EXPIRATION_PERIOD =
Duration.ofMinutes(3);
private static final Duration TASK_UPDATE_PRIORITY_LOCK_EXPIRATION_PERIOD = Duration.ofMinutes(1);
private final JobServiceImpl jobService = (JobServiceImpl) taskanaEngine.getJobService();
@Test
@ -34,7 +47,8 @@ class JobRunnerAccTest extends AbstractAccTest {
resetDb(true); // for some reason clearing the job table is not enough..
assertThat(jobService.findJobsToRun()).isEmpty();
ScheduledJob job = createJob(Instant.now().minus(5, ChronoUnit.MINUTES));
ScheduledJob job =
createJob(Instant.now().minus(5, ChronoUnit.MINUTES), TaskCleanupJob.class.getName());
assertThat(jobService.findJobsToRun()).containsExactly(job);
ParallelThreadHelper.runInThread(
@ -70,9 +84,55 @@ class JobRunnerAccTest extends AbstractAccTest {
assertThat(jobsToRun).hasSize(1).doesNotContain(job);
}
private ScheduledJob createJob(Instant firstDue) {
@TestFactory
Stream<DynamicTest> should_setTheLockExpirationDateCorrectly_When_CreatingJobs() {
List<Pair<String, Duration>> list =
List.of(
Pair.of(TaskCleanupJob.class.getName(), TASK_CLEANUP_JOB_LOCK_EXPIRATION_PERIOD),
Pair.of(
TaskUpdatePriorityJob.class.getName(), TASK_UPDATE_PRIORITY_LOCK_EXPIRATION_PERIOD),
Pair.of(
WorkbasketCleanupJob.class.getName(),
WORKBASKET_CLEANUP_JOB_LOCK_EXPIRATION_PERIOD));
ThrowingConsumer<Pair<String, Duration>> testSettingLockExpirationDate =
p -> {
resetDb(true);
assertThat(jobService.findJobsToRun()).isEmpty();
createJob(Instant.now().minus(5, ChronoUnit.MINUTES), p.getLeft());
ParallelThreadHelper.runInThread(
() -> {
TaskanaEngine taskanaEngine;
try {
taskanaEngine =
TaskanaEngine.buildTaskanaEngine(
taskanaConfiguration, ConnectionManagementMode.AUTOCOMMIT);
} catch (SQLException e) {
throw new RuntimeException("Could not build the TaskanaEngine");
}
DataSource dataSource = DataSourceGenerator.getDataSource();
PlainJavaTransactionProvider transactionProvider =
new PlainJavaTransactionProvider(taskanaEngine, dataSource);
JobRunner runner = new JobRunner(taskanaEngine);
runner.registerTransactionProvider(transactionProvider);
runner.runJobs();
},
1);
List<ScheduledJob> resultJobs =
getJobMapper(taskanaEngine).findJobsToRun(Instant.now().plus(2, ChronoUnit.DAYS));
assertThat(resultJobs).hasSize(1);
assertThat(resultJobs.get(0).getType()).isEqualTo(p.getLeft());
assertThat(resultJobs.get(0).getLockExpires())
.isBetween(
resultJobs.get(0).getCreated().plus(p.getRight()),
resultJobs.get(0).getCreated().plus(p.getRight()).plusSeconds(1));
};
return DynamicTest.stream(list.iterator(), Pair::getLeft, testSettingLockExpirationDate);
}
private ScheduledJob createJob(Instant firstDue, String type) {
ScheduledJob job = new ScheduledJob();
job.setType(TaskCleanupJob.class.getName());
job.setType(type);
job.setDue(firstDue);
jobService.createJob(job);
return job;

View File

@ -0,0 +1,223 @@
package acceptance.persistence;
import static org.assertj.core.api.Assertions.assertThat;
import static pro.taskana.common.api.SharedConstants.MASTER_DOMAIN;
import acceptance.AbstractAccTest;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import pro.taskana.TaskanaConfiguration;
import pro.taskana.TaskanaConfiguration.Builder;
import pro.taskana.classification.api.ClassificationService;
import pro.taskana.classification.api.models.Classification;
import pro.taskana.common.api.JobService;
import pro.taskana.common.api.ScheduledJob;
import pro.taskana.common.internal.JobServiceImpl;
import pro.taskana.common.test.security.JaasExtension;
import pro.taskana.common.test.security.WithAccessId;
import pro.taskana.task.api.TaskCustomField;
import pro.taskana.task.api.TaskService;
import pro.taskana.task.api.models.Task;
import pro.taskana.task.internal.jobs.TaskCleanupJob;
import pro.taskana.task.internal.models.TaskImpl;
import pro.taskana.workbasket.api.WorkbasketCustomField;
import pro.taskana.workbasket.api.WorkbasketPermission;
import pro.taskana.workbasket.api.WorkbasketService;
import pro.taskana.workbasket.api.WorkbasketType;
import pro.taskana.workbasket.api.models.Workbasket;
import pro.taskana.workbasket.api.models.WorkbasketAccessItem;
@ExtendWith(JaasExtension.class)
public class UpdateObjectsUseUtcTimeStampsWithWorkingDaysCalculationAccTest
extends AbstractAccTest {
@BeforeAll
protected static void setupTest() throws Exception {
resetDb(false);
TaskanaConfiguration config =
new Builder(taskanaConfiguration).useWorkingTimeCalculation(false).build();
initTaskanaEngine(config);
}
@WithAccessId(user = "admin")
@Test
void testTimestampsOnTaskUpdate() throws Exception {
TaskService taskService = taskanaEngine.getTaskService();
Task task = taskService.getTask("TKI:000000000000000000000000000000000000");
Instant now = Instant.now();
task.setPlanned(now.plus(Duration.ofHours(17)));
// associated Classification has ServiceLevel 'P1D'
task.setDue(workingTimeCalculator.addWorkingTime(task.getPlanned(), Duration.ofDays(1)));
TaskImpl ti = (TaskImpl) task;
ti.setCompleted(now.plus(Duration.ofHours(27)));
TimeZone originalZone = TimeZone.getDefault();
Task updatedTask = taskService.updateTask(task);
TimeZone.setDefault(TimeZone.getTimeZone("EST"));
Task retrievedTask = taskService.getTask(updatedTask.getId());
TimeZone.setDefault(originalZone);
assertThat(retrievedTask).isEqualTo(updatedTask);
}
@WithAccessId(user = "user-1-1")
@Test
void testCreatedTaskObjectEqualsReadTaskObjectInNewTimezone() throws Exception {
TaskService taskService = taskanaEngine.getTaskService();
Task newTask = taskService.newTask("USER-1-1", "DOMAIN_A");
newTask.setClassificationKey("T2100");
newTask.setPrimaryObjRef(
createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"));
for (TaskCustomField taskCustomField : TaskCustomField.values()) {
newTask.setCustomField(taskCustomField, taskCustomField.name());
}
newTask.setCustomAttributeMap(createSimpleCustomPropertyMap(5));
newTask.setDescription("Description of test task");
newTask.setNote("My note");
newTask.addAttachment(
createExampleAttachment(
"DOCTYPE_DEFAULT",
createObjectReference(
"COMPANY_A",
"SYSTEM_B",
"INSTANCE_B",
"ArchiveId",
"12345678901234567890123456789012345678901234567890"),
"E-MAIL",
Instant.parse("2018-01-15T00:00:00Z"),
createSimpleCustomPropertyMap(3)));
TimeZone originalZone = TimeZone.getDefault();
Task createdTask = taskService.createTask(newTask);
TimeZone.setDefault(TimeZone.getTimeZone("EST"));
Task readTask = taskService.getTask(createdTask.getId());
TimeZone.setDefault(originalZone);
assertThat(readTask).isEqualTo(createdTask);
}
@WithAccessId(user = "admin")
@Test
void testTimestampsOnClassificationUpdate() throws Exception {
ClassificationService classificationService = taskanaEngine.getClassificationService();
Classification classification =
classificationService.getClassification("CLI:000000000000000000000000000000000001");
classification.setPriority(17);
TimeZone originalZone = TimeZone.getDefault();
Classification updatedClassification =
classificationService.updateClassification(classification);
TimeZone.setDefault(TimeZone.getTimeZone("EST"));
Classification retrievedClassification =
classificationService.getClassification(updatedClassification.getId());
TimeZone.setDefault(originalZone);
assertThat(retrievedClassification).isEqualTo(updatedClassification);
}
@WithAccessId(user = "businessadmin")
@Test
void testTimestampsOnCreateMasterClassification() throws Exception {
ClassificationService classificationService = taskanaEngine.getClassificationService();
final long amountOfClassificationsBefore =
classificationService.createClassificationQuery().count();
Classification classification =
classificationService.newClassification("Key0", MASTER_DOMAIN, "TASK");
classification.setIsValidInDomain(true);
classification.setServiceLevel("P1D");
classification = classificationService.createClassification(classification);
// check only 1 created
long amountOfClassificationsAfter = classificationService.createClassificationQuery().count();
assertThat(amountOfClassificationsAfter).isEqualTo(amountOfClassificationsBefore + 1);
TimeZone originalZone = TimeZone.getDefault();
TimeZone.setDefault(TimeZone.getTimeZone("EST"));
Classification retrievedClassification =
classificationService.getClassification(classification.getId());
TimeZone.setDefault(originalZone);
assertThat(retrievedClassification).isEqualTo(classification);
}
@WithAccessId(user = "admin")
@Test
void testTimestampsOnWorkbasketUpdate() throws Exception {
WorkbasketService workbasketService = taskanaEngine.getWorkbasketService();
Workbasket workbasket =
workbasketService.getWorkbasket("WBI:100000000000000000000000000000000001");
workbasket.setCustomField(WorkbasketCustomField.CUSTOM_1, "bla");
TimeZone originalZone = TimeZone.getDefault();
Workbasket updatedWorkbasket = workbasketService.updateWorkbasket(workbasket);
TimeZone.setDefault(TimeZone.getTimeZone("EST"));
Workbasket retrievedWorkbasket = workbasketService.getWorkbasket(updatedWorkbasket.getId());
TimeZone.setDefault(originalZone);
assertThat(retrievedWorkbasket).isEqualTo(updatedWorkbasket);
}
@WithAccessId(user = "businessadmin")
@Test
void testTimestampsOnCreateWorkbasket() throws Exception {
WorkbasketService workbasketService = taskanaEngine.getWorkbasketService();
final int before = workbasketService.createWorkbasketQuery().domainIn("DOMAIN_A").list().size();
Workbasket workbasket = workbasketService.newWorkbasket("NT1234", "DOMAIN_A");
workbasket.setName("Megabasket");
workbasket.setType(WorkbasketType.GROUP);
workbasket.setOrgLevel1("company");
workbasket = workbasketService.createWorkbasket(workbasket);
WorkbasketAccessItem wbai =
workbasketService.newWorkbasketAccessItem(workbasket.getId(), "user-1-2");
wbai.setPermission(WorkbasketPermission.READ, true);
workbasketService.createWorkbasketAccessItem(wbai);
int after = workbasketService.createWorkbasketQuery().domainIn("DOMAIN_A").list().size();
assertThat(after).isEqualTo(before + 1);
TimeZone originalZone = TimeZone.getDefault();
TimeZone.setDefault(TimeZone.getTimeZone("EST"));
Workbasket retrievedWorkbasket = workbasketService.getWorkbasket("NT1234", "DOMAIN_A");
TimeZone.setDefault(originalZone);
assertThat(retrievedWorkbasket).isEqualTo(workbasket);
}
@WithAccessId(user = "user-1-2")
@Test
void testTimestampsOnCreateScheduledJob() throws Exception {
resetDb(true);
ScheduledJob job = new ScheduledJob();
job.setArguments(Map.of("keyBla", "valueBla"));
job.setType(TaskCleanupJob.class.getName());
job.setDue(Instant.now().minus(Duration.ofHours(5)));
job.setLockExpires(Instant.now().minus(Duration.ofHours(5)));
JobService jobService = taskanaEngine.getJobService();
job = jobService.createJob(job);
TimeZone originalZone = TimeZone.getDefault();
TimeZone.setDefault(TimeZone.getTimeZone("EST"));
JobServiceImpl jobServiceImpl = (JobServiceImpl) jobService;
List<ScheduledJob> jobs = jobServiceImpl.findJobsToRun();
final ScheduledJob jobForLambda = job;
ScheduledJob retrievedJob =
jobs.stream()
.filter(
j ->
j.getJobId().equals(jobForLambda.getJobId())
&& j.getArguments() != null
&& "valueBla".equals(j.getArguments().get("keyBla")))
.findFirst()
.orElse(null);
TimeZone.setDefault(originalZone);
assertThat(retrievedJob).isEqualTo(job);
}
}

View File

@ -0,0 +1,616 @@
package acceptance.task;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import acceptance.AbstractAccTest;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
import org.assertj.core.data.TemporalUnitWithinOffset;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import pro.taskana.TaskanaConfiguration;
import pro.taskana.TaskanaConfiguration.Builder;
import pro.taskana.classification.api.ClassificationService;
import pro.taskana.classification.api.models.Classification;
import pro.taskana.common.api.BulkOperationResults;
import pro.taskana.common.api.exceptions.InvalidArgumentException;
import pro.taskana.common.api.exceptions.TaskanaException;
import pro.taskana.common.test.security.JaasExtension;
import pro.taskana.common.test.security.WithAccessId;
import pro.taskana.task.api.TaskService;
import pro.taskana.task.api.exceptions.TaskNotFoundException;
import pro.taskana.task.api.models.Task;
import pro.taskana.workbasket.api.exceptions.NotAuthorizedOnWorkbasketException;
@ExtendWith(JaasExtension.class)
public class ServiceLevelPriorityWithWorkingDaysCalculationAccTest extends AbstractAccTest {
private static ClassificationService classificationService;
@BeforeAll
protected static void setupTest() throws Exception {
resetDb(false);
TaskanaConfiguration config =
new Builder(taskanaConfiguration).useWorkingTimeCalculation(false).build();
initTaskanaEngine(config);
classificationService = taskanaEngine.getClassificationService();
}
/* CREATE TASK */
@WithAccessId(user = "user-1-1")
@Test
void should_CalculatePlannedDateAtCreate() throws Exception {
// P16D
Classification classification = classificationService.getClassification("L110105", "DOMAIN_A");
long serviceLevelDays = Duration.parse(classification.getServiceLevel()).toDays();
assertThat(serviceLevelDays).isEqualTo(16);
Task newTask = taskService.newTask("USER-1-1", classification.getDomain());
newTask.setClassificationKey(classification.getKey());
newTask.setPrimaryObjRef(
createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"));
Instant due =
moveBackToWorkingDay(
Instant.now().truncatedTo(ChronoUnit.MILLIS).plus(40, ChronoUnit.DAYS));
newTask.setDue(due);
Task createdTask = taskService.createTask(newTask);
assertThat(createdTask.getId()).isNotNull();
Task readTask = taskService.getTask(createdTask.getId());
assertThat(readTask).isNotNull();
assertThat(readTask.getDue()).isEqualTo(due);
Instant expectedPlanned =
workingTimeCalculator.subtractWorkingTime(due, Duration.ofDays(serviceLevelDays));
assertThat(readTask.getPlanned()).isEqualTo(expectedPlanned);
}
@WithAccessId(user = "user-1-1")
@Test
void should_CalculateDueDateAtCreate() throws Exception {
// P16D
Classification classification = classificationService.getClassification("L110105", "DOMAIN_A");
long serviceLevelDays = Duration.parse(classification.getServiceLevel()).toDays();
assertThat(serviceLevelDays).isEqualTo(16);
Task newTask = taskService.newTask("USER-1-1", classification.getDomain());
newTask.setClassificationKey(classification.getKey());
newTask.setPrimaryObjRef(
createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"));
Instant planned = moveForwardToWorkingDay(Instant.now().truncatedTo(ChronoUnit.MILLIS));
newTask.setPlanned(planned);
Task createdTask = taskService.createTask(newTask);
assertThat(createdTask.getId()).isNotNull();
Task readTask = taskService.getTask(createdTask.getId());
assertThat(readTask).isNotNull();
assertThat(readTask.getPlanned()).isEqualTo(planned);
Instant expectedDue =
workingTimeCalculator.addWorkingTime(
readTask.getPlanned(), Duration.ofDays(serviceLevelDays));
assertThat(readTask.getDue()).isEqualTo(expectedDue);
}
@WithAccessId(user = "user-1-1")
@Test
void should_NotThrowException_When_DueAndPlannedAreConsistent() throws Exception {
Classification classification = classificationService.getClassification("T2100", "DOMAIN_A");
long duration = Duration.parse(classification.getServiceLevel()).toDays();
Task newTask = taskService.newTask("USER-1-1", "DOMAIN_A");
newTask.setPlanned(moveForwardToWorkingDay(Instant.now()));
newTask.setClassificationKey(classification.getKey());
newTask.setPrimaryObjRef(
createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"));
newTask.setOwner("user-1-1");
// due date according to service level
Instant expectedDue =
workingTimeCalculator.addWorkingTime(newTask.getPlanned(), Duration.ofDays(duration));
newTask.setDue(expectedDue);
ThrowingCallable call = () -> taskService.createTask(newTask);
assertThatCode(call).doesNotThrowAnyException();
}
@WithAccessId(user = "user-1-1")
@Test
void should_ThrowException_When_DueAndPlannedAreInconsistent() {
Task newTask = taskService.newTask("USER-1-1", "DOMAIN_A");
Instant planned = moveForwardToWorkingDay(Instant.now().plus(2, ChronoUnit.HOURS));
newTask.setClassificationKey("T2100"); // P10D
newTask.setPrimaryObjRef(
createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"));
newTask.setOwner("user-1-1");
newTask.setPlanned(planned);
newTask.setDue(planned); // due date not according to service level
ThrowingCallable call = () -> taskService.createTask(newTask);
assertThatThrownBy(call).isInstanceOf(InvalidArgumentException.class);
}
@WithAccessId(user = "user-1-1")
@Test
void should_VerifyThatCreateAndPlannedAreClose() throws Exception {
Instant now = Instant.now().truncatedTo(ChronoUnit.MILLIS);
Instant inTwoHours = now.plus(2, ChronoUnit.HOURS);
Task newTask = taskService.newTask("USER-1-1", "DOMAIN_A");
Instant planned = moveForwardToWorkingDay(inTwoHours);
newTask.setClassificationKey("T2100");
newTask.setPrimaryObjRef(
createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"));
newTask.setOwner("user-1-1");
newTask.setPlanned(planned);
Task createdTask = taskService.createTask(newTask);
assertThat(createdTask).isNotNull();
assertThat(createdTask.getPlanned()).isEqualTo(planned);
assertThat(createdTask.getCreated()).isBefore(createdTask.getPlanned());
assertThat(createdTask.getPlanned())
.isCloseTo(
moveForwardToWorkingDay(createdTask.getCreated()),
new TemporalUnitWithinOffset(2L, ChronoUnit.HOURS));
}
/* UPDATE TASK */
@WithAccessId(user = "user-1-1")
@Test
void should_ThrowException_When_DueAndPlannedAreChangedInconsistently() throws Exception {
TaskService taskService = taskanaEngine.getTaskService();
Task task = taskService.getTask("TKI:000000000000000000000000000000000000"); // P1D
task.setDue(Instant.parse("2020-07-02T00:00:00Z"));
task.setPlanned(Instant.parse("2020-07-07T00:00:00Z"));
assertThatThrownBy(() -> taskService.updateTask(task))
.isInstanceOf(InvalidArgumentException.class)
.hasMessage(
"Cannot update a task with given planned 2020-07-07T00:00:00Z and due "
+ "date 2020-07-02T00:00:00Z not matching the service level PT24H.");
}
@WithAccessId(user = "user-b-2")
@Test
void should_SetPlanned_When_SetPlannedRequestContainsDuplicateTaskIds() throws Exception {
// This test works with the following tasks (w/o attachments) and classifications
//
// +-----------------------------------------+------------------------------------------+------+
// | TaskId | ClassificationId | SL |
// +-----------------------------------------+------------------------------------------+------+
// |TKI:000000000000000000000000000000000058 | CLI:200000000000000000000000000000000017 | P1D |
// |TKI:000000000000000000000000000000000059 | CLI:200000000000000000000000000000000017 | P1D |
// |TKI:000000000000000000000000000000000060 | CLI:200000000000000000000000000000000017 | P1D |
// +-----------------------------------------+------------------------------------------+------+
String tkId1 = "TKI:000000000000000000000000000000000058";
String tkId2 = "TKI:000000000000000000000000000000000059";
String tkId3 = "TKI:000000000000000000000000000000000058";
String tkId4 = "TKI:000000000000000000000000000000000060";
List<String> taskIds = List.of(tkId1, tkId2, tkId3, tkId4);
Instant planned = getInstant("2020-02-11T07:00:00");
BulkOperationResults<String, TaskanaException> results =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
assertThat(results.containsErrors()).isFalse();
Instant dueExpected = getInstant("2020-02-12T07:00:00");
Instant due1 = taskService.getTask(tkId1).getDue();
assertThat(due1).isEqualTo(dueExpected);
Instant due2 = taskService.getTask(tkId2).getDue();
assertThat(due2).isEqualTo(dueExpected);
Instant due3 = taskService.getTask(tkId3).getDue();
assertThat(due3).isEqualTo(dueExpected);
Instant due4 = taskService.getTask(tkId4).getDue();
assertThat(due4).isEqualTo(dueExpected);
}
@WithAccessId(user = "user-b-2")
@Test
void should_SetPlanned_When_RequestContainsDuplicatesAndNotExistingTaskIds() throws Exception {
String tkId1 = "TKI:000000000000000000000000000000000058";
String tkId2 = "TKI:000000000000000000000000000047110059";
String tkId3 = "TKI:000000000000000000000000000000000059";
String tkId4 = "TKI:000000000000000000000000000000000058";
String tkId5 = "TKI:000000000000000000000000000000000060";
List<String> taskIds = List.of(tkId1, tkId2, tkId3, tkId4, tkId5);
Instant planned = getInstant("2020-04-20T07:00:00");
BulkOperationResults<String, TaskanaException> results =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
assertThat(results.containsErrors()).isTrue();
assertThat(results.getErrorMap()).hasSize(1);
assertThat(results.getErrorForId("TKI:000000000000000000000000000047110059"))
.isInstanceOf(TaskNotFoundException.class);
Instant dueExpected = getInstant("2020-04-21T07:00:00");
Instant due1 = taskService.getTask(tkId1).getDue();
assertThat(due1).isEqualTo(dueExpected);
Instant due3 = taskService.getTask(tkId3).getDue();
assertThat(due3).isEqualTo(dueExpected);
Instant due5 = taskService.getTask(tkId5).getDue();
assertThat(due5).isEqualTo(dueExpected);
}
@WithAccessId(user = "user-1-1")
@Test
void should_SetPlanned_When_RequestContainsTasksWithAttachments() throws Exception {
// This test works with the following tasks, attachments and classifications
//
// +-----------------------------------------+------------------------------------------+------+
// | Task / associated attachment | Classification | SL |
// +-----------------------------------------+------------------------------------------+------+
// |TKI:000000000000000000000000000000000000 | CLI:100000000000000000000000000000000016 | P1D |
// |TAI:000000000000000000000000000000000000 | CLI:100000000000000000000000000000000003 | P13D |
// |TAI:000000000000000000000000000000000009 | CLI:100000000000000000000000000000000003 | P13D |
// +-----------------------------------------+------------------------------------------+------+
// |TKI:000000000000000000000000000000000001 | CLI:100000000000000000000000000000000005 | P15D |
// |TAI:000000000000000000000000000000000001 | CLI:100000000000000000000000000000000002 | P2D |
// |TAI:000000000000000000000000000000000002 | CLI:000000000000000000000000000000000003 | P3D |
// +-----------------------------------------+------------------------------------------+------+
// |TKI:000000000000000000000000000000000002 | CLI:100000000000000000000000000000000016 | P1D |
// |TAI:000000000000000000000000000000000003 | CLI:000000000000000000000000000000000004 | P4D |
// |TAI:000000000000000000000000000000000004 | CLI:000000000000000000000000000000000005 | P5D |
// |TAI:000000000000000000000000000000000005 | CLI:000000000000000000000000000000000006 | P5D |
// |TAI:000000000000000000000000000000000006 | CLI:000000000000000000000000000000000007 | P6D |
// |TAI:000000000000000000000000000000000007 | CLI:100000000000000000000000000000000008 | P1D |
// +-----------------------------------------+------------------------------------------+------+
String tkId0 = "TKI:000000000000000000000000000000000000";
String tkId1 = "TKI:000000000000000000000000000000000001";
String tkId2 = "TKI:000000000000000000000000000000000002";
// get due dates by updating the tasks individually
Task task0 = taskService.getTask(tkId0);
Task task1 = taskService.getTask(tkId1);
Task task2 = taskService.getTask(tkId2);
Instant planned = getInstant("2020-04-21T13:00:00");
task0.setPlanned(planned);
task1.setPlanned(planned);
task2.setPlanned(planned);
final Instant due0 = taskService.updateTask(task0).getDue();
final Instant due1 = taskService.updateTask(task1).getDue();
final Instant due2 = taskService.updateTask(task2).getDue();
// now check that bulk update gives the same due dates
List<String> taskIds = List.of(tkId0, tkId1, tkId2);
BulkOperationResults<String, TaskanaException> results =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
Instant dueBulk0 = taskService.getTask(tkId0).getDue();
Instant dueBulk1 = taskService.getTask(tkId1).getDue();
Instant dueBulk2 = taskService.getTask(tkId2).getDue();
assertThat(dueBulk0).isEqualTo(planned.plus(1, ChronoUnit.DAYS));
assertThat(dueBulk1).isEqualTo(planned.plus(2, ChronoUnit.DAYS));
assertThat(dueBulk2).isEqualTo(planned.plus(1, ChronoUnit.DAYS));
assertThat(results.containsErrors()).isFalse();
assertThat(dueBulk0).isEqualTo(due0);
assertThat(dueBulk1).isEqualTo(due1);
assertThat(dueBulk2).isEqualTo(due2);
}
// the following tests use these tasks, attachments and classifications
// +-----------------------------------------+------------------------------------------+------+
// | Task / associated attachment | Classification | SL |
// +-----------------------------------------+------------------------------------------+------+
// |TKI:000000000000000000000000000000000008 | CLI:100000000000000000000000000000000003 | P13D |
// |TAI:000000000000000000000000000000000008 | CLI:000000000000000000000000000000000009 | P8D |
// +---------------------------------------- + -----------------------------------------+ -----+
// |TKI:000000000000000000000000000000000009 | CLI:100000000000000000000000000000000003 | P13D |
// +---------------------------------------- + -----------------------------------------+ -----+
// |TKI:000000000000000000000000000000000010 | CLI:100000000000000000000000000000000003 | P13D |
// |TAI:000000000000000000000000000000000014 | CLI:100000000000000000000000000000000004,| P14D |
// +-----------------------------------------+------------------------------------------+------+
@WithAccessId(user = "user-b-2")
@Test
void should_ReturnBulkLog_When_UserIsNotAuthorizedForTasks() {
String tkId1 = "TKI:000000000000000000000000000000000008";
String tkId2 = "TKI:000000000000000000000000000000000009";
String tkId3 = "TKI:000000000000000000000000000000000008";
String tkId4 = "TKI:000000000000000000000000000000000010";
List<String> taskIds = List.of(tkId1, tkId2, tkId3, tkId4);
Instant planned = getInstant("2020-02-25T07:00:00");
BulkOperationResults<String, TaskanaException> results =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
assertThat(results.containsErrors()).isTrue();
assertThat(results.getFailedIds()).hasSize(3).containsAnyElementsOf(taskIds);
assertThat(results.getErrorMap().values())
.hasOnlyElementsOfType(NotAuthorizedOnWorkbasketException.class);
}
@WithAccessId(user = "admin")
@Test
void should_SetPlannedPropertyOfTasks_When_RequestedByAdminUser() throws Exception {
String tkId1 = "TKI:000000000000000000000000000000000008";
String tkId2 = "TKI:000000000000000000000000000000000009";
String tkId3 = "TKI:000000000000000000000000000000000008";
String tkId4 = "TKI:000000000000000000000000000000000010"; // all three have P13D
List<String> taskIds = List.of(tkId1, tkId2, tkId3, tkId4);
Instant planned = getInstant("2020-05-03T07:00:00");
BulkOperationResults<String, TaskanaException> results =
taskService.setPlannedPropertyOfTasks(planned, taskIds);
assertThat(results.containsErrors()).isFalse();
Instant dueBulk1 = taskService.getTask(tkId1).getDue();
Instant dueBulk2 = taskService.getTask(tkId2).getDue();
Instant dueBulk3 = taskService.getTask(tkId3).getDue();
Instant dueBulk4 = taskService.getTask(tkId4).getDue();
assertThat(dueBulk1).isEqualTo(getInstant("2020-05-14T07:00:00"));
assertThat(dueBulk2).isEqualTo(getInstant("2020-05-22T07:00:00"));
assertThat(dueBulk3).isEqualTo(getInstant("2020-05-14T07:00:00"));
assertThat(dueBulk4).isEqualTo(getInstant("2020-05-22T07:00:00"));
}
@WithAccessId(user = "admin")
@Test
void should_DoNothing_When_SetPlannedIsCalledWithEmptyTasksList() {
Instant planned = getInstant("2020-05-03T07:00:00");
BulkOperationResults<String, TaskanaException> results =
taskService.setPlannedPropertyOfTasks(planned, new ArrayList<>());
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 |
// |TKI:000000000000000000000000000000000066 | CLI:100000000000000000000000000000000024 | P0D |
// +-----------------------------------------+------------------------------------------+------+
@WithAccessId(user = "admin")
@Test
void should_SetPlannedPropertyWithBulkUpdate_When_RequestContainsASingleTask() throws Exception {
String taskId = "TKI:000000000000000000000000000000000002";
Instant planned = getInstant("2020-05-03T07:00:00");
// test bulk operation setPlanned...
BulkOperationResults<String, TaskanaException> results =
taskService.setPlannedPropertyOfTasks(planned, List.of(taskId));
Task task = taskService.getTask(taskId);
assertThat(results.containsErrors()).isFalse();
Instant expectedDue =
workingTimeCalculator.addWorkingTime(task.getPlanned(), Duration.ofDays(1));
assertThat(task.getDue()).isEqualTo(expectedDue);
}
@WithAccessId(user = "admin")
@Test
void should_SetPlannedPropertyOnSingle_When_UpdateTaskWasCalled() throws Exception {
String taskId = "TKI:000000000000000000000000000000000002";
Task task = taskService.getTask(taskId);
// test update of planned date via updateTask()
task.setPlanned(task.getPlanned().plus(Duration.ofDays(3)));
task = taskService.updateTask(task);
Instant expectedDue =
workingTimeCalculator.addWorkingTime(task.getPlanned(), Duration.ofDays(1));
assertThat(task.getDue()).isEqualTo(expectedDue);
}
@WithAccessId(user = "admin")
@Test
void should_SetPlanned_When_OnlyDueWasChanged() throws Exception {
String taskId = "TKI:000000000000000000000000000000000002"; // P1D
Instant planned = getInstant("2020-05-03T07:00:00");
Task task = taskService.getTask(taskId);
// test update of due with unchanged planned
task.setDue(planned.plus(Duration.ofDays(8))); // Monday
task = taskService.updateTask(task);
assertThat(task.getPlanned()).isEqualTo(getInstant("2020-05-08T07:00:00")); // Friday
}
@WithAccessId(user = "admin")
@Test
void should_SetDue_When_OnlyPlannedWasChanged() throws Exception {
String taskId = "TKI:000000000000000000000000000000000002"; // P1D ServiceLevel
Task task = taskService.getTask(taskId);
Instant planned = getInstant("2020-05-10T07:00:00"); // Sunday
task.setPlanned(planned);
task = taskService.updateTask(task);
assertThat(task.getPlanned()).isEqualTo(getInstant("2020-05-11T07:00:00")); // Monday
assertThat(task.getDue()).isEqualTo(getInstant("2020-05-12T07:00:00")); // Tuesday
}
@WithAccessId(user = "admin")
@Test
void should_SetPlanned_When_DueIsChangedAndPlannedIsNulled() throws Exception {
String taskId = "TKI:000000000000000000000000000000000002";
Instant due = getInstant("2020-05-06T07:00:00");
Task task = taskService.getTask(taskId);
task.setDue(due);
task.setPlanned(null);
task = taskService.updateTask(task);
String serviceLevel = task.getClassificationSummary().getServiceLevel();
Instant expPlanned =
workingTimeCalculator.subtractWorkingTime(task.getDue(), Duration.parse(serviceLevel));
assertThat(task.getPlanned()).isEqualTo(expPlanned);
assertThat(task.getDue()).isEqualTo(due);
}
@WithAccessId(user = "admin")
@Test
void should_SetDue_When_TaskUpdateIsCalled() throws Exception {
String taskId = "TKI:000000000000000000000000000000000002";
final Instant planned = getInstant("2020-05-03T07:00:00"); // Sunday
Task task = taskService.getTask(taskId);
task.setPlanned(null);
task = taskService.updateTask(task);
Instant expectedDue =
workingTimeCalculator.addWorkingTime(task.getPlanned(), Duration.ofDays(1));
assertThat(task.getDue()).isEqualTo(expectedDue);
task.setDue(null);
task = taskService.updateTask(task);
expectedDue = workingTimeCalculator.addWorkingTime(task.getPlanned(), Duration.ofDays(1));
assertThat(task.getDue()).isEqualTo(expectedDue);
task.setPlanned(planned.plus(Duration.ofDays(13))); // Saturday
task.setDue(null);
task = taskService.updateTask(task);
expectedDue = workingTimeCalculator.addWorkingTime(task.getPlanned(), Duration.ofDays(1));
assertThat(task.getDue()).isEqualTo(expectedDue);
task.setDue(planned.plus(Duration.ofDays(13))); // Saturday
task.setPlanned(null);
task = taskService.updateTask(task);
assertThat(task.getPlanned()).isEqualTo(getInstant("2020-05-14T07:00:00"));
assertThat(task.getDue()).isEqualTo(getInstant("2020-05-15T07:00:00"));
}
@WithAccessId(user = "user-1-2")
@Test
void should_UpdateTaskPlannedOrDue_When_PlannedOrDueAreWeekendDays() throws Exception {
Task task = taskService.getTask("TKI:000000000000000000000000000000000030"); // SL=P13D
task.setPlanned(getInstant("2020-03-21T07:00:00")); // planned = saturday
task = taskService.updateTask(task);
assertThat(task.getDue()).isEqualTo(getInstant("2020-04-09T07:00:00"));
task.setDue(getInstant("2020-04-11T07:00:00")); // due = saturday
task.setPlanned(null);
task = taskService.updateTask(task);
assertThat(task.getPlanned()).isEqualTo(getInstant("2020-03-23T07:00:00"));
task.setDue(getInstant("2020-04-12T07:00:00")); // due = sunday
task = taskService.updateTask(task);
assertThat(task.getPlanned()).isEqualTo(getInstant("2020-03-23T07:00:00"));
task.setPlanned(getInstant("2020-03-21T07:00:00")); // planned = saturday
task.setDue(getInstant("2020-04-09T07:00:00")); // thursday
task = taskService.updateTask(task);
assertThat(task.getPlanned()).isEqualTo(getInstant("2020-03-23T07:00:00"));
task.setPlanned(getInstant("2020-03-03T07:00:00")); // planned on tuesday
task.setDue(getInstant("2020-03-22T07:00:00")); // due = sunday
task = taskService.updateTask(task);
assertThat(task.getDue()).isEqualTo(getInstant("2020-03-20T07:00:00")); // friday
task.setPlanned(getInstant("2024-03-29T07:00:00")); // Good Friday
task.setDue(null);
task = taskService.updateTask(task);
assertThat(task.getPlanned()).isEqualTo(getInstant("2024-04-02T07:00:00")); // Tuesday
assertThat(task.getDue()).isEqualTo(getInstant("2024-04-19T07:00:00")); // Friday
}
@WithAccessId(user = "user-1-1")
@Test
void should_UpdateTaskPlannedAndDueDate_When_PlannedDateIsNotWorkingDay() throws Exception {
Task task = taskService.getTask("TKI:000000000000000000000000000000000201"); // SL=P2D
assertThat(task.getClassificationSummary().getServiceLevel()).isEqualTo("P2D");
task.setPlanned(getInstant("2024-03-29T07:00:00")); // planned = Good Friday
task = taskService.updateTask(task);
assertThat(task.getPlanned()).isEqualTo(getInstant("2024-04-02T07:00:00")); // Tuesday
assertThat(task.getDue()).isEqualTo(getInstant("2024-04-04T07:00:00")); // Thursday
}
@WithAccessId(user = "user-1-1")
@Test
void should_UpdateTaskPlannedOrDue_When_PlannedOrDueAreOnWeekends_ServiceLevel_P0D()
throws Exception {
Task task = taskService.getTask("TKI:000000000000000000000000000000000066"); // P0D
// nothing changed
task = taskService.updateTask(task);
assertThat(task.getDue()).isEqualTo(getInstant("2018-01-29T15:55:00")); // Monday
assertThat(task.getPlanned()).isEqualTo(getInstant("2018-01-29T15:55:00")); // Monday
// planned changed, due did not change
task.setPlanned(getInstant("2020-03-21T07:00:00")); // Saturday
task = taskService.updateTask(task);
assertThat(task.getDue()).isEqualTo(getInstant("2020-03-23T07:00:00")); // Monday
assertThat(task.getPlanned()).isEqualTo(getInstant("2020-03-23T07:00:00")); // Monday
// due changed, planned did not change
task.setDue(getInstant("2020-04-12T07:00:00")); // Sunday
task = taskService.updateTask(task);
assertThat(task.getPlanned())
.isEqualTo(getInstant("2020-04-09T07:00:00")); // Thursday (skip Good Friday)
assertThat(task.getDue()).isEqualTo(getInstant("2020-04-09T07:00:00"));
// due changed, planned is null
task.setDue(getInstant("2020-04-11T07:00:00")); // Saturday
task.setPlanned(null);
task = taskService.updateTask(task);
assertThat(task.getPlanned())
.isEqualTo(getInstant("2020-04-09T07:00:00")); // Thursday (skip Good Friday)
assertThat(task.getDue()).isEqualTo(getInstant("2020-04-09T07:00:00"));
// planned changed, due is null
task.setPlanned(getInstant("2020-03-22T07:00:00")); // Sunday
task.setDue(null);
task = taskService.updateTask(task);
assertThat(task.getDue()).isEqualTo(getInstant("2020-03-23T07:00:00")); // Monday
assertThat(task.getPlanned()).isEqualTo(getInstant("2020-03-23T07:00:00")); // Monday
// both changed, not null (due at weekend)
task.setPlanned(getInstant("2020-03-20T07:00:00")); // Friday
task.setDue(getInstant("2020-03-22T07:00:00")); // Sunday
task = taskService.updateTask(task);
assertThat(task.getPlanned()).isEqualTo(getInstant("2020-03-20T07:00:00")); // Friday
assertThat(task.getDue()).isEqualTo(getInstant("2020-03-20T07:00:00")); // Friday
// both changed, not null (planned at weekend)
task.setPlanned(getInstant("2020-03-22T07:00:00")); // Sunday
task.setDue(getInstant("2020-03-23T07:00:00")); // Monday
task = taskService.updateTask(task);
assertThat(task.getDue()).isEqualTo(getInstant("2020-03-23T07:00:00")); // Monday
assertThat(task.getPlanned()).isEqualTo(getInstant("2020-03-23T07:00:00")); // Monday
// both changed, not null (both at weekend) within SLA
task.setPlanned(getInstant("2020-03-22T07:00:00")); // Sunday
task.setDue(getInstant("2020-03-22T07:00:00")); // Sunday
task = taskService.updateTask(task);
assertThat(task.getDue()).isEqualTo(getInstant("2020-03-20T07:00:00")); // Friday
assertThat(task.getPlanned()).isEqualTo(getInstant("2020-03-20T07:00:00")); // Friday
// both changed, not null (planned > due)
task.setPlanned(getInstant("2020-03-24T07:00:00")); // Tuesday
task.setDue(getInstant("2020-03-23T07:00:00")); // Monday
task = taskService.updateTask(task);
assertThat(task.getDue()).isEqualTo(getInstant("2020-03-23T07:00:00")); // Monday
assertThat(task.getPlanned()).isEqualTo(getInstant("2020-03-23T07:00:00")); // Monday
}
@WithAccessId(user = "user-1-1")
@Test
void should_NotThrowServiceLevelViolation_IfWeekendOrHolidaysBetweenDates() throws Exception {
Task task = taskService.getTask("TKI:000000000000000000000000000000000002"); // P1D
// SLA is broken but only with holidays in between
task.setDue(getInstant("2020-04-14T07:00:00")); // Tuesday after Easter
task.setPlanned(getInstant("2020-04-09T07:00:00")); // Thursday before Easter
task = taskService.updateTask(task);
assertThat(task.getDue()).isEqualTo(getInstant("2020-04-14T07:00:00")); // Tuesday
assertThat(task.getPlanned()).isEqualTo(getInstant("2020-04-09T07:00:00")); // Thursday
}
}

View File

@ -346,7 +346,7 @@ class QueryTasksAccTest extends AbstractAccTest {
List<Triplet<TaskCustomField, String[], Integer>> list =
List.of(
Triplet.of(
TaskCustomField.CUSTOM_1, new String[] {"custom%", "p%", "%xyz%", "efg"}, 3),
TaskCustomField.CUSTOM_1, new String[] {"custom%", "p%", "%xyz%", "efg"}, 4),
Triplet.of(TaskCustomField.CUSTOM_2, new String[] {"custom%", "a%"}, 2),
Triplet.of(TaskCustomField.CUSTOM_3, new String[] {"ffg"}, 1),
Triplet.of(TaskCustomField.CUSTOM_4, new String[] {"%ust%", "%ty"}, 2),
@ -359,7 +359,7 @@ class QueryTasksAccTest extends AbstractAccTest {
Triplet.of(TaskCustomField.CUSTOM_11, new String[] {"%ert"}, 3),
Triplet.of(TaskCustomField.CUSTOM_12, new String[] {"dd%"}, 1),
Triplet.of(TaskCustomField.CUSTOM_13, new String[] {"%dd_"}, 1),
Triplet.of(TaskCustomField.CUSTOM_14, new String[] {"%"}, 99),
Triplet.of(TaskCustomField.CUSTOM_14, new String[] {"%"}, 100),
Triplet.of(TaskCustomField.CUSTOM_15, new String[] {"___"}, 4),
Triplet.of(TaskCustomField.CUSTOM_16, new String[] {"___"}, 4));
assertThat(list).hasSameSizeAs(TaskCustomField.values());
@ -391,22 +391,22 @@ class QueryTasksAccTest extends AbstractAccTest {
// carefully constructed to always return exactly 2 results
List<Triplet<TaskCustomField, String[], Integer>> list =
List.of(
Triplet.of(TaskCustomField.CUSTOM_1, new String[] {"custom1"}, 98),
Triplet.of(TaskCustomField.CUSTOM_1, new String[] {"custom1"}, 99),
Triplet.of(TaskCustomField.CUSTOM_2, new String[] {""}, 2),
Triplet.of(TaskCustomField.CUSTOM_3, new String[] {"custom3"}, 98),
Triplet.of(TaskCustomField.CUSTOM_3, new String[] {"custom3"}, 99),
Triplet.of(TaskCustomField.CUSTOM_4, new String[] {""}, 2),
Triplet.of(TaskCustomField.CUSTOM_5, new String[] {"ew", "al", "el"}, 92),
Triplet.of(TaskCustomField.CUSTOM_6, new String[] {"11", "vvg"}, 95),
Triplet.of(TaskCustomField.CUSTOM_7, new String[] {"custom7", "ijk"}, 97),
Triplet.of(TaskCustomField.CUSTOM_8, new String[] {"not_existing"}, 99),
Triplet.of(TaskCustomField.CUSTOM_9, new String[] {"custom9"}, 98),
Triplet.of(TaskCustomField.CUSTOM_10, new String[] {"custom10"}, 98),
Triplet.of(TaskCustomField.CUSTOM_11, new String[] {"custom11"}, 98),
Triplet.of(TaskCustomField.CUSTOM_12, new String[] {"custom12"}, 98),
Triplet.of(TaskCustomField.CUSTOM_13, new String[] {"custom13"}, 98),
Triplet.of(TaskCustomField.CUSTOM_5, new String[] {"ew", "al", "el"}, 93),
Triplet.of(TaskCustomField.CUSTOM_6, new String[] {"11", "vvg"}, 96),
Triplet.of(TaskCustomField.CUSTOM_7, new String[] {"custom7", "ijk"}, 98),
Triplet.of(TaskCustomField.CUSTOM_8, new String[] {"not_existing"}, 100),
Triplet.of(TaskCustomField.CUSTOM_9, new String[] {"custom9"}, 99),
Triplet.of(TaskCustomField.CUSTOM_10, new String[] {"custom10"}, 99),
Triplet.of(TaskCustomField.CUSTOM_11, new String[] {"custom11"}, 99),
Triplet.of(TaskCustomField.CUSTOM_12, new String[] {"custom12"}, 99),
Triplet.of(TaskCustomField.CUSTOM_13, new String[] {"custom13"}, 99),
Triplet.of(TaskCustomField.CUSTOM_14, new String[] {"abc"}, 0),
Triplet.of(TaskCustomField.CUSTOM_15, new String[] {"custom15"}, 98),
Triplet.of(TaskCustomField.CUSTOM_16, new String[] {"custom16"}, 98));
Triplet.of(TaskCustomField.CUSTOM_15, new String[] {"custom15"}, 99),
Triplet.of(TaskCustomField.CUSTOM_16, new String[] {"custom16"}, 99));
assertThat(list).hasSameSizeAs(TaskCustomField.values());
return DynamicTest.stream(
@ -455,7 +455,7 @@ class QueryTasksAccTest extends AbstractAccTest {
throws InvalidArgumentException {
List<TaskSummary> results =
taskService.createTaskQuery().customAttributeIn(TaskCustomField.CUSTOM_9, "").list();
assertThat(results).hasSize(97);
assertThat(results).hasSize(98);
}
@WithAccessId(user = "admin")
@ -467,7 +467,7 @@ class QueryTasksAccTest extends AbstractAccTest {
.createTaskQuery()
.customAttributeIn(TaskCustomField.CUSTOM_9, "", null)
.list();
assertThat(results).hasSize(98);
assertThat(results).hasSize(99);
}
@WithAccessId(user = "admin")
@ -479,14 +479,14 @@ class QueryTasksAccTest extends AbstractAccTest {
.createTaskQuery()
.customAttributeNotIn(TaskCustomField.CUSTOM_9, new String[] {null})
.list();
assertThat(results).hasSize(98);
assertThat(results).hasSize(99);
results =
taskService
.createTaskQuery()
.customAttributeNotIn(TaskCustomField.CUSTOM_9, null, "custom9")
.list();
assertThat(results).hasSize(97);
assertThat(results).hasSize(98);
results =
taskService
@ -494,7 +494,7 @@ class QueryTasksAccTest extends AbstractAccTest {
.customAttributeNotIn(TaskCustomField.CUSTOM_9, new String[] {null})
.customAttributeNotIn(TaskCustomField.CUSTOM_10, "custom10")
.list();
assertThat(results).hasSize(97);
assertThat(results).hasSize(98);
}
@WithAccessId(user = "admin")

View File

@ -47,7 +47,7 @@ class QueryTasksByRoleAccTest extends AbstractAccTest {
switch (taskanaEngine.getCurrentUserContext().getUserid()) {
case "admin":
case "taskadmin":
expectedSize = 99;
expectedSize = 100;
break;
case "businessadmin":
case "monitor":
@ -57,7 +57,7 @@ class QueryTasksByRoleAccTest extends AbstractAccTest {
expectedSize = 26;
break;
case "user-1-1":
expectedSize = 9;
expectedSize = 10;
break;
case "user-taskrouter":
expectedSize = 0;

View File

@ -92,7 +92,7 @@ class QueryTasksByTimeIntervalsAccTest extends AbstractAccTest {
List<TaskSummary> results =
taskService.createTaskQuery().createdWithin(interval1).orderByCreated(asc).list();
assertThat(results).hasSize(61);
assertThat(results).hasSize(62);
TaskSummary previousSummary = null;
for (TaskSummary taskSummary : results) {
Instant cr = taskSummary.getCreated();

View File

@ -62,7 +62,7 @@ class QueryTasksListValuesAccTest extends AbstractAccTest {
.ownerLike("%user%")
.orderByClassificationName(ASCENDING)
.listValues(TaskQueryColumnName.CLASSIFICATION_NAME, null);
assertThat(columnValueList).hasSize(5);
assertThat(columnValueList).hasSize(6);
}
@WithAccessId(user = "admin")

View File

@ -30,9 +30,9 @@ class QueryTasksWithPaginationAccTest extends AbstractAccTest {
void testQueryAllPaged() {
TaskQuery taskQuery = taskanaEngine.getTaskService().createTaskQuery();
long numberOfTasks = taskQuery.count();
assertThat(numberOfTasks).isEqualTo(99);
assertThat(numberOfTasks).isEqualTo(100);
List<TaskSummary> tasks = taskQuery.orderByDue(DESCENDING).list();
assertThat(tasks).hasSize(99);
assertThat(tasks).hasSize(100);
List<TaskSummary> tasksp = taskQuery.orderByDue(DESCENDING).listPage(4, 5);
assertThat(tasksp).hasSize(5);
tasksp = taskQuery.orderByDue(DESCENDING).listPage(5, 5);

View File

@ -0,0 +1,655 @@
package acceptance.task.update;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import acceptance.AbstractAccTest;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import org.assertj.core.api.Condition;
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import pro.taskana.TaskanaConfiguration;
import pro.taskana.TaskanaConfiguration.Builder;
import pro.taskana.classification.api.ClassificationService;
import pro.taskana.classification.api.exceptions.ClassificationNotFoundException;
import pro.taskana.classification.api.models.Classification;
import pro.taskana.classification.api.models.ClassificationSummary;
import pro.taskana.classification.internal.models.ClassificationSummaryImpl;
import pro.taskana.common.api.exceptions.InvalidArgumentException;
import pro.taskana.common.test.security.JaasExtension;
import pro.taskana.common.test.security.WithAccessId;
import pro.taskana.task.api.TaskService;
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.Task;
import pro.taskana.task.api.models.TaskSummary;
import pro.taskana.task.internal.models.AttachmentImpl;
import pro.taskana.task.internal.models.TaskImpl;
@ExtendWith(JaasExtension.class)
public class UpdateTaskAttachmentWithWorkingDaysCalculationAccTest extends AbstractAccTest {
private Task task;
private Attachment attachment;
private ClassificationService classificationService;
@BeforeEach
@WithAccessId(user = "admin")
void setUp() throws Exception {
resetDb(false);
TaskanaConfiguration config =
new Builder(taskanaConfiguration).useWorkingTimeCalculation(false).build();
initTaskanaEngine(config);
classificationService = taskanaEngine.getClassificationService();
task =
taskService.getTask(
"TKI:000000000000000000000000000000000000"); // class T2000, prio 1, SL P1D
task.setClassificationKey("T2000");
attachment =
createExampleAttachment(
"DOCTYPE_DEFAULT", // prio 99, SL P2000D
createObjectReference(
"COMPANY_A",
"SYSTEM_B",
"INSTANCE_B",
"ArchiveId",
"12345678901234567890123456789012345678901234567890"),
"E-MAIL",
Instant.parse("2018-01-15T00:00:00Z"),
createSimpleCustomPropertyMap(3));
task.getAttachments().clear();
taskService.updateTask(task);
assertThat(task).isNotNull();
}
@WithAccessId(user = "user-1-1")
@Test
void should_UpdateTaskCorrectlyInDatabase_When_AddingAnAttachment() throws Exception {
final int attachmentCount = task.getAttachments().size();
assertThat(task.getPriority()).isEqualTo(1);
assertThat(task.getPlanned().plus(Duration.ofDays(1))).isEqualTo(task.getDue());
task.addAttachment(attachment);
task = taskService.updateTask(task);
task = taskService.getTask(task.getId());
assertThat(task.getAttachments())
.hasSize(attachmentCount + 1)
.contains(attachment)
.extracting(Attachment::getModified)
.containsExactlyInAnyOrder(task.getModified());
}
@WithAccessId(user = "user-1-1")
@Test
void should_UpdateTaskReceived_When_AddingAnAttachment() throws Exception {
task.addAttachment(attachment);
task = taskService.updateTask(task);
task = taskService.getTask(task.getId());
assertThat(task)
.extracting(TaskSummary::getReceived)
.isEqualTo(Instant.parse("2018-01-15T00:00:00Z"));
}
@WithAccessId(user = "user-1-1")
@Test
void should_NotUpdateTaskReceived_When_TaskAlreadyHasAReceived() throws Exception {
task.setReceived(Instant.parse("2019-09-13T08:44:17.588Z"));
task.addAttachment(attachment);
task = taskService.updateTask(task);
task = taskService.getTask(task.getId());
assertThat(task).extracting(TaskSummary::getReceived).isNotEqualTo(attachment.getReceived());
}
@WithAccessId(user = "user-1-1")
@Test
void should_UpdateTaskReceived_When_AddingTwoAttachments() throws Exception {
task.addAttachment(attachment);
Attachment attachment2 =
createExampleAttachment(
"L10303",
createObjectReference(
"COMPANY_B",
"SYSTEM_C",
"INSTANCE_C",
"ArchiveId",
"ABC45678901234567890123456789012345678901234567890"),
"ROHRPOST",
Instant.parse("2018-01-12T00:00:00Z"),
createSimpleCustomPropertyMap(4));
task.addAttachment(attachment2);
task = taskService.updateTask(task);
task = taskService.getTask(task.getId());
assertThat(task)
.extracting(TaskSummary::getReceived)
.isEqualTo(Instant.parse("2018-01-12T00:00:00Z"));
}
@WithAccessId(user = "user-1-1")
@Test
void should_NotAddSameAttachmentAgain_When_AddingToTaskSummary() throws Exception {
task.getAttachments().clear();
task = taskService.updateTask(task);
task = taskService.getTask(task.getId());
assertThat(task.getAttachments()).isEmpty();
AttachmentImpl attachment = (AttachmentImpl) this.attachment;
attachment.setId("TAI:000017");
task.addAttachment(attachment);
task.addAttachment(attachment);
task = taskService.updateTask(task);
assertThat(task.getAttachments())
.hasSize(1)
.extracting(AttachmentSummary::getModified)
.containsExactlyInAnyOrder(task.getModified());
}
@WithAccessId(user = "user-1-1")
@Test
void should_ThrowAttachmentPersistenceException_When_UpdatingTaskWithTwoIdenticalAttachments()
throws Exception {
final int attachmentCount = 0;
task.getAttachments().clear();
task = taskService.updateTask(task);
task = taskService.getTask(task.getId());
assertThat(task.getAttachments()).hasSize(attachmentCount);
AttachmentImpl attachment = (AttachmentImpl) this.attachment;
attachment.setId("TAI:000017");
task.getAttachments().add(attachment);
task.getAttachments().add(attachment);
ThrowingCallable call = () -> taskService.updateTask(task);
assertThatThrownBy(call).isInstanceOf(AttachmentPersistenceException.class);
}
@WithAccessId(user = "user-1-1")
@Test
void should_UpdateExistingAttachment_When_AddingSameButNotEqualAttachmentAgain()
throws Exception {
// Add attachment before
task = taskService.getTask(task.getId());
final int attachmentCount = task.getAttachments().size();
task.addAttachment(attachment);
task = taskService.updateTask(task);
task = taskService.getTask(task.getId());
assertThat(task.getAttachments()).hasSize(attachmentCount + 1);
// Change sth. and add same (id) again - override/update
String newChannel = "UPDATED EXTERNAL SINCE LAST ADD";
final int attachmentCount2 = task.getAttachments().size();
Attachment updatedAttachment = task.getAttachments().get(0);
updatedAttachment.setChannel(newChannel);
Classification newClassification =
taskanaEngine
.getClassificationService()
.getClassification("CLI:100000000000000000000000000000000013"); // Prio 99, P2000D
updatedAttachment.setClassificationSummary(newClassification.asSummary());
task.addAttachment(updatedAttachment);
task = taskService.updateTask(task);
task = taskService.getTask(task.getId());
assertThat(task.getAttachments()).hasSize(attachmentCount2);
assertThat(task.getAttachments().get(0).getChannel()).isEqualTo(newChannel);
assertThat(task.getPriority()).isEqualTo(99);
Instant expDue = workingTimeCalculator.addWorkingTime(task.getPlanned(), Duration.ofDays(1));
assertThat(task.getDue()).isEqualTo(expDue);
}
@WithAccessId(user = "user-1-1")
@Test
void should_NotUpdateExistingAttachment_When_AddingIdenticalAttachmentAgain() throws Exception {
// Add Attachment before
final int attachmentCount = task.getAttachments().size();
((AttachmentImpl) attachment).setId("TAI:0001");
task.addAttachment(attachment);
task.addAttachment(attachment); // overwrite, same id
task.addAttachment(attachment); // overwrite, same id
task = taskService.updateTask(task);
task = taskService.getTask(task.getId());
assertThat(task.getAttachments()).hasSize(attachmentCount + 1);
// Add same again - ignored
final int attachmentCount2 = task.getAttachments().size();
Attachment redundantAttachment = task.getAttachments().get(0);
task.addAttachment(redundantAttachment);
task = taskService.updateTask(task);
assertThat(task.getAttachments()).hasSize(attachmentCount2);
}
@WithAccessId(user = "user-1-1")
@Test
void testAddAttachmentAsNullValueWillBeIgnored() throws Exception {
// Try to add a single NULL-Element
final int attachmentCount = task.getAttachments().size();
task.addAttachment(null);
task = taskService.updateTask(task);
task = taskService.getTask(task.getId());
assertThat(task.getAttachments()).hasSize(attachmentCount);
// Try to set the Attachments to NULL and update it
((TaskImpl) task).setAttachments(null);
task = taskService.updateTask(task);
assertThat(task.getAttachments()).hasSize(attachmentCount); // locally, not inserted
task = taskService.getTask(task.getId());
assertThat(task.getAttachments()).hasSize(attachmentCount); // inserted values not changed
// Test no NullPointer on NULL-Value and removing it on current data.
// New loading can do this, but returned value should got this "function", too.
final int attachmentCount2 = task.getAttachments().size();
task.getAttachments().add(null);
task.getAttachments().add(null);
task.getAttachments().add(null);
task = taskService.updateTask(task);
assertThat(task.getAttachments()).hasSize(attachmentCount2); // locally, not inserted
task = taskService.getTask(task.getId());
assertThat(task.getAttachments()).hasSize(attachmentCount2); // inserted values not changed
assertThat(task.getPriority()).isEqualTo(1);
assertThat(task.getPlanned().plus(Duration.ofDays(1))).isEqualTo(task.getDue());
}
@WithAccessId(user = "user-1-1")
@Test
void testRemoveAttachment() throws Exception {
task.addAttachment(attachment);
task = taskService.updateTask(task);
assertThat(task.getPriority()).isEqualTo(99);
assertThat(task.getPlanned().plus(Duration.ofDays(1))).isEqualTo(task.getDue());
int attachmentCount = task.getAttachments().size();
Attachment attachmentToRemove = task.getAttachments().get(0);
task.removeAttachment(attachmentToRemove.getId());
task = taskService.updateTask(task);
assertThat(task.getAttachments())
.hasSize(attachmentCount - 1); // locally, removed and not inserted
task = taskService.getTask(task.getId());
assertThat(task.getAttachments()).hasSize(attachmentCount - 1); // inserted, values removed
assertThat(task.getPriority()).isEqualTo(1);
assertThat(task.getPlanned().plus(Duration.ofDays(1))).isEqualTo(task.getDue());
}
@WithAccessId(user = "user-1-1")
@Test
void testRemoveAttachmentWithNullAndNotAddedId() throws Exception {
task.addAttachment(attachment);
task = taskService.updateTask(task);
int attachmentCount = task.getAttachments().size();
task.removeAttachment(null);
task = taskService.updateTask(task);
assertThat(task.getAttachments()).hasSize(attachmentCount); // locally, nothing changed
task = taskService.getTask(task.getId());
assertThat(task.getAttachments()).hasSize(attachmentCount); // inserted, still same
task.removeAttachment("INVALID ID HERE");
task = taskService.updateTask(task);
assertThat(task.getAttachments()).hasSize(attachmentCount); // locally, nothing changed
task = taskService.getTask(task.getId());
assertThat(task.getAttachments()).hasSize(attachmentCount); // inserted, still same
}
@WithAccessId(user = "user-1-1")
@Test
void testUpdateAttachment() throws Exception {
((TaskImpl) task).setAttachments(new ArrayList<>());
task = taskService.updateTask(task);
assertThat(task.getPriority()).isEqualTo(1);
assertThat(task.getPlanned().plus(Duration.ofDays(1))).isEqualTo(task.getDue());
Attachment attachment = this.attachment;
task.addAttachment(attachment);
task = taskService.updateTask(task);
assertThat(task.getPriority()).isEqualTo(99);
assertThat(task.getPlanned().plus(Duration.ofDays(1))).isEqualTo(task.getDue());
final int attachmentCount = task.getAttachments().size();
String newChannel = attachment.getChannel() + "-X";
task.getAttachments().get(0).setChannel(newChannel);
Classification newClassification =
taskanaEngine
.getClassificationService()
.getClassification("CLI:100000000000000000000000000000000013"); // Prio 99, P2000D
task.getAttachments().get(0).setClassificationSummary(newClassification.asSummary());
task = taskService.updateTask(task);
task = taskService.getTask(task.getId());
assertThat(task.getAttachments()).hasSize(attachmentCount);
assertThat(task.getAttachments().get(0).getChannel()).isEqualTo(newChannel);
assertThat(task.getPriority()).isEqualTo(99);
Instant expDue = workingTimeCalculator.addWorkingTime(task.getPlanned(), Duration.ofDays(1));
assertThat(task.getDue()).isEqualTo(expDue);
}
@WithAccessId(user = "user-1-1")
@Test
void modifyExistingAttachment() throws Exception {
// setup test
assertThat(task.getAttachments()).isEmpty();
task.addAttachment(attachment);
Attachment attachment2 =
createExampleAttachment(
"L10303", // prio 101, SL PT7H
createObjectReference(
"COMPANY_B",
"SYSTEM_C",
"INSTANCE_C",
"ArchiveId",
"ABC45678901234567890123456789012345678901234567890"),
"ROHRPOST",
Instant.parse("2018-01-15T00:00:00Z"),
createSimpleCustomPropertyMap(4));
task.addAttachment(attachment2);
task = taskService.updateTask(task);
task = taskService.getTask(task.getId());
assertThat(task.getPriority()).isEqualTo(101);
Instant expDue = workingTimeCalculator.addWorkingTime(task.getPlanned(), Duration.ofDays(1));
assertThat(task.getDue()).isEqualTo(expDue);
assertThat(task.getAttachments())
.hasSize(2)
.areExactly(
1,
new Condition<>(
e -> "E-MAIL".equals(e.getChannel()) && e.getCustomAttributeMap().size() == 3,
"E-MAIL with 3 custom attributes"))
.areExactly(
1,
new Condition<>(
e -> "ROHRPOST".equals(e.getChannel()) && e.getCustomAttributeMap().size() == 4,
"ROHRPOST with 4 custom attributes"));
ClassificationSummary newClassificationSummary =
taskanaEngine
.getClassificationService()
.getClassification("CLI:100000000000000000000000000000000006") // Prio 5, SL P16D
.asSummary();
// modify existing attachment
for (Attachment att : task.getAttachments()) {
att.setClassificationSummary(newClassificationSummary);
if (att.getCustomAttributeMap().size() == 3) {
att.setChannel("FAX");
}
}
// modify existing attachment and task classification
task.setClassificationKey("DOCTYPE_DEFAULT"); // Prio 99, SL P2000D
task = taskService.updateTask(task);
task = taskService.getTask(task.getId());
assertThat(task.getPriority()).isEqualTo(99);
expDue = workingTimeCalculator.addWorkingTime(task.getPlanned(), Duration.ofDays(16));
assertThat(task.getDue()).isEqualTo(expDue);
assertThat(task.getAttachments())
.hasSize(2)
.areExactly(
1,
new Condition<>(
e -> "FAX".equals(e.getChannel()) && e.getCustomAttributeMap().size() == 3,
"FAX with 3 custom attributes"))
.areExactly(
1,
new Condition<>(
e -> "ROHRPOST".equals(e.getChannel()) && e.getCustomAttributeMap().size() == 4,
"ROHRPOST with 4 custom attributes"));
}
@WithAccessId(user = "user-1-1")
@Test
void replaceExistingAttachments() throws Exception {
// setup test
assertThat(task.getAttachments()).isEmpty();
task.addAttachment(attachment);
Attachment attachment2 =
createExampleAttachment(
"DOCTYPE_DEFAULT",
createObjectReference(
"COMPANY_B",
"SYSTEM_C",
"INSTANCE_C",
"ArchiveId",
"ABC45678901234567890123456789012345678901234567890"),
"E-MAIL",
Instant.parse("2018-01-15T00:00:00Z"),
createSimpleCustomPropertyMap(4));
task.addAttachment(attachment2);
task = taskService.updateTask(task);
task = taskService.getTask(task.getId());
assertThat(task.getAttachments()).hasSize(2);
assertThat(task.getAttachments().get(0).getClassificationSummary().getKey())
.isEqualTo("DOCTYPE_DEFAULT");
Attachment attachment3 =
createExampleAttachment(
"DOCTYPE_DEFAULT",
createObjectReference(
"COMPANY_C",
"SYSTEM_7",
"INSTANCE_7",
"ArchiveId",
"ABC4567890123456789012345678901234567890DEF"),
"DHL",
Instant.parse("2018-01-15T00:00:00Z"),
createSimpleCustomPropertyMap(4));
// replace existing attachments by new via addAttachment call
task.getAttachments().clear();
task.addAttachment(attachment3);
task = taskService.updateTask(task);
assertThat(task.getAttachments()).hasSize(1);
assertThat(task.getAttachments().get(0).getChannel()).isEqualTo("DHL");
task.getAttachments().forEach(at -> assertThat(task.getModified()).isEqualTo(at.getModified()));
// setup environment for 2nd version of replacement (list.add call)
task.getAttachments().add(attachment2);
task = taskService.updateTask(task);
assertThat(task.getAttachments()).hasSize(2);
assertThat(task.getAttachments().get(1).getChannel()).isEqualTo("E-MAIL");
// replace attachments
task.getAttachments().clear();
task.getAttachments().add(attachment3);
task = taskService.updateTask(task);
assertThat(task.getAttachments()).hasSize(1);
assertThat(task.getAttachments().get(0).getChannel()).isEqualTo("DHL");
}
@WithAccessId(user = "user-1-1")
@Test
void testPrioDurationOfTaskFromAttachmentsAtUpdate() throws Exception {
TaskService taskService = taskanaEngine.getTaskService();
Task newTask = taskService.newTask("USER-1-1", "DOMAIN_A");
newTask.setClassificationKey("L12010"); // prio 8, SL P7D
newTask.setPrimaryObjRef(
createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"));
newTask.addAttachment(
createExampleAttachment(
"DOCTYPE_DEFAULT", // prio 99, SL P2000D
createObjectReference(
"COMPANY_A",
"SYSTEM_B",
"INSTANCE_B",
"ArchiveId",
"12345678901234567890123456789012345678901234567890"),
"E-MAIL",
Instant.parse("2018-01-15T00:00:00Z"),
createSimpleCustomPropertyMap(3)));
newTask.addAttachment(
createExampleAttachment(
"L1060", // prio 1, SL P1D
createObjectReference(
"COMPANY_A",
"SYSTEM_B",
"INSTANCE_B",
"ArchiveId",
"12345678901234567890123456789012345678901234567890"),
"E-MAIL",
Instant.parse("2018-01-15T00:00:00Z"),
createSimpleCustomPropertyMap(3)));
Task createdTask = taskService.createTask(newTask);
assertThat(createdTask.getId()).isNotNull();
assertThat(createdTask.getCreator())
.isEqualTo(taskanaEngine.getCurrentUserContext().getUserid());
createdTask
.getAttachments()
.forEach(at -> assertThat(createdTask.getModified()).isEqualTo(at.getModified()));
Task readTask = taskService.getTask(createdTask.getId());
assertThat(readTask).isNotNull();
assertThat(createdTask.getCreator())
.isEqualTo(taskanaEngine.getCurrentUserContext().getUserid());
assertThat(readTask.getAttachments()).isNotNull();
assertThat(readTask.getAttachments()).hasSize(2);
assertThat(readTask.getAttachments().get(1).getCreated()).isNotNull();
assertThat(readTask.getAttachments().get(1).getModified()).isNotNull();
assertThat(readTask.getAttachments().get(0).getCreated())
.isEqualTo(readTask.getAttachments().get(1).getModified());
assertThat(readTask.getAttachments().get(0).getObjectReference()).isNotNull();
assertThat(readTask.getPriority()).isEqualTo(99);
Instant expDue =
workingTimeCalculator.addWorkingTime(readTask.getPlanned(), Duration.ofDays(1));
assertThat(readTask.getDue()).isEqualTo(expDue);
}
@WithAccessId(user = "user-1-1")
@Test
void testAddCustomAttributeToAttachment() throws Exception {
TaskService taskService = taskanaEngine.getTaskService();
task =
taskService.getTask(
"TKI:000000000000000000000000000000000000"); // class T2000, prio 1, SL P1D
attachment =
createExampleAttachment(
"DOCTYPE_DEFAULT", // prio 99, SL P2000D
createObjectReference(
"COMPANY_A",
"SYSTEM_B",
"INSTANCE_B",
"ArchiveId",
"12345678901234567890123456789012345678901234567890"),
"E-MAIL",
Instant.parse("2018-01-15T00:00:00Z"),
null);
attachment.getCustomAttributeMap().put("TEST_KEY", "TEST_VALUE");
task.addAttachment(attachment);
taskService.updateTask(task);
Task updatedTask = taskService.getTask("TKI:000000000000000000000000000000000000");
Attachment updatedAttachment =
updatedTask.getAttachments().stream()
.filter(a -> attachment.getId().equals(a.getId()))
.findFirst()
.orElse(null);
assertThat(updatedAttachment).isNotNull();
assertThat(updatedTask.getModified()).isEqualTo(updatedAttachment.getModified());
assertThat(updatedAttachment.getCustomAttributeMap()).containsEntry("TEST_KEY", "TEST_VALUE");
}
@WithAccessId(user = "user-1-1")
@Test
void should_ThrowException_When_UpdatingTaskWithNewAttachmentClassificationNull() {
attachment.setClassificationSummary(null);
task.addAttachment(attachment);
assertThatThrownBy(() -> taskService.updateTask(task))
.isInstanceOf(InvalidArgumentException.class)
.hasMessage("Classification of Attachment must not be null.");
}
@WithAccessId(user = "user-1-1")
@Test
void should_ThrowException_When_UpdatingTaskWithChangedAttachmentClassificationNull()
throws Exception {
task.addAttachment(attachment);
taskService.updateTask(task);
attachment.setClassificationSummary(null);
assertThatThrownBy(() -> taskService.updateTask(task))
.isInstanceOf(InvalidArgumentException.class)
.hasMessage("Classification of Attachment must not be null.");
}
@WithAccessId(user = "user-1-1")
@Test
void should_ThrowException_When_UpdatingTaskWithNewAttachmentObjectReferenceNull()
throws Exception {
task.addAttachment(attachment);
taskService.updateTask(task);
task.removeAttachment(attachment.getId());
attachment.setObjectReference(null);
task.addAttachment(attachment);
assertThatThrownBy(() -> taskService.updateTask(task))
.isInstanceOf(InvalidArgumentException.class)
.hasMessage("ObjectReference of Attachment must not be null.");
}
@WithAccessId(user = "user-1-1")
@Test
void should_ThrowException_When_UpdatingTaskWithChangedAttachmentObjectReferenceNull()
throws Exception {
task.addAttachment(attachment);
taskService.updateTask(task);
task.removeAttachment(attachment.getId());
attachment.setObjectReference(null);
task.addAttachment(attachment);
assertThatThrownBy(() -> taskService.updateTask(task))
.isInstanceOf(InvalidArgumentException.class)
.hasMessage("ObjectReference of Attachment must not be null.");
}
@WithAccessId(user = "user-1-1")
@Test
void should_ThrowException_When_UpdatingTaskWithMissingAttachmentClassificationKey() {
ClassificationSummaryImpl classification = new ClassificationSummaryImpl();
attachment.setClassificationSummary(classification);
task.addAttachment(attachment);
assertThatThrownBy(() -> taskService.updateTask(task))
.isInstanceOf(InvalidArgumentException.class)
.hasMessageContaining("ClassificationKey of Attachment must not be empty.");
}
@WithAccessId(user = "user-1-1")
@Test
void should_ThrowException_When_UpdatingTaskWithNotExistingAttachmentClassification() {
Classification classification =
classificationService.newClassification("NOT_EXISTING", "DOMAIN_A", "");
attachment.setClassificationSummary(classification);
task.addAttachment(attachment);
assertThatThrownBy(() -> taskService.updateTask(task))
.isInstanceOf(ClassificationNotFoundException.class);
}
@WithAccessId(user = "user-1-1")
@Test
void should_FetchAttachmentClassification_When_UpdatingTaskWithAttachments() throws Exception {
ClassificationSummary classification =
classificationService.newClassification("T2000", "DOMAIN_A", "").asSummary();
attachment.setClassificationSummary(classification);
task.addAttachment(attachment);
assertThat(classification.getServiceLevel()).isNull();
TaskImpl updatedTask = (TaskImpl) taskService.updateTask(task);
classification = updatedTask.getAttachments().get(0).getClassificationSummary();
assertThat(classification.getId()).isNotNull();
assertThat(classification.getDomain()).isNotNull();
assertThat(classification.getServiceLevel()).isNotNull();
}
}

View File

@ -185,6 +185,8 @@ class DeleteWorkbasketAccTest extends AbstractAccTest {
taskService.forceCompleteTask(task.getId());
task = (TaskImpl) taskService.getTask("TKI:200000000000000000000000000000000066");
taskService.forceCompleteTask(task.getId());
task = (TaskImpl) taskService.getTask("TKI:000000000000000000000000000000000201");
taskService.forceCompleteTask(task.getId());
boolean canBeDeletedNow = workbasketService.deleteWorkbasket(wb.getId());
assertThat(canBeDeletedNow).isFalse();

View File

@ -13,6 +13,12 @@ taskana.jobs.maxRetries=3
taskana.jobs.batchSize=50
taskana.jobs.runEvery=P1D
taskana.jobs.firstRunAt=2018-07-25T08:00:00Z
taskana.jobs.lockExpirationPeriod=PT1M
taskana.jobs.cleanup.task.lockExpirationPeriod=PT4M
taskana.jobs.cleanup.workbasket.lockExpirationPeriod=PT3M
taskana.jobs.priority.task.lockExpirationPeriod=PT1M
taskana.jobs.refresh.user.lockExpirationPeriod=PT5M
taskana.jobs.cleanup.history.lockExpirationPeriod=PT2M
taskana.jobs.cleanup.task.minimumAge=P14D
taskana.workingTime.holidays.german.enabled=true
taskana.workingTime.holidays.german.corpus-christi.enabled=false
@ -29,12 +35,12 @@ taskana.workingTime.timezone=UTC
# set it to false and no jobs are running
taskana.jobs.scheduler.enabled=true
# wait time before the first job run in millis
taskana.jobs.scheduler.initialStartDelay=100000
taskana.jobs.scheduler.initialStartDelay=0
# sleeping time befor the next job runs
taskana.jobs.scheduler.period=12
# timeunit for the sleeping period
# Possible values: MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS
taskana.jobs.scheduler.periodTimeUnit=HOURS
taskana.jobs.scheduler.periodTimeUnit=SECONDS
taskana.jobs.cleanup.task.enable=false
taskana.jobs.priority.task.enable=false
taskana.jobs.cleanup.workbasket.enable=false

View File

@ -10,7 +10,7 @@
<parent>
<groupId>pro.taskana</groupId>
<artifactId>taskana-lib-parent</artifactId>
<version>6.2.1-SNAPSHOT</version>
<version>6.3.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -10,7 +10,7 @@
<parent>
<groupId>pro.taskana</groupId>
<artifactId>taskana-lib-parent</artifactId>
<version>6.2.1-SNAPSHOT</version>
<version>6.3.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>pro.taskana</groupId>
<artifactId>taskana-lib-parent</artifactId>
<version>6.2.1-SNAPSHOT</version>
<version>6.3.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>pro.taskana</groupId>
<artifactId>taskana-parent</artifactId>
<version>6.2.1-SNAPSHOT</version>
<version>6.3.3-SNAPSHOT</version>
<packaging>pom</packaging>
<name>${project.groupId}:${project.artifactId}</name>

View File

@ -12,7 +12,7 @@
<parent>
<groupId>pro.taskana</groupId>
<artifactId>taskana-parent</artifactId>
<version>6.2.1-SNAPSHOT</version>
<version>6.3.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -11,7 +11,7 @@
<parent>
<groupId>pro.taskana</groupId>
<artifactId>taskana-rest-parent</artifactId>
<version>6.2.1-SNAPSHOT</version>
<version>6.3.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -11,7 +11,7 @@
<parent>
<groupId>pro.taskana</groupId>
<artifactId>taskana-rest-parent</artifactId>
<version>6.2.1-SNAPSHOT</version>
<version>6.3.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -11,7 +11,7 @@
<parent>
<groupId>pro.taskana</groupId>
<artifactId>taskana-rest-parent</artifactId>
<version>6.2.1-SNAPSHOT</version>
<version>6.3.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -22,7 +22,7 @@ import pro.taskana.workbasket.rest.models.WorkbasketSummaryRepresentationModel;
@TaskanaSpringBootTest
public class AbstractAccTest {
protected static final String DEPENDENCY_VERSION = "6.2.1-SNAPSHOT";
protected static final String DEPENDENCY_VERSION = "6.3.3-SNAPSHOT";
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractAccTest.class);

View File

@ -11,7 +11,7 @@
<parent>
<groupId>pro.taskana</groupId>
<artifactId>taskana-rest-parent</artifactId>
<version>6.2.1-SNAPSHOT</version>
<version>6.3.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -10,7 +10,7 @@
<parent>
<groupId>pro.taskana</groupId>
<artifactId>taskana-rest-parent</artifactId>
<version>6.2.1-SNAPSHOT</version>
<version>6.3.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -1,10 +1,12 @@
package pro.taskana.user.jobs;
import java.sql.PreparedStatement;
import java.time.Duration;
import java.util.List;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pro.taskana.TaskanaConfiguration;
import pro.taskana.common.api.ScheduledJob;
import pro.taskana.common.api.TaskanaEngine;
import pro.taskana.common.api.exceptions.InvalidArgumentException;
@ -42,6 +44,10 @@ public class UserInfoRefreshJob extends AbstractTaskanaJob {
refreshUserPostprocessorManager = new RefreshUserPostprocessorManager();
}
public static Duration getLockExpirationPeriod(TaskanaConfiguration taskanaConfiguration) {
return taskanaConfiguration.getUserRefreshJobLockExpirationPeriod();
}
@Override
protected String getType() {
return UserInfoRefreshJob.class.getName();

View File

@ -12,7 +12,7 @@
<parent>
<groupId>pro.taskana</groupId>
<artifactId>taskana-parent</artifactId>
<version>6.2.1-SNAPSHOT</version>
<version>6.3.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -11,7 +11,7 @@
<parent>
<groupId>pro.taskana</groupId>
<artifactId>taskana-routing-parent</artifactId>
<version>6.2.1-SNAPSHOT</version>
<version>6.3.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -11,7 +11,7 @@
<parent>
<groupId>pro.taskana</groupId>
<artifactId>taskana-routing-parent</artifactId>
<version>6.2.1-SNAPSHOT</version>
<version>6.3.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -10,7 +10,7 @@
<parent>
<groupId>pro.taskana</groupId>
<artifactId>taskana-rest-parent</artifactId>
<version>6.2.1-SNAPSHOT</version>
<version>6.3.3-SNAPSHOT</version>
<relativePath>../rest/pom.xml</relativePath>
</parent>