Closes #2366 - add TaskEndstatePreprocessor
This commit is contained in:
parent
24fb36a7df
commit
948fe21d1c
|
@ -6,18 +6,23 @@ import static pro.taskana.testapi.DefaultTestEntities.defaultTestClassification;
|
||||||
import static pro.taskana.testapi.DefaultTestEntities.defaultTestObjectReference;
|
import static pro.taskana.testapi.DefaultTestEntities.defaultTestObjectReference;
|
||||||
import static pro.taskana.testapi.DefaultTestEntities.defaultTestWorkbasket;
|
import static pro.taskana.testapi.DefaultTestEntities.defaultTestWorkbasket;
|
||||||
|
|
||||||
|
import acceptance.task.complete.CompleteTaskWithSpiAccTest.SetCustomAttributeToEndstate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
|
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.junit.jupiter.api.DynamicTest;
|
import org.junit.jupiter.api.DynamicTest;
|
||||||
|
import org.junit.jupiter.api.Nested;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.TestFactory;
|
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.TestTemplate;
|
||||||
import org.junit.jupiter.api.function.ThrowingConsumer;
|
import org.junit.jupiter.api.function.ThrowingConsumer;
|
||||||
import pro.taskana.classification.api.ClassificationService;
|
import pro.taskana.classification.api.ClassificationService;
|
||||||
import pro.taskana.classification.api.models.ClassificationSummary;
|
import pro.taskana.classification.api.models.ClassificationSummary;
|
||||||
import pro.taskana.common.internal.util.Triplet;
|
import pro.taskana.common.internal.util.Triplet;
|
||||||
|
import pro.taskana.spi.task.api.TaskEndstatePreprocessor;
|
||||||
import pro.taskana.task.api.TaskService;
|
import pro.taskana.task.api.TaskService;
|
||||||
import pro.taskana.task.api.TaskState;
|
import pro.taskana.task.api.TaskState;
|
||||||
import pro.taskana.task.api.exceptions.InvalidTaskStateException;
|
import pro.taskana.task.api.exceptions.InvalidTaskStateException;
|
||||||
|
@ -26,6 +31,7 @@ import pro.taskana.task.api.models.Task;
|
||||||
import pro.taskana.testapi.DefaultTestEntities;
|
import pro.taskana.testapi.DefaultTestEntities;
|
||||||
import pro.taskana.testapi.TaskanaInject;
|
import pro.taskana.testapi.TaskanaInject;
|
||||||
import pro.taskana.testapi.TaskanaIntegrationTest;
|
import pro.taskana.testapi.TaskanaIntegrationTest;
|
||||||
|
import pro.taskana.testapi.WithServiceProvider;
|
||||||
import pro.taskana.testapi.builder.TaskBuilder;
|
import pro.taskana.testapi.builder.TaskBuilder;
|
||||||
import pro.taskana.testapi.builder.WorkbasketAccessItemBuilder;
|
import pro.taskana.testapi.builder.WorkbasketAccessItemBuilder;
|
||||||
import pro.taskana.testapi.security.WithAccessId;
|
import pro.taskana.testapi.security.WithAccessId;
|
||||||
|
@ -182,4 +188,33 @@ class CancelTaskAccTest {
|
||||||
};
|
};
|
||||||
return DynamicTest.stream(list.iterator(), Triplet::getLeft, testCancelTask);
|
return DynamicTest.stream(list.iterator(), Triplet::getLeft, testCancelTask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@TestInstance(Lifecycle.PER_CLASS)
|
||||||
|
@WithServiceProvider(
|
||||||
|
serviceProviderInterface = TaskEndstatePreprocessor.class,
|
||||||
|
serviceProviders = SetCustomAttributeToEndstate.class)
|
||||||
|
class ServiceProviderSetsCustomAttributeToCancelled {
|
||||||
|
|
||||||
|
@TaskanaInject TaskService taskService;
|
||||||
|
|
||||||
|
@WithAccessId(user = "user-1-2")
|
||||||
|
@Test
|
||||||
|
void should_SetCustomAttribute_When_UserCancelsTask() throws Exception {
|
||||||
|
Task task =
|
||||||
|
TaskBuilder.newTask()
|
||||||
|
.classificationSummary(defaultClassificationSummary)
|
||||||
|
.workbasketSummary(defaultWorkbasketSummary)
|
||||||
|
.state(TaskState.CLAIMED)
|
||||||
|
.primaryObjRef(DefaultTestEntities.defaultTestObjectReference().build())
|
||||||
|
.buildAndStore(taskService);
|
||||||
|
Task processedTask = taskService.cancelTask(task.getId());
|
||||||
|
assertThat(processedTask.getState()).isEqualTo(TaskState.CANCELLED);
|
||||||
|
assertThat(processedTask.getCustomAttributeMap())
|
||||||
|
.containsEntry(
|
||||||
|
"camunda:attribute1",
|
||||||
|
"{\"valueInfo\":{\"objectTypeName\":\"java.lang.String\"},"
|
||||||
|
+ "\"type\":\"String\",\"value\":\"CANCELLED\"}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import java.util.stream.Stream;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.junit.jupiter.api.DynamicTest;
|
import org.junit.jupiter.api.DynamicTest;
|
||||||
import org.junit.jupiter.api.Nested;
|
import org.junit.jupiter.api.Nested;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.TestFactory;
|
import org.junit.jupiter.api.TestFactory;
|
||||||
import org.junit.jupiter.api.TestInstance;
|
import org.junit.jupiter.api.TestInstance;
|
||||||
import org.junit.jupiter.api.TestInstance.Lifecycle;
|
import org.junit.jupiter.api.TestInstance.Lifecycle;
|
||||||
|
@ -17,6 +18,7 @@ import org.junit.jupiter.api.function.ThrowingConsumer;
|
||||||
import pro.taskana.classification.api.ClassificationService;
|
import pro.taskana.classification.api.ClassificationService;
|
||||||
import pro.taskana.classification.api.models.ClassificationSummary;
|
import pro.taskana.classification.api.models.ClassificationSummary;
|
||||||
import pro.taskana.spi.task.api.ReviewRequiredProvider;
|
import pro.taskana.spi.task.api.ReviewRequiredProvider;
|
||||||
|
import pro.taskana.spi.task.api.TaskEndstatePreprocessor;
|
||||||
import pro.taskana.task.api.TaskService;
|
import pro.taskana.task.api.TaskService;
|
||||||
import pro.taskana.task.api.TaskState;
|
import pro.taskana.task.api.TaskState;
|
||||||
import pro.taskana.task.api.models.ObjectReference;
|
import pro.taskana.task.api.models.ObjectReference;
|
||||||
|
@ -91,6 +93,21 @@ class CompleteTaskWithSpiAccTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class SetCustomAttributeToEndstate implements TaskEndstatePreprocessor {
|
||||||
|
@Override
|
||||||
|
public Task processTaskBeforeEndstate(Task task) {
|
||||||
|
String endstate = task.getState().toString();
|
||||||
|
task.getCustomAttributeMap()
|
||||||
|
.put(
|
||||||
|
"camunda:attribute1",
|
||||||
|
"{\"valueInfo\":{\"objectTypeName\":\"java.lang.String\"},"
|
||||||
|
+ "\"type\":\"String\",\"value\":\""
|
||||||
|
+ endstate
|
||||||
|
+ "\"}");
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
@TestInstance(Lifecycle.PER_CLASS)
|
@TestInstance(Lifecycle.PER_CLASS)
|
||||||
@WithServiceProvider(
|
@WithServiceProvider(
|
||||||
|
@ -171,4 +188,27 @@ class CompleteTaskWithSpiAccTest {
|
||||||
tasks.iterator(), t -> "Try to complete " + t.getState().name() + " Task", test);
|
tasks.iterator(), t -> "Try to complete " + t.getState().name() + " Task", test);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@TestInstance(Lifecycle.PER_CLASS)
|
||||||
|
@WithServiceProvider(
|
||||||
|
serviceProviderInterface = TaskEndstatePreprocessor.class,
|
||||||
|
serviceProviders = SetCustomAttributeToEndstate.class)
|
||||||
|
class ServiceProviderSetsCustomAttributeToCompleted {
|
||||||
|
|
||||||
|
@TaskanaInject TaskService taskService;
|
||||||
|
|
||||||
|
@WithAccessId(user = "user-1-1")
|
||||||
|
@Test
|
||||||
|
void should_SetCustomAttribute_When_UserCompletesTask() throws Exception {
|
||||||
|
Task task = createTaskClaimedByUser("user-1-1").buildAndStore(taskService);
|
||||||
|
Task processedTask = taskService.completeTask(task.getId());
|
||||||
|
assertThat(processedTask.getState()).isEqualTo(TaskState.COMPLETED);
|
||||||
|
assertThat(processedTask.getCustomAttributeMap())
|
||||||
|
.containsEntry(
|
||||||
|
"camunda:attribute1",
|
||||||
|
"{\"valueInfo\":{\"objectTypeName\":\"java.lang.String\"},"
|
||||||
|
+ "\"type\":\"String\",\"value\":\"COMPLETED\"}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,17 @@ import static org.assertj.core.api.Assertions.catchThrowableOfType;
|
||||||
import static pro.taskana.testapi.DefaultTestEntities.defaultTestClassification;
|
import static pro.taskana.testapi.DefaultTestEntities.defaultTestClassification;
|
||||||
import static pro.taskana.testapi.DefaultTestEntities.defaultTestWorkbasket;
|
import static pro.taskana.testapi.DefaultTestEntities.defaultTestWorkbasket;
|
||||||
|
|
||||||
|
import acceptance.task.complete.CompleteTaskWithSpiAccTest.SetCustomAttributeToEndstate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
|
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.junit.jupiter.api.DynamicTest;
|
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.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.TestTemplate;
|
||||||
import org.junit.jupiter.api.function.ThrowingConsumer;
|
import org.junit.jupiter.api.function.ThrowingConsumer;
|
||||||
import pro.taskana.classification.api.ClassificationService;
|
import pro.taskana.classification.api.ClassificationService;
|
||||||
|
@ -19,6 +24,7 @@ import pro.taskana.common.api.TaskanaRole;
|
||||||
import pro.taskana.common.api.exceptions.NotAuthorizedException;
|
import pro.taskana.common.api.exceptions.NotAuthorizedException;
|
||||||
import pro.taskana.common.api.security.CurrentUserContext;
|
import pro.taskana.common.api.security.CurrentUserContext;
|
||||||
import pro.taskana.common.internal.util.Triplet;
|
import pro.taskana.common.internal.util.Triplet;
|
||||||
|
import pro.taskana.spi.task.api.TaskEndstatePreprocessor;
|
||||||
import pro.taskana.task.api.TaskService;
|
import pro.taskana.task.api.TaskService;
|
||||||
import pro.taskana.task.api.TaskState;
|
import pro.taskana.task.api.TaskState;
|
||||||
import pro.taskana.task.api.exceptions.InvalidTaskStateException;
|
import pro.taskana.task.api.exceptions.InvalidTaskStateException;
|
||||||
|
@ -26,6 +32,7 @@ import pro.taskana.task.api.models.Task;
|
||||||
import pro.taskana.testapi.DefaultTestEntities;
|
import pro.taskana.testapi.DefaultTestEntities;
|
||||||
import pro.taskana.testapi.TaskanaInject;
|
import pro.taskana.testapi.TaskanaInject;
|
||||||
import pro.taskana.testapi.TaskanaIntegrationTest;
|
import pro.taskana.testapi.TaskanaIntegrationTest;
|
||||||
|
import pro.taskana.testapi.WithServiceProvider;
|
||||||
import pro.taskana.testapi.builder.TaskBuilder;
|
import pro.taskana.testapi.builder.TaskBuilder;
|
||||||
import pro.taskana.testapi.builder.WorkbasketAccessItemBuilder;
|
import pro.taskana.testapi.builder.WorkbasketAccessItemBuilder;
|
||||||
import pro.taskana.testapi.security.WithAccessId;
|
import pro.taskana.testapi.security.WithAccessId;
|
||||||
|
@ -157,4 +164,33 @@ class TerminateTaskAccTest {
|
||||||
|
|
||||||
return DynamicTest.stream(testValues.iterator(), Triplet::getLeft, test);
|
return DynamicTest.stream(testValues.iterator(), Triplet::getLeft, test);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
@TestInstance(Lifecycle.PER_CLASS)
|
||||||
|
@WithServiceProvider(
|
||||||
|
serviceProviderInterface = TaskEndstatePreprocessor.class,
|
||||||
|
serviceProviders = SetCustomAttributeToEndstate.class)
|
||||||
|
class ServiceProviderSetsCustomAttributeToTerminated {
|
||||||
|
|
||||||
|
@TaskanaInject TaskService taskService;
|
||||||
|
|
||||||
|
@WithAccessId(user = "admin")
|
||||||
|
@Test
|
||||||
|
void should_SetCustomAttribute_When_UserTerminatesTask() throws Exception {
|
||||||
|
Task task =
|
||||||
|
TaskBuilder.newTask()
|
||||||
|
.classificationSummary(defaultClassificationSummary)
|
||||||
|
.workbasketSummary(defaultWorkbasketSummary)
|
||||||
|
.state(TaskState.READY)
|
||||||
|
.primaryObjRef(DefaultTestEntities.defaultTestObjectReference().build())
|
||||||
|
.buildAndStore(taskService);
|
||||||
|
Task processedTask = taskService.terminateTask(task.getId());
|
||||||
|
assertThat(processedTask.getState()).isEqualTo(TaskState.TERMINATED);
|
||||||
|
assertThat(processedTask.getCustomAttributeMap())
|
||||||
|
.containsEntry(
|
||||||
|
"camunda:attribute1",
|
||||||
|
"{\"valueInfo\":{\"objectTypeName\":\"java.lang.String\"},"
|
||||||
|
+ "\"type\":\"String\",\"value\":\"TERMINATED\"}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import pro.taskana.spi.task.internal.BeforeRequestChangesManager;
|
||||||
import pro.taskana.spi.task.internal.BeforeRequestReviewManager;
|
import pro.taskana.spi.task.internal.BeforeRequestReviewManager;
|
||||||
import pro.taskana.spi.task.internal.CreateTaskPreprocessorManager;
|
import pro.taskana.spi.task.internal.CreateTaskPreprocessorManager;
|
||||||
import pro.taskana.spi.task.internal.ReviewRequiredManager;
|
import pro.taskana.spi.task.internal.ReviewRequiredManager;
|
||||||
|
import pro.taskana.spi.task.internal.TaskEndstatePreprocessorManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FOR INTERNAL USE ONLY.
|
* FOR INTERNAL USE ONLY.
|
||||||
|
@ -144,4 +145,11 @@ public interface InternalTaskanaEngine {
|
||||||
* @return the {@linkplain AfterRequestChangesManager} instance
|
* @return the {@linkplain AfterRequestChangesManager} instance
|
||||||
*/
|
*/
|
||||||
AfterRequestChangesManager getAfterRequestChangesManager();
|
AfterRequestChangesManager getAfterRequestChangesManager();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the {@linkplain pro.taskana.spi.task.internal.TaskEndstatePreprocessorManager}.
|
||||||
|
*
|
||||||
|
* @return the {@linkplain pro.taskana.spi.task.internal.TaskEndstatePreprocessorManager} instance
|
||||||
|
*/
|
||||||
|
TaskEndstatePreprocessorManager getTaskEndstatePreprocessorManager();
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,6 +65,7 @@ import pro.taskana.spi.task.internal.BeforeRequestChangesManager;
|
||||||
import pro.taskana.spi.task.internal.BeforeRequestReviewManager;
|
import pro.taskana.spi.task.internal.BeforeRequestReviewManager;
|
||||||
import pro.taskana.spi.task.internal.CreateTaskPreprocessorManager;
|
import pro.taskana.spi.task.internal.CreateTaskPreprocessorManager;
|
||||||
import pro.taskana.spi.task.internal.ReviewRequiredManager;
|
import pro.taskana.spi.task.internal.ReviewRequiredManager;
|
||||||
|
import pro.taskana.spi.task.internal.TaskEndstatePreprocessorManager;
|
||||||
import pro.taskana.task.api.TaskService;
|
import pro.taskana.task.api.TaskService;
|
||||||
import pro.taskana.task.internal.AttachmentMapper;
|
import pro.taskana.task.internal.AttachmentMapper;
|
||||||
import pro.taskana.task.internal.ObjectReferenceMapper;
|
import pro.taskana.task.internal.ObjectReferenceMapper;
|
||||||
|
@ -98,6 +99,8 @@ public class TaskanaEngineImpl implements TaskanaEngine {
|
||||||
private final AfterRequestReviewManager afterRequestReviewManager;
|
private final AfterRequestReviewManager afterRequestReviewManager;
|
||||||
private final BeforeRequestChangesManager beforeRequestChangesManager;
|
private final BeforeRequestChangesManager beforeRequestChangesManager;
|
||||||
private final AfterRequestChangesManager afterRequestChangesManager;
|
private final AfterRequestChangesManager afterRequestChangesManager;
|
||||||
|
private final TaskEndstatePreprocessorManager taskEndstatePreprocessorManager;
|
||||||
|
|
||||||
private final InternalTaskanaEngineImpl internalTaskanaEngineImpl;
|
private final InternalTaskanaEngineImpl internalTaskanaEngineImpl;
|
||||||
private final WorkingTimeCalculator workingTimeCalculator;
|
private final WorkingTimeCalculator workingTimeCalculator;
|
||||||
private final HistoryEventManager historyEventManager;
|
private final HistoryEventManager historyEventManager;
|
||||||
|
@ -176,6 +179,7 @@ public class TaskanaEngineImpl implements TaskanaEngine {
|
||||||
afterRequestReviewManager = new AfterRequestReviewManager(this);
|
afterRequestReviewManager = new AfterRequestReviewManager(this);
|
||||||
beforeRequestChangesManager = new BeforeRequestChangesManager(this);
|
beforeRequestChangesManager = new BeforeRequestChangesManager(this);
|
||||||
afterRequestChangesManager = new AfterRequestChangesManager(this);
|
afterRequestChangesManager = new AfterRequestChangesManager(this);
|
||||||
|
taskEndstatePreprocessorManager = new TaskEndstatePreprocessorManager();
|
||||||
|
|
||||||
// don't remove, to reset possible explicit mode
|
// don't remove, to reset possible explicit mode
|
||||||
this.mode = connectionManagementMode;
|
this.mode = connectionManagementMode;
|
||||||
|
@ -624,5 +628,10 @@ public class TaskanaEngineImpl implements TaskanaEngine {
|
||||||
public AfterRequestChangesManager getAfterRequestChangesManager() {
|
public AfterRequestChangesManager getAfterRequestChangesManager() {
|
||||||
return afterRequestChangesManager;
|
return afterRequestChangesManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TaskEndstatePreprocessorManager getTaskEndstatePreprocessorManager() {
|
||||||
|
return taskEndstatePreprocessorManager;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
package pro.taskana.spi.task.api;
|
||||||
|
|
||||||
|
import pro.taskana.task.api.models.Task;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The TaskEndstatePreprocessor allows to implement customized behaviour before the given
|
||||||
|
* {@linkplain Task} goes into an {@linkplain pro.taskana.task.api.TaskState#END_STATES end state}
|
||||||
|
* (cancelled, terminated or completed).
|
||||||
|
*/
|
||||||
|
public interface TaskEndstatePreprocessor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform any action before a {@linkplain Task} goes into an {@linkplain
|
||||||
|
* pro.taskana.task.api.TaskState#END_STATES end state}. A {@linkplain Task} goes into an end
|
||||||
|
* state at the end of the following methods: {@linkplain
|
||||||
|
* pro.taskana.task.api.TaskService#completeTask(String)}, {@linkplain
|
||||||
|
* pro.taskana.task.api.TaskService#cancelTask(String)}, {@linkplain
|
||||||
|
* pro.taskana.task.api.TaskService#terminateTask(String)}.
|
||||||
|
*
|
||||||
|
* <p>This SPI is executed within the same transaction staple as {@linkplain
|
||||||
|
* pro.taskana.task.api.TaskService#completeTask(String)}, {@linkplain
|
||||||
|
* pro.taskana.task.api.TaskService#cancelTask(String)}, {@linkplain
|
||||||
|
* pro.taskana.task.api.TaskService#terminateTask(String)}.
|
||||||
|
*
|
||||||
|
* <p>This SPI is executed with the same {@linkplain
|
||||||
|
* pro.taskana.common.api.security.UserPrincipal} and {@linkplain
|
||||||
|
* pro.taskana.common.api.security.GroupPrincipal} as in the methods mentioned above.
|
||||||
|
*
|
||||||
|
* @param taskToProcess the {@linkplain Task} to preprocess
|
||||||
|
* @return the modified {@linkplain Task}. <b>IMPORTANT:</b> persistent changes to the {@linkplain
|
||||||
|
* Task} have to be managed by the service provider
|
||||||
|
*/
|
||||||
|
Task processTaskBeforeEndstate(Task taskToProcess);
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package pro.taskana.spi.task.internal;
|
||||||
|
|
||||||
|
import static pro.taskana.common.internal.util.CheckedConsumer.wrap;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import pro.taskana.common.internal.util.SpiLoader;
|
||||||
|
import pro.taskana.spi.task.api.TaskEndstatePreprocessor;
|
||||||
|
import pro.taskana.task.api.models.Task;
|
||||||
|
|
||||||
|
public class TaskEndstatePreprocessorManager {
|
||||||
|
|
||||||
|
private static final Logger LOGGER =
|
||||||
|
LoggerFactory.getLogger(TaskEndstatePreprocessorManager.class);
|
||||||
|
private final List<TaskEndstatePreprocessor> taskEndstatePreprocessors;
|
||||||
|
|
||||||
|
public TaskEndstatePreprocessorManager() {
|
||||||
|
taskEndstatePreprocessors = SpiLoader.load(TaskEndstatePreprocessor.class);
|
||||||
|
for (TaskEndstatePreprocessor preprocessor : taskEndstatePreprocessors) {
|
||||||
|
LOGGER.info(
|
||||||
|
"Registered TaskEndstatePreprocessor provider: {}", preprocessor.getClass().getName());
|
||||||
|
}
|
||||||
|
if (taskEndstatePreprocessors.isEmpty()) {
|
||||||
|
LOGGER.info("No TaskEndstatePreprocessor found. Running without TaskEndstatePreprocessor.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task processTaskBeforeEndstate(Task taskToProcess) {
|
||||||
|
if (LOGGER.isDebugEnabled()) {
|
||||||
|
LOGGER.debug("Sending task to TaskEndstatePreprocessor providers: {}", taskToProcess);
|
||||||
|
}
|
||||||
|
taskEndstatePreprocessors.forEach(
|
||||||
|
wrap(
|
||||||
|
taskEndstatePreprocessor ->
|
||||||
|
taskEndstatePreprocessor.processTaskBeforeEndstate(taskToProcess)));
|
||||||
|
return taskToProcess;
|
||||||
|
}
|
||||||
|
}
|
|
@ -58,6 +58,7 @@ import pro.taskana.spi.task.internal.BeforeRequestChangesManager;
|
||||||
import pro.taskana.spi.task.internal.BeforeRequestReviewManager;
|
import pro.taskana.spi.task.internal.BeforeRequestReviewManager;
|
||||||
import pro.taskana.spi.task.internal.CreateTaskPreprocessorManager;
|
import pro.taskana.spi.task.internal.CreateTaskPreprocessorManager;
|
||||||
import pro.taskana.spi.task.internal.ReviewRequiredManager;
|
import pro.taskana.spi.task.internal.ReviewRequiredManager;
|
||||||
|
import pro.taskana.spi.task.internal.TaskEndstatePreprocessorManager;
|
||||||
import pro.taskana.task.api.CallbackState;
|
import pro.taskana.task.api.CallbackState;
|
||||||
import pro.taskana.task.api.TaskCommentQuery;
|
import pro.taskana.task.api.TaskCommentQuery;
|
||||||
import pro.taskana.task.api.TaskCustomField;
|
import pro.taskana.task.api.TaskCustomField;
|
||||||
|
@ -123,6 +124,7 @@ public class TaskServiceImpl implements TaskService {
|
||||||
private final AfterRequestReviewManager afterRequestReviewManager;
|
private final AfterRequestReviewManager afterRequestReviewManager;
|
||||||
private final BeforeRequestChangesManager beforeRequestChangesManager;
|
private final BeforeRequestChangesManager beforeRequestChangesManager;
|
||||||
private final AfterRequestChangesManager afterRequestChangesManager;
|
private final AfterRequestChangesManager afterRequestChangesManager;
|
||||||
|
private final TaskEndstatePreprocessorManager taskEndstatePreprocessorManager;
|
||||||
|
|
||||||
public TaskServiceImpl(
|
public TaskServiceImpl(
|
||||||
InternalTaskanaEngine taskanaEngine,
|
InternalTaskanaEngine taskanaEngine,
|
||||||
|
@ -146,6 +148,7 @@ public class TaskServiceImpl implements TaskService {
|
||||||
this.afterRequestReviewManager = taskanaEngine.getAfterRequestReviewManager();
|
this.afterRequestReviewManager = taskanaEngine.getAfterRequestReviewManager();
|
||||||
this.beforeRequestChangesManager = taskanaEngine.getBeforeRequestChangesManager();
|
this.beforeRequestChangesManager = taskanaEngine.getBeforeRequestChangesManager();
|
||||||
this.afterRequestChangesManager = taskanaEngine.getAfterRequestChangesManager();
|
this.afterRequestChangesManager = taskanaEngine.getAfterRequestChangesManager();
|
||||||
|
this.taskEndstatePreprocessorManager = taskanaEngine.getTaskEndstatePreprocessorManager();
|
||||||
this.taskTransferrer = new TaskTransferrer(taskanaEngine, taskMapper, this);
|
this.taskTransferrer = new TaskTransferrer(taskanaEngine, taskMapper, this);
|
||||||
this.taskCommentService =
|
this.taskCommentService =
|
||||||
new TaskCommentServiceImpl(taskanaEngine, taskCommentMapper, userMapper, this);
|
new TaskCommentServiceImpl(taskanaEngine, taskCommentMapper, userMapper, this);
|
||||||
|
@ -874,12 +877,35 @@ public class TaskServiceImpl implements TaskService {
|
||||||
public Task cancelTask(String taskId)
|
public Task cancelTask(String taskId)
|
||||||
throws TaskNotFoundException, NotAuthorizedOnWorkbasketException, InvalidTaskStateException {
|
throws TaskNotFoundException, NotAuthorizedOnWorkbasketException, InvalidTaskStateException {
|
||||||
|
|
||||||
Task cancelledTask;
|
TaskImpl cancelledTask;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
taskanaEngine.openConnection();
|
taskanaEngine.openConnection();
|
||||||
cancelledTask = terminateCancelCommonActions(taskId, TaskState.CANCELLED);
|
if (taskId == null || taskId.isEmpty()) {
|
||||||
|
throw new TaskNotFoundException(taskId);
|
||||||
|
}
|
||||||
|
cancelledTask = (TaskImpl) getTask(taskId);
|
||||||
|
TaskState state = cancelledTask.getState();
|
||||||
|
if (state.isEndState()) {
|
||||||
|
throw new InvalidTaskStateException(
|
||||||
|
taskId,
|
||||||
|
state,
|
||||||
|
TaskState.READY,
|
||||||
|
TaskState.CLAIMED,
|
||||||
|
TaskState.READY_FOR_REVIEW,
|
||||||
|
TaskState.IN_REVIEW);
|
||||||
|
}
|
||||||
|
|
||||||
|
terminateCancelCommonActions(cancelledTask, TaskState.CANCELLED);
|
||||||
|
cancelledTask =
|
||||||
|
(TaskImpl) taskEndstatePreprocessorManager.processTaskBeforeEndstate(cancelledTask);
|
||||||
|
taskMapper.update(cancelledTask);
|
||||||
|
if (LOGGER.isDebugEnabled()) {
|
||||||
|
LOGGER.debug(
|
||||||
|
"Task '{}' cancelled by user '{}'.",
|
||||||
|
taskId,
|
||||||
|
taskanaEngine.getEngine().getCurrentUserContext().getUserid());
|
||||||
|
}
|
||||||
if (historyEventManager.isEnabled()) {
|
if (historyEventManager.isEnabled()) {
|
||||||
historyEventManager.createEvent(
|
historyEventManager.createEvent(
|
||||||
new TaskCancelledEvent(
|
new TaskCancelledEvent(
|
||||||
|
@ -901,12 +927,35 @@ public class TaskServiceImpl implements TaskService {
|
||||||
|
|
||||||
taskanaEngine.getEngine().checkRoleMembership(TaskanaRole.ADMIN, TaskanaRole.TASK_ADMIN);
|
taskanaEngine.getEngine().checkRoleMembership(TaskanaRole.ADMIN, TaskanaRole.TASK_ADMIN);
|
||||||
|
|
||||||
Task terminatedTask;
|
TaskImpl terminatedTask;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
taskanaEngine.openConnection();
|
taskanaEngine.openConnection();
|
||||||
terminatedTask = terminateCancelCommonActions(taskId, TaskState.TERMINATED);
|
if (taskId == null || taskId.isEmpty()) {
|
||||||
|
throw new TaskNotFoundException(taskId);
|
||||||
|
}
|
||||||
|
terminatedTask = (TaskImpl) getTask(taskId);
|
||||||
|
TaskState state = terminatedTask.getState();
|
||||||
|
if (state.isEndState()) {
|
||||||
|
throw new InvalidTaskStateException(
|
||||||
|
taskId,
|
||||||
|
state,
|
||||||
|
TaskState.READY,
|
||||||
|
TaskState.CLAIMED,
|
||||||
|
TaskState.READY_FOR_REVIEW,
|
||||||
|
TaskState.IN_REVIEW);
|
||||||
|
}
|
||||||
|
|
||||||
|
terminateCancelCommonActions(terminatedTask, TaskState.TERMINATED);
|
||||||
|
terminatedTask =
|
||||||
|
(TaskImpl) taskEndstatePreprocessorManager.processTaskBeforeEndstate(terminatedTask);
|
||||||
|
taskMapper.update(terminatedTask);
|
||||||
|
if (LOGGER.isDebugEnabled()) {
|
||||||
|
LOGGER.debug(
|
||||||
|
"Task '{}' cancelled by user '{}'.",
|
||||||
|
taskId,
|
||||||
|
taskanaEngine.getEngine().getCurrentUserContext().getUserid());
|
||||||
|
}
|
||||||
if (historyEventManager.isEnabled()) {
|
if (historyEventManager.isEnabled()) {
|
||||||
historyEventManager.createEvent(
|
historyEventManager.createEvent(
|
||||||
new TaskTerminatedEvent(
|
new TaskTerminatedEvent(
|
||||||
|
@ -1208,35 +1257,11 @@ public class TaskServiceImpl implements TaskService {
|
||||||
newTaskImpl.setModified(Instant.now());
|
newTaskImpl.setModified(Instant.now());
|
||||||
}
|
}
|
||||||
|
|
||||||
private TaskImpl terminateCancelCommonActions(String taskId, TaskState targetState)
|
private static void terminateCancelCommonActions(TaskImpl task, TaskState targetState) {
|
||||||
throws TaskNotFoundException, NotAuthorizedOnWorkbasketException, InvalidTaskStateException {
|
|
||||||
if (taskId == null || taskId.isEmpty()) {
|
|
||||||
throw new TaskNotFoundException(taskId);
|
|
||||||
}
|
|
||||||
TaskImpl task = (TaskImpl) getTask(taskId);
|
|
||||||
TaskState state = task.getState();
|
|
||||||
if (state.isEndState()) {
|
|
||||||
throw new InvalidTaskStateException(
|
|
||||||
taskId,
|
|
||||||
state,
|
|
||||||
TaskState.READY,
|
|
||||||
TaskState.CLAIMED,
|
|
||||||
TaskState.READY_FOR_REVIEW,
|
|
||||||
TaskState.IN_REVIEW);
|
|
||||||
}
|
|
||||||
|
|
||||||
Instant now = Instant.now();
|
Instant now = Instant.now();
|
||||||
task.setModified(now);
|
task.setModified(now);
|
||||||
task.setCompleted(now);
|
task.setCompleted(now);
|
||||||
task.setState(targetState);
|
task.setState(targetState);
|
||||||
taskMapper.update(task);
|
|
||||||
if (LOGGER.isDebugEnabled()) {
|
|
||||||
LOGGER.debug(
|
|
||||||
"Task '{}' cancelled by user '{}'.",
|
|
||||||
taskId,
|
|
||||||
taskanaEngine.getEngine().getCurrentUserContext().getUserid());
|
|
||||||
}
|
|
||||||
return task;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task claim(String taskId, boolean forceClaim)
|
private Task claim(String taskId, boolean forceClaim)
|
||||||
|
@ -1552,6 +1577,7 @@ public class TaskServiceImpl implements TaskService {
|
||||||
|
|
||||||
Instant now = Instant.now();
|
Instant now = Instant.now();
|
||||||
completeActionsOnTask(task, userId, now);
|
completeActionsOnTask(task, userId, now);
|
||||||
|
task = (TaskImpl) taskEndstatePreprocessorManager.processTaskBeforeEndstate(task);
|
||||||
taskMapper.update(task);
|
taskMapper.update(task);
|
||||||
if (LOGGER.isDebugEnabled()) {
|
if (LOGGER.isDebugEnabled()) {
|
||||||
LOGGER.debug("Task '{}' completed by user '{}'.", taskId, userId);
|
LOGGER.debug("Task '{}' completed by user '{}'.", taskId, userId);
|
||||||
|
|
|
@ -20,6 +20,7 @@ import pro.taskana.spi.task.api.BeforeRequestChangesProvider;
|
||||||
import pro.taskana.spi.task.api.BeforeRequestReviewProvider;
|
import pro.taskana.spi.task.api.BeforeRequestReviewProvider;
|
||||||
import pro.taskana.spi.task.api.CreateTaskPreprocessor;
|
import pro.taskana.spi.task.api.CreateTaskPreprocessor;
|
||||||
import pro.taskana.spi.task.api.ReviewRequiredProvider;
|
import pro.taskana.spi.task.api.ReviewRequiredProvider;
|
||||||
|
import pro.taskana.spi.task.api.TaskEndstatePreprocessor;
|
||||||
import pro.taskana.testapi.WithServiceProvider;
|
import pro.taskana.testapi.WithServiceProvider;
|
||||||
|
|
||||||
public class ServiceProviderExtractor {
|
public class ServiceProviderExtractor {
|
||||||
|
@ -34,7 +35,8 @@ public class ServiceProviderExtractor {
|
||||||
BeforeRequestReviewProvider.class,
|
BeforeRequestReviewProvider.class,
|
||||||
AfterRequestReviewProvider.class,
|
AfterRequestReviewProvider.class,
|
||||||
BeforeRequestChangesProvider.class,
|
BeforeRequestChangesProvider.class,
|
||||||
AfterRequestChangesProvider.class);
|
AfterRequestChangesProvider.class,
|
||||||
|
TaskEndstatePreprocessor.class);
|
||||||
|
|
||||||
private ServiceProviderExtractor() {
|
private ServiceProviderExtractor() {
|
||||||
throw new IllegalStateException("utility class");
|
throw new IllegalStateException("utility class");
|
||||||
|
|
Loading…
Reference in New Issue