diff --git a/lib/taskana-core/src/main/java/pro/taskana/TaskanaEngineConfiguration.java b/lib/taskana-core/src/main/java/pro/taskana/TaskanaEngineConfiguration.java index d952d7623..96e04fd6b 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/TaskanaEngineConfiguration.java +++ b/lib/taskana-core/src/main/java/pro/taskana/TaskanaEngineConfiguration.java @@ -74,6 +74,8 @@ public class TaskanaEngineConfiguration { private static final String TASKANA_CUSTOM_HOLIDAY_DAY_MONTH_SEPARATOR = "."; private static final String TASKANA_HISTORY_DELETION_ON_TASK_DELETION_ENABLED = "taskana.history.deletion.on.task.deletion.enabled"; + private static final String TASKANA_VALIDATION_ALLOW_TIMESTAMP_SERVICE_LEVEL_MISMATCH = + "taskana.validation.allowTimestampServiceLevelMismatch"; // TASKANA_SCHEMA_VERSION private static final String DEFAULT_SCHEMA_NAME = "TASKANA"; @@ -107,6 +109,7 @@ public class TaskanaEngineConfiguration { private Duration cleanupJobRunEvery = Duration.parse("P1D"); private Duration cleanupJobMinimumAge = Duration.parse("P14D"); private boolean taskCleanupJobAllCompletedSameParentBusiness = true; + private boolean validationAllowTimestampServiceLevelMismatch = false; private int priorityJobBatchSize = 100; private Instant priorityJobFirstRun = Instant.parse("2018-01-01T00:00:00Z"); @@ -174,6 +177,10 @@ public class TaskanaEngineConfiguration { props, TASKANA_HISTORY_DELETION_ON_TASK_DELETION_ENABLED, this::setDeleteHistoryOnTaskDeletionEnabled); + initBooleanProperty( + props, + TASKANA_VALIDATION_ALLOW_TIMESTAMP_SERVICE_LEVEL_MISMATCH, + this::setValidationAllowTimestampServiceLevelMismatch); initCustomHolidays(props, separator); } @@ -262,6 +269,16 @@ public class TaskanaEngineConfiguration { this.germanPublicHolidaysEnabled = germanPublicHolidaysEnabled; } + public boolean isValidationAllowTimestampServiceLevelMismatch() { + return validationAllowTimestampServiceLevelMismatch; + } + + public void setValidationAllowTimestampServiceLevelMismatch( + boolean validationAllowTimestampServiceLevelMismatch) { + this.validationAllowTimestampServiceLevelMismatch = + validationAllowTimestampServiceLevelMismatch; + } + public boolean isDeleteHistoryOnTaskDeletionEnabled() { return deleteHistoryOnTaskDeletionEnabled; } diff --git a/lib/taskana-core/src/main/java/pro/taskana/task/internal/ServiceLevelHandler.java b/lib/taskana-core/src/main/java/pro/taskana/task/internal/ServiceLevelHandler.java index 5d05fc5c6..587eb67a8 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/task/internal/ServiceLevelHandler.java +++ b/lib/taskana-core/src/main/java/pro/taskana/task/internal/ServiceLevelHandler.java @@ -243,6 +243,15 @@ class ServiceLevelHandler { private TaskImpl updatePlannedDueOnTaskUpdate( TaskImpl newTaskImpl, TaskImpl oldTaskImpl, DurationPrioHolder durationPrioHolder) throws InvalidArgumentException { + if (taskanaEngine + .getEngine() + .getConfiguration() + .isValidationAllowTimestampServiceLevelMismatch() + && newTaskImpl.getDue() != null + && newTaskImpl.getPlanned() != null) { + + return newTaskImpl; + } if (newTaskImpl.getPlanned() == null && newTaskImpl.getDue() == null) { newTaskImpl.setPlanned(oldTaskImpl.getPlanned()); } @@ -325,6 +334,14 @@ class ServiceLevelHandler { private TaskImpl updatePlannedDueOnCreationOfNewTask( TaskImpl newTask, DurationPrioHolder durationPrioHolder) throws InvalidArgumentException { + if (taskanaEngine + .getEngine() + .getConfiguration() + .isValidationAllowTimestampServiceLevelMismatch() + && newTask.getDue() != null + && newTask.getPlanned() != null) { + return newTask; + } if (newTask.getDue() != null) { // due is specified: calculate back and check correctness Instant calcDue = getPrecedingWorkingDays(newTask.getDue(), Duration.ofDays(0)); diff --git a/lib/taskana-core/src/test/java/acceptance/task/CreateTaskAccTest.java b/lib/taskana-core/src/test/java/acceptance/task/CreateTaskAccTest.java index e84d7997b..ab10f53b6 100644 --- a/lib/taskana-core/src/test/java/acceptance/task/CreateTaskAccTest.java +++ b/lib/taskana-core/src/test/java/acceptance/task/CreateTaskAccTest.java @@ -100,6 +100,57 @@ class CreateTaskAccTest extends AbstractAccTest { assertThat(createdTask.isTransferred()).isFalse(); } + @WithAccessId(user = "user-1-1") + @Test + void should_PreventTimestampServiceLevelMismatch_When_ConfigurationPreventsIt() { + // Given + Task newTask = taskService.newTask("USER-1-1", "DOMAIN_A"); + newTask.setClassificationKey("T6310"); + ObjectReference objectReference = + createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"); + newTask.setPrimaryObjRef(objectReference); + newTask.setOwner("user-1-1"); + + // When + Instant planned = Instant.parse("2018-01-02T00:00:00Z"); + newTask.setPlanned(planned); + Instant due = Instant.parse("2018-02-15T00:00:00Z"); + newTask.setDue(due); + + // Then + assertThatThrownBy(() -> taskService.createTask(newTask)) + .isInstanceOf(InvalidArgumentException.class) + .hasMessageContaining("not matching the service level"); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_AllowTimestampServiceLevelMismatch_When_ConfigurationAllowsIt() throws Exception { + // Given + try { + taskanaEngineConfiguration.setValidationAllowTimestampServiceLevelMismatch(true); + Task newTask = taskService.newTask("USER-1-1", "DOMAIN_A"); + newTask.setClassificationKey("T6310"); + ObjectReference objectReference = + createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"); + newTask.setPrimaryObjRef(objectReference); + newTask.setOwner("user-1-1"); + + // When + Instant planned = Instant.parse("2018-01-02T00:00:00Z"); + newTask.setPlanned(planned); + Instant due = Instant.parse("2018-02-15T00:00:00Z"); + newTask.setDue(due); + Task createdTask = taskService.createTask(newTask); + + // Then + assertThat(createdTask.getPlanned()).isEqualTo(planned); + assertThat(createdTask.getDue()).isEqualTo(due); + } finally { + taskanaEngineConfiguration.setValidationAllowTimestampServiceLevelMismatch(false); + } + } + @WithAccessId(user = "user-1-1") @Test void should_CreateTask_When_ObjectReferenceSystemAndSystemInstanceIsNull() throws Exception { diff --git a/lib/taskana-core/src/test/java/acceptance/task/UpdateTaskAccTest.java b/lib/taskana-core/src/test/java/acceptance/task/UpdateTaskAccTest.java index cea3c46e6..8db012522 100644 --- a/lib/taskana-core/src/test/java/acceptance/task/UpdateTaskAccTest.java +++ b/lib/taskana-core/src/test/java/acceptance/task/UpdateTaskAccTest.java @@ -68,6 +68,48 @@ class UpdateTaskAccTest extends AbstractAccTest { assertThat(updatedTask.getParentBusinessProcessId()).isEqualTo("MY_PARENT_PROCESS_ID"); } + @WithAccessId(user = "user-1-1") + @Test + void should_PreventTimestampServiceLevelMismatch_When_ConfigurationPreventsIt() throws Exception { + // Given + + taskanaEngineConfiguration.setValidationAllowTimestampServiceLevelMismatch(false); + Task task = taskService.getTask("TKI:000000000000000000000000000000000000"); + // When + Instant planned = Instant.parse("2018-03-02T00:00:00Z"); + task.setPlanned(planned); + Instant due = Instant.parse("2018-04-15T00:00:00Z"); + task.setDue(due); + + // Then + assertThatThrownBy(() -> taskService.updateTask(task)) + .isInstanceOf(InvalidArgumentException.class) + .hasMessageContaining("not matching the service level"); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_AllowTimestampServiceLevelMismatch_When_ConfigurationAllowsIt() throws Exception { + try { + // Given + taskanaEngineConfiguration.setValidationAllowTimestampServiceLevelMismatch(true); + Task task = taskService.getTask("TKI:000000000000000000000000000000000000"); + + // When + Instant planned = Instant.parse("2018-01-02T00:00:00Z"); + task.setPlanned(planned); + Instant due = Instant.parse("2018-02-15T00:00:00Z"); + task.setDue(due); + Task updateTask = taskService.updateTask(task); + + // Then + assertThat(updateTask.getPlanned()).isEqualTo(planned); + assertThat(updateTask.getDue()).isEqualTo(due); + } finally { + taskanaEngineConfiguration.setValidationAllowTimestampServiceLevelMismatch(false); + } + } + @WithAccessId(user = "user-1-1") @Test void should_UpdatePrimaryObjectReferenceOfTask_When_ObjectreferenceSystemAndSystemInstanceIsNull() diff --git a/lib/taskana-core/src/test/resources/taskana.properties b/lib/taskana-core/src/test/resources/taskana.properties index 05a50d862..5b0040671 100644 --- a/lib/taskana-core/src/test/resources/taskana.properties +++ b/lib/taskana-core/src/test/resources/taskana.properties @@ -15,5 +15,5 @@ taskana.jobs.cleanup.minimumAge=P14D taskana.german.holidays.enabled=true taskana.german.holidays.corpus-christi.enabled=false taskana.history.deletion.on.task.deletion.enabled=true - +taskana.validation.allowTimestampServiceLevelMismatch=false