TSK-1941: implemented ReviewRequired SPI
This commit is contained in:
parent
830723800e
commit
cf690bf6b5
|
@ -0,0 +1,144 @@
|
|||
package acceptance.task.complete;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static pro.taskana.testapi.DefaultTestEntities.defaultTestClassification;
|
||||
import static pro.taskana.testapi.DefaultTestEntities.defaultTestWorkbasket;
|
||||
|
||||
import java.time.Instant;
|
||||
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.classification.api.ClassificationService;
|
||||
import pro.taskana.classification.api.models.ClassificationSummary;
|
||||
import pro.taskana.spi.task.api.ReviewRequiredProvider;
|
||||
import pro.taskana.task.api.TaskService;
|
||||
import pro.taskana.task.api.TaskState;
|
||||
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
|
||||
class CompleteTaskWithSpiAccTest {
|
||||
|
||||
@TaskanaInject TaskService taskService;
|
||||
|
||||
ClassificationSummary defaultClassificationSummary;
|
||||
WorkbasketSummary defaultWorkbasketSummary;
|
||||
ObjectReference defaultObjectReference;
|
||||
|
||||
@SuppressWarnings("JUnitMalformedDeclaration")
|
||||
@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)
|
||||
.buildAndStore(workbasketService);
|
||||
|
||||
defaultObjectReference = DefaultTestEntities.defaultTestObjectReference().build();
|
||||
}
|
||||
|
||||
private TaskBuilder createTaskClaimedByUser(String user) {
|
||||
return createDefaultTask().owner(user).state(TaskState.CLAIMED).claimed(Instant.now());
|
||||
}
|
||||
|
||||
private TaskBuilder createDefaultTask() {
|
||||
return TaskBuilder.newTask()
|
||||
.classificationSummary(defaultClassificationSummary)
|
||||
.workbasketSummary(defaultWorkbasketSummary)
|
||||
.primaryObjRef(defaultObjectReference);
|
||||
}
|
||||
|
||||
static class AlwaysRequireReview implements ReviewRequiredProvider {
|
||||
@Override
|
||||
public boolean reviewRequired(Task task) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static class NeverRequireReview implements ReviewRequiredProvider {
|
||||
@Override
|
||||
public boolean reviewRequired(Task task) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestInstance(Lifecycle.PER_CLASS)
|
||||
@WithServiceProvider(
|
||||
serviceProviderInterface = ReviewRequiredProvider.class,
|
||||
serviceProviders = AlwaysRequireReview.class)
|
||||
class ServiceProviderAlwaysRequiringReview {
|
||||
|
||||
@TaskanaInject TaskService taskService;
|
||||
|
||||
@WithAccessId(user = "user-1-1")
|
||||
@Test
|
||||
void should_RequireReview_When_UserTriesToCompleteTask() throws Exception {
|
||||
Task task = createTaskClaimedByUser("user-1-1").buildAndStore(taskService);
|
||||
|
||||
Task processedTask = taskService.completeTask(task.getId());
|
||||
|
||||
assertThat(processedTask.getState()).isEqualTo(TaskState.READY_FOR_REVIEW);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestInstance(Lifecycle.PER_CLASS)
|
||||
@WithServiceProvider(
|
||||
serviceProviderInterface = ReviewRequiredProvider.class,
|
||||
serviceProviders = NeverRequireReview.class)
|
||||
class ServiceProviderNeverRequiringReview {
|
||||
|
||||
@TaskanaInject TaskService taskService;
|
||||
|
||||
@WithAccessId(user = "user-1-1")
|
||||
@Test
|
||||
void should_CompleteTask_When_UserTriesToCompleteTask() throws Exception {
|
||||
Task task = createTaskClaimedByUser("user-1-1").buildAndStore(taskService);
|
||||
|
||||
Task processedTask = taskService.completeTask(task.getId());
|
||||
|
||||
assertThat(processedTask.getState()).isEqualTo(TaskState.COMPLETED);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestInstance(Lifecycle.PER_CLASS)
|
||||
@WithServiceProvider(
|
||||
serviceProviderInterface = ReviewRequiredProvider.class,
|
||||
serviceProviders = {AlwaysRequireReview.class, NeverRequireReview.class})
|
||||
class ServiceProvidersAlwaysAndNeverRequiringReview {
|
||||
|
||||
@TaskanaInject TaskService taskService;
|
||||
|
||||
@WithAccessId(user = "user-1-1")
|
||||
@Test
|
||||
void should_RequestReview_When_UserTriesToCompleteTask() throws Exception {
|
||||
Task task = createTaskClaimedByUser("user-1-1").buildAndStore(taskService);
|
||||
|
||||
Task processedTask = taskService.completeTask(task.getId());
|
||||
|
||||
assertThat(processedTask.getState()).isEqualTo(TaskState.READY_FOR_REVIEW);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -88,11 +88,17 @@ public class RequestChangesWithSpiAccTest {
|
|||
|
||||
static class ExceptionThrower implements AfterRequestChangesProvider {
|
||||
|
||||
@Override
|
||||
public void initialize(TaskanaEngine taskanaEngine) {}
|
||||
private TaskanaEngine taskanaEngine;
|
||||
|
||||
@Override
|
||||
public Task afterRequestChanges(Task task) {
|
||||
public void initialize(TaskanaEngine taskanaEngine) {
|
||||
this.taskanaEngine = taskanaEngine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task afterRequestChanges(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 (*_*)");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,11 +88,17 @@ public class RequestReviewWithSpiAccTest {
|
|||
|
||||
static class ExceptionThrower implements AfterRequestReviewProvider {
|
||||
|
||||
@Override
|
||||
public void initialize(TaskanaEngine taskanaEngine) {}
|
||||
private TaskanaEngine taskanaEngine;
|
||||
|
||||
@Override
|
||||
public Task afterRequestReview(Task task) {
|
||||
public void initialize(TaskanaEngine taskanaEngine) {
|
||||
this.taskanaEngine = taskanaEngine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task afterRequestReview(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 (*_*)");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ 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.CreateTaskPreprocessorManager;
|
||||
import pro.taskana.spi.task.internal.ReviewRequiredManager;
|
||||
|
||||
/**
|
||||
* FOR INTERNAL USE ONLY.
|
||||
|
@ -108,6 +109,13 @@ public interface InternalTaskanaEngine {
|
|||
*/
|
||||
PriorityServiceManager getPriorityServiceManager();
|
||||
|
||||
/**
|
||||
* Retrieves the {@linkplain ReviewRequiredManager}.
|
||||
*
|
||||
* @return the {@linkplain ReviewRequiredManager} instance
|
||||
*/
|
||||
ReviewRequiredManager getReviewRequiredManager();
|
||||
|
||||
/**
|
||||
* Retrieves the {@linkplain AfterRequestReviewManager}.
|
||||
*
|
||||
|
|
|
@ -56,6 +56,7 @@ 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.CreateTaskPreprocessorManager;
|
||||
import pro.taskana.spi.task.internal.ReviewRequiredManager;
|
||||
import pro.taskana.task.api.TaskService;
|
||||
import pro.taskana.task.internal.AttachmentMapper;
|
||||
import pro.taskana.task.internal.ObjectReferenceMapper;
|
||||
|
@ -84,6 +85,7 @@ public class TaskanaEngineImpl implements TaskanaEngine {
|
|||
private final TaskRoutingManager taskRoutingManager;
|
||||
private final CreateTaskPreprocessorManager createTaskPreprocessorManager;
|
||||
private final PriorityServiceManager priorityServiceManager;
|
||||
private final ReviewRequiredManager reviewRequiredManager;
|
||||
private final AfterRequestReviewManager afterRequestReviewManager;
|
||||
private final AfterRequestChangesManager afterRequestChangesManager;
|
||||
|
||||
|
@ -122,6 +124,7 @@ public class TaskanaEngineImpl implements TaskanaEngine {
|
|||
createTaskPreprocessorManager = new CreateTaskPreprocessorManager();
|
||||
historyEventManager = new HistoryEventManager(this);
|
||||
taskRoutingManager = new TaskRoutingManager(this);
|
||||
reviewRequiredManager = new ReviewRequiredManager(this);
|
||||
afterRequestReviewManager = new AfterRequestReviewManager(this);
|
||||
afterRequestChangesManager = new AfterRequestChangesManager(this);
|
||||
}
|
||||
|
@ -529,6 +532,11 @@ public class TaskanaEngineImpl implements TaskanaEngine {
|
|||
return priorityServiceManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReviewRequiredManager getReviewRequiredManager() {
|
||||
return reviewRequiredManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AfterRequestReviewManager getAfterRequestReviewManager() {
|
||||
return afterRequestReviewManager;
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
package pro.taskana.spi.task.api;
|
||||
|
||||
import pro.taskana.common.api.TaskanaEngine;
|
||||
import pro.taskana.task.api.models.Task;
|
||||
|
||||
/**
|
||||
* The ReviewRequiredProvider allows to determine whether a {@linkplain Task} requires a review
|
||||
* instead of completion.
|
||||
*/
|
||||
public interface ReviewRequiredProvider {
|
||||
|
||||
/**
|
||||
* Provide the active {@linkplain TaskanaEngine} which is initialized for this TASKANA
|
||||
* installation.
|
||||
*
|
||||
* <p>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
|
||||
*/
|
||||
default void initialize(TaskanaEngine taskanaEngine) {}
|
||||
|
||||
/**
|
||||
* Determine if a {@linkplain Task} has to be reviewed instead of completed before {@linkplain
|
||||
* pro.taskana.task.api.TaskService#completeTask(String)} is executed.
|
||||
*
|
||||
* <p>The behaviour is undefined if this method tries to apply persistent changes to any entity.
|
||||
*
|
||||
* <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 {@linkplain
|
||||
* pro.taskana.task.api.TaskService#completeTask(String)}.
|
||||
*
|
||||
* @param task the {@linkplain Task} before {@linkplain
|
||||
* pro.taskana.task.api.TaskService#completeTask(String)} has started
|
||||
* @return true, if {@linkplain pro.taskana.task.api.TaskService#requestReview(String)} should be
|
||||
* executed instead of {@linkplain pro.taskana.task.api.TaskService#completeTask(String)}.
|
||||
* False, if {@linkplain pro.taskana.task.api.TaskService#completeTask(String)} can be
|
||||
* executed regularly
|
||||
*/
|
||||
boolean reviewRequired(Task task);
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
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.internal.util.SpiLoader;
|
||||
import pro.taskana.spi.task.api.ReviewRequiredProvider;
|
||||
import pro.taskana.task.api.models.Task;
|
||||
|
||||
public class ReviewRequiredManager {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ReviewRequiredManager.class);
|
||||
|
||||
private final List<ReviewRequiredProvider> reviewRequiredProviders;
|
||||
|
||||
public ReviewRequiredManager(TaskanaEngine taskanaEngine) {
|
||||
reviewRequiredProviders = SpiLoader.load(ReviewRequiredProvider.class);
|
||||
for (ReviewRequiredProvider serviceProvider : reviewRequiredProviders) {
|
||||
serviceProvider.initialize(taskanaEngine);
|
||||
LOGGER.info(
|
||||
"Registered ReviewRequiredProvider service provider: {}",
|
||||
serviceProvider.getClass().getName());
|
||||
}
|
||||
if (reviewRequiredProviders.isEmpty()) {
|
||||
LOGGER.info(
|
||||
"No ReviewRequiredProvider service provider found. "
|
||||
+ "Running without any ReviewRequiredProvider implementation.");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean reviewRequired(Task task) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Sending Task to ReviewRequiredProvider service providers: {}", task);
|
||||
}
|
||||
|
||||
return reviewRequiredProviders.stream()
|
||||
.anyMatch(serviceProvider -> serviceProvider.reviewRequired(task));
|
||||
}
|
||||
}
|
|
@ -56,6 +56,7 @@ 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.CreateTaskPreprocessorManager;
|
||||
import pro.taskana.spi.task.internal.ReviewRequiredManager;
|
||||
import pro.taskana.task.api.CallbackState;
|
||||
import pro.taskana.task.api.TaskCommentQuery;
|
||||
import pro.taskana.task.api.TaskCustomField;
|
||||
|
@ -116,6 +117,7 @@ public class TaskServiceImpl implements TaskService {
|
|||
private final HistoryEventManager historyEventManager;
|
||||
private final CreateTaskPreprocessorManager createTaskPreprocessorManager;
|
||||
private final PriorityServiceManager priorityServiceManager;
|
||||
private final ReviewRequiredManager reviewRequiredManager;
|
||||
private final AfterRequestReviewManager afterRequestReviewManager;
|
||||
private final AfterRequestChangesManager afterRequestChangesManager;
|
||||
|
||||
|
@ -136,6 +138,7 @@ public class TaskServiceImpl implements TaskService {
|
|||
this.historyEventManager = taskanaEngine.getHistoryEventManager();
|
||||
this.createTaskPreprocessorManager = taskanaEngine.getCreateTaskPreprocessorManager();
|
||||
this.priorityServiceManager = taskanaEngine.getPriorityServiceManager();
|
||||
this.reviewRequiredManager = taskanaEngine.getReviewRequiredManager();
|
||||
this.afterRequestReviewManager = taskanaEngine.getAfterRequestReviewManager();
|
||||
this.afterRequestChangesManager = taskanaEngine.getAfterRequestChangesManager();
|
||||
this.taskTransferrer = new TaskTransferrer(taskanaEngine, taskMapper, this);
|
||||
|
@ -1448,10 +1451,12 @@ public class TaskServiceImpl implements TaskService {
|
|||
throws TaskNotFoundException, InvalidOwnerException, InvalidStateException,
|
||||
NotAuthorizedException {
|
||||
String userId = taskanaEngine.getEngine().getCurrentUserContext().getUserid();
|
||||
TaskImpl task;
|
||||
TaskImpl task = (TaskImpl) this.getTask(taskId);
|
||||
if (reviewRequiredManager.reviewRequired(task)) {
|
||||
return requestReview(taskId);
|
||||
}
|
||||
try {
|
||||
taskanaEngine.openConnection();
|
||||
task = (TaskImpl) this.getTask(taskId);
|
||||
|
||||
if (task.getState() == TaskState.COMPLETED) {
|
||||
return task;
|
||||
|
|
|
@ -18,6 +18,7 @@ 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.CreateTaskPreprocessor;
|
||||
import pro.taskana.spi.task.api.ReviewRequiredProvider;
|
||||
import pro.taskana.testapi.WithServiceProvider;
|
||||
|
||||
public class ServiceProviderExtractor {
|
||||
|
@ -28,6 +29,7 @@ public class ServiceProviderExtractor {
|
|||
PriorityServiceProvider.class,
|
||||
TaskRoutingProvider.class,
|
||||
CreateTaskPreprocessor.class,
|
||||
ReviewRequiredProvider.class,
|
||||
AfterRequestReviewProvider.class,
|
||||
AfterRequestChangesProvider.class);
|
||||
|
||||
|
|
Loading…
Reference in New Issue