diff --git a/lib/taskana-core-test/src/test/java/acceptance/task/requestreview/RequestReviewWithSpiAccTest.java b/lib/taskana-core-test/src/test/java/acceptance/task/requestreview/RequestReviewWithAfterSpiAccTest.java similarity index 99% rename from lib/taskana-core-test/src/test/java/acceptance/task/requestreview/RequestReviewWithSpiAccTest.java rename to lib/taskana-core-test/src/test/java/acceptance/task/requestreview/RequestReviewWithAfterSpiAccTest.java index c0685d049..d62f5f227 100644 --- a/lib/taskana-core-test/src/test/java/acceptance/task/requestreview/RequestReviewWithSpiAccTest.java +++ b/lib/taskana-core-test/src/test/java/acceptance/task/requestreview/RequestReviewWithAfterSpiAccTest.java @@ -38,7 +38,7 @@ import pro.taskana.workbasket.api.WorkbasketService; import pro.taskana.workbasket.api.models.WorkbasketSummary; @TaskanaIntegrationTest -public class RequestReviewWithSpiAccTest { +public class RequestReviewWithAfterSpiAccTest { private static final String NEW_WORKBASKET_KEY = "W1000"; ClassificationSummary defaultClassificationSummary; diff --git a/lib/taskana-core-test/src/test/java/acceptance/task/requestreview/RequestReviewWithBeforeSpiAccTest.java b/lib/taskana-core-test/src/test/java/acceptance/task/requestreview/RequestReviewWithBeforeSpiAccTest.java new file mode 100644 index 000000000..a88063425 --- /dev/null +++ b/lib/taskana-core-test/src/test/java/acceptance/task/requestreview/RequestReviewWithBeforeSpiAccTest.java @@ -0,0 +1,312 @@ +package acceptance.task.requestreview; + +import static org.assertj.core.api.Assertions.catchThrowableOfType; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import static pro.taskana.common.internal.util.CheckedSupplier.wrap; +import static pro.taskana.testapi.DefaultTestEntities.defaultTestClassification; +import static pro.taskana.testapi.DefaultTestEntities.defaultTestWorkbasket; + +import org.assertj.core.api.ThrowableAssert.ThrowingCallable; +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.TaskanaEngineConfiguration; +import pro.taskana.classification.api.ClassificationService; +import pro.taskana.classification.api.models.Classification; +import pro.taskana.classification.api.models.ClassificationSummary; +import pro.taskana.common.api.TaskanaEngine; +import pro.taskana.common.api.exceptions.SystemException; +import pro.taskana.common.internal.jobs.PlainJavaTransactionProvider; +import pro.taskana.spi.task.api.BeforeRequestReviewProvider; +import pro.taskana.task.api.TaskCustomField; +import pro.taskana.task.api.TaskService; +import pro.taskana.task.api.TaskState; +import pro.taskana.task.api.exceptions.InvalidOwnerException; +import pro.taskana.task.api.models.ObjectReference; +import pro.taskana.task.api.models.Task; +import pro.taskana.testapi.DefaultTestEntities; +import pro.taskana.testapi.TaskanaInject; +import pro.taskana.testapi.TaskanaIntegrationTest; +import pro.taskana.testapi.WithServiceProvider; +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 RequestReviewWithBeforeSpiAccTest { + + ClassificationSummary defaultClassificationSummary; + WorkbasketSummary defaultWorkbasketSummary; + ObjectReference defaultObjectReference; + + @WithAccessId(user = "businessadmin") + @BeforeAll + void setup(ClassificationService classificationService, WorkbasketService workbasketService) + throws Exception { + defaultClassificationSummary = + defaultTestClassification().buildAndStoreAsSummary(classificationService); + defaultWorkbasketSummary = defaultTestWorkbasket().buildAndStoreAsSummary(workbasketService); + + WorkbasketAccessItemBuilder.newWorkbasketAccessItem() + .workbasketId(defaultWorkbasketSummary.getId()) + .accessId("user-1-1") + .permission(WorkbasketPermission.READ) + .permission(WorkbasketPermission.APPEND) + .permission(WorkbasketPermission.TRANSFER) + .buildAndStore(workbasketService); + + defaultObjectReference = DefaultTestEntities.defaultTestObjectReference().build(); + } + + private TaskBuilder createTaskClaimedByUser(String owner) { + return createDefaultTask().owner(owner).state(TaskState.CLAIMED); + } + + private TaskBuilder createDefaultTask() { + return TaskBuilder.newTask() + .classificationSummary(defaultClassificationSummary) + .workbasketSummary(defaultWorkbasketSummary) + .primaryObjRef(defaultObjectReference); + } + + static class ExceptionThrower implements BeforeRequestReviewProvider { + + private TaskanaEngine taskanaEngine; + + @Override + public void initialize(TaskanaEngine taskanaEngine) { + this.taskanaEngine = taskanaEngine; + } + + @Override + public Task beforeRequestReview(Task task) throws Exception { + task.setDescription("should not matter. Will get rolled back anyway"); + taskanaEngine.getTaskService().updateTask(task); + throw new SystemException("I AM THE EXCEPTION THROWER (*_*)"); + } + } + + static class TaskModifier implements BeforeRequestReviewProvider { + private static final String NEW_CUSTOM_3_VALUE = "bla"; + + private TaskanaEngine taskanaEngine; + + @Override + public void initialize(TaskanaEngine taskanaEngine) { + this.taskanaEngine = taskanaEngine; + } + + @Override + public Task beforeRequestReview(Task task) throws Exception { + task.setCustomField(TaskCustomField.CUSTOM_3, NEW_CUSTOM_3_VALUE); + task = taskanaEngine.getTaskService().updateTask(task); + return task; + } + } + + static class ClassificationUpdater implements BeforeRequestReviewProvider { + + public static final String SPI_CLASSIFICATION_NAME = "Neuer Classification Name"; + + private TaskanaEngine taskanaEngine; + + @Override + public void initialize(TaskanaEngine taskanaEngine) { + this.taskanaEngine = taskanaEngine; + } + + @Override + public Task beforeRequestReview(Task task) throws Exception { + Classification newClassification = + defaultTestClassification().buildAndStore(taskanaEngine.getClassificationService()); + + task.setClassificationKey(newClassification.getKey()); + task = taskanaEngine.getTaskService().updateTask(task); + + newClassification.setName(SPI_CLASSIFICATION_NAME); + taskanaEngine.getClassificationService().updateClassification(newClassification); + + return task; + } + } + + static class SetTaskOwner implements BeforeRequestReviewProvider { + + TaskanaEngine taskanaEngine; + + @Override + public void initialize(TaskanaEngine taskanaEngine) { + this.taskanaEngine = taskanaEngine; + } + + @Override + public Task beforeRequestReview(Task task) throws Exception { + task.setOwner("new owner"); + return taskanaEngine.getTaskService().updateTask(task); + } + } + + @Nested + @TestInstance(Lifecycle.PER_CLASS) + @WithServiceProvider( + serviceProviderInterface = BeforeRequestReviewProvider.class, + serviceProviders = TaskModifier.class) + class SpiModifiesTask { + + @TaskanaInject TaskService taskService; + + @WithAccessId(user = "user-1-1") + @Test + void should_ReturnModifiedTask_When_SpiModifiesAndTransfersTask() throws Exception { + Task task = createTaskClaimedByUser("user-1-1").buildAndStore(taskService); + + Task result = taskService.requestReview(task.getId()); + + assertThat(result.getCustomField(TaskCustomField.CUSTOM_3)) + .isEqualTo(TaskModifier.NEW_CUSTOM_3_VALUE); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_ReturnPersistentModifiedTask_When_SpiModifiesAndTransfersTask() throws Exception { + Task task = createTaskClaimedByUser("user-1-1").buildAndStore(taskService); + + taskService.requestReview(task.getId()); + Task result = taskService.getTask(task.getId()); + + assertThat(result.getCustomField(TaskCustomField.CUSTOM_3)) + .isEqualTo(TaskModifier.NEW_CUSTOM_3_VALUE); + } + } + + @Nested + @TestInstance(Lifecycle.PER_CLASS) + @WithServiceProvider( + serviceProviderInterface = BeforeRequestReviewProvider.class, + serviceProviders = SetTaskOwner.class) + class SpiSetsValuesWhichGetOverridenByTaskana { + + @TaskanaInject TaskService taskService; + + @WithAccessId(user = "user-1-1") + @Test + void should_OverrideOwner_When_SpiModifiesOwner() throws Exception { + Task task = createTaskClaimedByUser("user-1-1").buildAndStore(taskService); + + taskService.forceRequestReview(task.getId()); + Task result = taskService.getTask(task.getId()); + + assertThat(result.getOwner()).isNull(); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_ThrowError_When_SpiModifiesOwner() throws Exception { + Task task = createTaskClaimedByUser("user-1-1").buildAndStore(taskService); + + ThrowingCallable call = () -> taskService.requestReview(task.getId()); + + InvalidOwnerException ex = catchThrowableOfType(call, InvalidOwnerException.class); + assertThat(ex.getTaskId()).isEqualTo(task.getId()); + assertThat(ex.getCurrentUserId()).isEqualTo("user-1-1"); + } + } + + @Nested + @TestInstance(Lifecycle.PER_CLASS) + @WithServiceProvider( + serviceProviderInterface = BeforeRequestReviewProvider.class, + serviceProviders = ClassificationUpdater.class) + class SpiModifiesTaskAndClassification { + + @TaskanaInject TaskService taskService; + + @WithAccessId(user = "user-1-1", groups = "businessadmin") + @Test + void should_ChangeMultipleEntities_When_SpiModifiesMoreThanTheTask() throws Exception { + Task task = createTaskClaimedByUser("user-1-1").buildAndStore(taskService); + + taskService.requestReview(task.getId()); + Task result = taskService.getTask(task.getId()); + + assertThat(result.getClassificationSummary().getId()) + .isNotEqualTo(task.getClassificationSummary().getId()); + assertThat(result.getClassificationSummary().getName()) + .isEqualTo(ClassificationUpdater.SPI_CLASSIFICATION_NAME); + } + } + + @Nested + @TestInstance(Lifecycle.PER_CLASS) + @WithServiceProvider( + serviceProviderInterface = BeforeRequestReviewProvider.class, + serviceProviders = {TaskModifier.class, ClassificationUpdater.class}) + class MultipleSpisAreDefined { + + @TaskanaInject TaskService taskService; + + @WithAccessId(user = "user-1-1", groups = "businessadmin") + @Test + void should_ApplyMultipleChanges_When_MultipleSpisAreChained() throws Exception { + Task task = createTaskClaimedByUser("user-1-1").buildAndStore(taskService); + + taskService.requestReview(task.getId()); + Task result = taskService.getTask(task.getId()); + + // changes from TaskModifier + assertThat(result.getCustomField(TaskCustomField.CUSTOM_3)) + .isEqualTo(TaskModifier.NEW_CUSTOM_3_VALUE); + // changes from ClassificationUpdater + assertThat(result.getClassificationSummary().getId()) + .isNotEqualTo(task.getClassificationSummary().getId()); + assertThat(result.getClassificationSummary().getName()) + .isEqualTo(ClassificationUpdater.SPI_CLASSIFICATION_NAME); + } + } + + @Nested + @TestInstance(Lifecycle.PER_CLASS) + @WithServiceProvider( + serviceProviderInterface = BeforeRequestReviewProvider.class, + serviceProviders = ExceptionThrower.class) + class SpiThrowsException { + @TaskanaInject TaskService taskService; + @TaskanaInject TaskanaEngine taskanaEngine; + PlainJavaTransactionProvider transactionProvider; + + @BeforeAll + void setup(TaskanaEngineConfiguration taskanaEngineConfiguration) { + transactionProvider = + new PlainJavaTransactionProvider( + taskanaEngine, taskanaEngineConfiguration.getDatasource()); + } + + @WithAccessId(user = "user-1-1") + @Test + void should_RollbackTransaction_When_SpiThrowsAnException() throws Exception { + Task task = createTaskClaimedByUser("user-1-1").buildAndStore(taskService); + + ThrowingCallable call = + () -> + transactionProvider.executeInTransaction( + wrap(() -> taskService.requestReview(task.getId()))); + + assertThatThrownBy(call) + .isInstanceOf(SystemException.class) + .getCause() // unwrap the "wrap" within "call" + .hasMessage("service provider '%s' threw an exception", ExceptionThrower.class.getName()) + .getCause() // unwrap the "wrap" from the service provider manager + .hasMessage("I AM THE EXCEPTION THROWER (*_*)"); + + Task persistentTask = taskService.getTask(task.getId()); + assertThat(persistentTask).isEqualTo(task); + } + } +} diff --git a/lib/taskana-core/src/main/java/pro/taskana/common/internal/InternalTaskanaEngine.java b/lib/taskana-core/src/main/java/pro/taskana/common/internal/InternalTaskanaEngine.java index 9d65256ce..6aba3cac1 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/common/internal/InternalTaskanaEngine.java +++ b/lib/taskana-core/src/main/java/pro/taskana/common/internal/InternalTaskanaEngine.java @@ -9,6 +9,7 @@ import pro.taskana.spi.priority.internal.PriorityServiceManager; import pro.taskana.spi.routing.internal.TaskRoutingManager; import pro.taskana.spi.task.internal.AfterRequestChangesManager; import pro.taskana.spi.task.internal.AfterRequestReviewManager; +import pro.taskana.spi.task.internal.BeforeRequestReviewManager; import pro.taskana.spi.task.internal.CreateTaskPreprocessorManager; import pro.taskana.spi.task.internal.ReviewRequiredManager; @@ -116,6 +117,13 @@ public interface InternalTaskanaEngine { */ ReviewRequiredManager getReviewRequiredManager(); + /** + * Retrieves the {@linkplain BeforeRequestReviewManager}. + * + * @return the {@linkplain BeforeRequestReviewManager} instance + */ + BeforeRequestReviewManager getBeforeRequestReviewManager(); + /** * Retrieves the {@linkplain AfterRequestReviewManager}. * diff --git a/lib/taskana-core/src/main/java/pro/taskana/common/internal/TaskanaEngineImpl.java b/lib/taskana-core/src/main/java/pro/taskana/common/internal/TaskanaEngineImpl.java index 616c4d427..af11f4959 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/common/internal/TaskanaEngineImpl.java +++ b/lib/taskana-core/src/main/java/pro/taskana/common/internal/TaskanaEngineImpl.java @@ -55,6 +55,7 @@ import pro.taskana.spi.priority.internal.PriorityServiceManager; import pro.taskana.spi.routing.internal.TaskRoutingManager; import pro.taskana.spi.task.internal.AfterRequestChangesManager; import pro.taskana.spi.task.internal.AfterRequestReviewManager; +import pro.taskana.spi.task.internal.BeforeRequestReviewManager; import pro.taskana.spi.task.internal.CreateTaskPreprocessorManager; import pro.taskana.spi.task.internal.ReviewRequiredManager; import pro.taskana.task.api.TaskService; @@ -86,6 +87,7 @@ public class TaskanaEngineImpl implements TaskanaEngine { private final CreateTaskPreprocessorManager createTaskPreprocessorManager; private final PriorityServiceManager priorityServiceManager; private final ReviewRequiredManager reviewRequiredManager; + private final BeforeRequestReviewManager beforeRequestReviewManager; private final AfterRequestReviewManager afterRequestReviewManager; private final AfterRequestChangesManager afterRequestChangesManager; @@ -125,6 +127,7 @@ public class TaskanaEngineImpl implements TaskanaEngine { historyEventManager = new HistoryEventManager(this); taskRoutingManager = new TaskRoutingManager(this); reviewRequiredManager = new ReviewRequiredManager(this); + beforeRequestReviewManager = new BeforeRequestReviewManager(this); afterRequestReviewManager = new AfterRequestReviewManager(this); afterRequestChangesManager = new AfterRequestChangesManager(this); } @@ -537,6 +540,11 @@ public class TaskanaEngineImpl implements TaskanaEngine { return reviewRequiredManager; } + @Override + public BeforeRequestReviewManager getBeforeRequestReviewManager() { + return beforeRequestReviewManager; + } + @Override public AfterRequestReviewManager getAfterRequestReviewManager() { return afterRequestReviewManager; diff --git a/lib/taskana-core/src/main/java/pro/taskana/spi/task/api/AfterRequestChangesProvider.java b/lib/taskana-core/src/main/java/pro/taskana/spi/task/api/AfterRequestChangesProvider.java index 7606d8750..b23a277fb 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/spi/task/api/AfterRequestChangesProvider.java +++ b/lib/taskana-core/src/main/java/pro/taskana/spi/task/api/AfterRequestChangesProvider.java @@ -36,8 +36,8 @@ public interface AfterRequestChangesProvider { * @param task the {@linkplain Task} after {@linkplain * pro.taskana.task.api.TaskService#requestChanges(String)} has completed * @return the modified {@linkplain Task}. IMPORTANT: persistent changes to the {@linkplain - * Task} have to be managed by the service provider. - * @throws Exception if the service provider throws any exception. + * Task} have to be managed by the service provider + * @throws Exception if the service provider throws any exception */ Task afterRequestChanges(Task task) throws Exception; } diff --git a/lib/taskana-core/src/main/java/pro/taskana/spi/task/api/BeforeRequestReviewProvider.java b/lib/taskana-core/src/main/java/pro/taskana/spi/task/api/BeforeRequestReviewProvider.java new file mode 100644 index 000000000..da66106f1 --- /dev/null +++ b/lib/taskana-core/src/main/java/pro/taskana/spi/task/api/BeforeRequestReviewProvider.java @@ -0,0 +1,43 @@ +package pro.taskana.spi.task.api; + +import pro.taskana.common.api.TaskanaEngine; +import pro.taskana.task.api.models.Task; + +/** + * The BeforeRequestReviewProvider allows to implement customized behaviour before a review has been + * requested on a given {@linkplain Task}. + */ +public interface BeforeRequestReviewProvider { + + /** + * Provide the active {@linkplain TaskanaEngine} which is initialized for this TASKANA + * installation. + * + *

This method is called during TASKANA startup and allows the service provider to store the + * active {@linkplain TaskanaEngine} for later usage. + * + * @param taskanaEngine the active {@linkplain TaskanaEngine} which is initialized for this + * installation + */ + void initialize(TaskanaEngine taskanaEngine); + + /** + * Perform any action before a review has been requested on a {@linkplain Task} through + * {@linkplain pro.taskana.task.api.TaskService#requestReview(String)}. + * + *

This SPI is executed within the same transaction staple as {@linkplain + * pro.taskana.task.api.TaskService#requestReview(String)}. + * + *

This SPI is executed with the same {@linkplain + * pro.taskana.common.api.security.UserPrincipal} and {@linkplain + * pro.taskana.common.api.security.GroupPrincipal} as in {@linkplain + * pro.taskana.task.api.TaskService#requestReview(String)}. + * + * @param task the {@linkplain Task} before {@linkplain + * pro.taskana.task.api.TaskService#requestReview(String)} has started + * @return the modified {@linkplain Task}. IMPORTANT: persistent changes to the {@linkplain + * Task} have to be managed by the service provider + * @throws Exception if the service provider throws any exception + */ + Task beforeRequestReview(Task task) throws Exception; +} diff --git a/lib/taskana-core/src/main/java/pro/taskana/spi/task/internal/AfterRequestChangesManager.java b/lib/taskana-core/src/main/java/pro/taskana/spi/task/internal/AfterRequestChangesManager.java index 50001ef4c..3511f3fc7 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/spi/task/internal/AfterRequestChangesManager.java +++ b/lib/taskana-core/src/main/java/pro/taskana/spi/task/internal/AfterRequestChangesManager.java @@ -21,7 +21,7 @@ public class AfterRequestChangesManager { for (AfterRequestChangesProvider serviceProvider : afterRequestChangesProviders) { serviceProvider.initialize(taskanaEngine); LOGGER.info( - "Registered AfterRequestChanges service provider: {}", + "Registered AfterRequestChangesProvider service provider: {}", serviceProvider.getClass().getName()); } if (afterRequestChangesProviders.isEmpty()) { diff --git a/lib/taskana-core/src/main/java/pro/taskana/spi/task/internal/AfterRequestReviewManager.java b/lib/taskana-core/src/main/java/pro/taskana/spi/task/internal/AfterRequestReviewManager.java index 56199df9f..a5432f6bb 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/spi/task/internal/AfterRequestReviewManager.java +++ b/lib/taskana-core/src/main/java/pro/taskana/spi/task/internal/AfterRequestReviewManager.java @@ -21,7 +21,7 @@ public class AfterRequestReviewManager { for (AfterRequestReviewProvider serviceProvider : afterRequestReviewProviders) { serviceProvider.initialize(taskanaEngine); LOGGER.info( - "Registered AfterRequestReview service provider: {}", + "Registered AfterRequestReviewProvider service provider: {}", serviceProvider.getClass().getName()); } if (afterRequestReviewProviders.isEmpty()) { diff --git a/lib/taskana-core/src/main/java/pro/taskana/spi/task/internal/BeforeRequestReviewManager.java b/lib/taskana-core/src/main/java/pro/taskana/spi/task/internal/BeforeRequestReviewManager.java new file mode 100644 index 000000000..1c133bc4b --- /dev/null +++ b/lib/taskana-core/src/main/java/pro/taskana/spi/task/internal/BeforeRequestReviewManager.java @@ -0,0 +1,50 @@ +package pro.taskana.spi.task.internal; + +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import pro.taskana.common.api.TaskanaEngine; +import pro.taskana.common.api.exceptions.SystemException; +import pro.taskana.common.internal.util.SpiLoader; +import pro.taskana.spi.task.api.BeforeRequestReviewProvider; +import pro.taskana.task.api.models.Task; + +public class BeforeRequestReviewManager { + + private static final Logger LOGGER = LoggerFactory.getLogger(BeforeRequestReviewManager.class); + + private final List beforeRequestReviewProviders; + + public BeforeRequestReviewManager(TaskanaEngine taskanaEngine) { + beforeRequestReviewProviders = SpiLoader.load(BeforeRequestReviewProvider.class); + for (BeforeRequestReviewProvider serviceProvider : beforeRequestReviewProviders) { + serviceProvider.initialize(taskanaEngine); + LOGGER.info( + "Registered BeforeRequestReviewProvider service provider: {}", + serviceProvider.getClass().getName()); + } + if (beforeRequestReviewProviders.isEmpty()) { + LOGGER.info( + "No BeforeRequestReviewProvider service provider found. " + + "Running without any BeforeRequestReviewProvider implementation."); + } + } + + public Task beforeRequestReview(Task task) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Sending Task to BeforeRequestReview service providers: {}", task); + } + for (BeforeRequestReviewProvider serviceProvider : beforeRequestReviewProviders) { + try { + task = serviceProvider.beforeRequestReview(task); + } catch (Exception e) { + throw new SystemException( + String.format( + "service provider '%s' threw an exception", serviceProvider.getClass().getName()), + e); + } + } + return task; + } +} diff --git a/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskServiceImpl.java b/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskServiceImpl.java index 358c8e079..0ecdc9cd0 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskServiceImpl.java +++ b/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskServiceImpl.java @@ -55,6 +55,7 @@ import pro.taskana.spi.history.internal.HistoryEventManager; import pro.taskana.spi.priority.internal.PriorityServiceManager; import pro.taskana.spi.task.internal.AfterRequestChangesManager; import pro.taskana.spi.task.internal.AfterRequestReviewManager; +import pro.taskana.spi.task.internal.BeforeRequestReviewManager; import pro.taskana.spi.task.internal.CreateTaskPreprocessorManager; import pro.taskana.spi.task.internal.ReviewRequiredManager; import pro.taskana.task.api.CallbackState; @@ -118,6 +119,7 @@ public class TaskServiceImpl implements TaskService { private final CreateTaskPreprocessorManager createTaskPreprocessorManager; private final PriorityServiceManager priorityServiceManager; private final ReviewRequiredManager reviewRequiredManager; + private final BeforeRequestReviewManager beforeRequestReviewManager; private final AfterRequestReviewManager afterRequestReviewManager; private final AfterRequestChangesManager afterRequestChangesManager; @@ -139,6 +141,7 @@ public class TaskServiceImpl implements TaskService { this.createTaskPreprocessorManager = taskanaEngine.getCreateTaskPreprocessorManager(); this.priorityServiceManager = taskanaEngine.getPriorityServiceManager(); this.reviewRequiredManager = taskanaEngine.getReviewRequiredManager(); + this.beforeRequestReviewManager = taskanaEngine.getBeforeRequestReviewManager(); this.afterRequestReviewManager = taskanaEngine.getAfterRequestReviewManager(); this.afterRequestChangesManager = taskanaEngine.getAfterRequestChangesManager(); this.taskTransferrer = new TaskTransferrer(taskanaEngine, taskMapper, this); @@ -1236,6 +1239,7 @@ public class TaskServiceImpl implements TaskService { try { taskanaEngine.openConnection(); task = (TaskImpl) getTask(taskId); + task = (TaskImpl) beforeRequestReviewManager.beforeRequestReview(task); TaskImpl oldTask = duplicateTaskExactly(task); diff --git a/lib/taskana-test-api/src/main/java/pro/taskana/testapi/util/ServiceProviderExtractor.java b/lib/taskana-test-api/src/main/java/pro/taskana/testapi/util/ServiceProviderExtractor.java index 893cb53a7..da4300551 100644 --- a/lib/taskana-test-api/src/main/java/pro/taskana/testapi/util/ServiceProviderExtractor.java +++ b/lib/taskana-test-api/src/main/java/pro/taskana/testapi/util/ServiceProviderExtractor.java @@ -17,6 +17,7 @@ import pro.taskana.spi.priority.api.PriorityServiceProvider; import pro.taskana.spi.routing.api.TaskRoutingProvider; import pro.taskana.spi.task.api.AfterRequestChangesProvider; import pro.taskana.spi.task.api.AfterRequestReviewProvider; +import pro.taskana.spi.task.api.BeforeRequestReviewProvider; import pro.taskana.spi.task.api.CreateTaskPreprocessor; import pro.taskana.spi.task.api.ReviewRequiredProvider; import pro.taskana.testapi.WithServiceProvider; @@ -30,6 +31,7 @@ public class ServiceProviderExtractor { TaskRoutingProvider.class, CreateTaskPreprocessor.class, ReviewRequiredProvider.class, + BeforeRequestReviewProvider.class, AfterRequestReviewProvider.class, AfterRequestChangesProvider.class);