TSK-1704: test API now restricting service providers to specific test

This commit is contained in:
Mustapha Zorgati 2021-09-20 13:35:11 +02:00
parent d61fc1f56d
commit fb4234b6f1
41 changed files with 729 additions and 536 deletions

View File

@ -18,6 +18,8 @@ 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.extension.ExtendWith;
@ -702,6 +704,7 @@ class JaasExtensionTest {
// region JaasExtension#interceptTestClassConstructor
@Nested
@TestInstance(Lifecycle.PER_CLASS)
class ConstructorWithoutAccessId {
ConstructorWithoutAccessId() {
assertThat(CURRENT_USER_CONTEXT.getUserid()).isNull();
@ -714,6 +717,7 @@ class JaasExtensionTest {
}
@Nested
@TestInstance(Lifecycle.PER_CLASS)
class ConstructorWithAccessId {
@WithAccessId(user = "constructor")
ConstructorWithAccessId() {

View File

@ -0,0 +1,18 @@
package pro.taskana.common.internal.util;
import java.util.List;
import java.util.ServiceLoader;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
public class SpiLoader {
private SpiLoader() {
throw new IllegalStateException("Utility class");
}
public static <T> List<T> load(Class<T> clazz) {
ServiceLoader<T> serviceLoader = ServiceLoader.load(clazz);
return StreamSupport.stream(serviceLoader.spliterator(), false).collect(Collectors.toList());
}
}

View File

@ -0,0 +1,22 @@
package pro.taskana.common.internal.util;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.List;
import org.junit.jupiter.api.Test;
import pro.taskana.common.internal.util.spi.ServiceProviderInterface;
class SpiLoaderTest {
@Test
void should_loadServiceProviders() {
List<ServiceProviderInterface> serviceProviders =
SpiLoader.load(ServiceProviderInterface.class);
assertThat(serviceProviders)
.isNotEmpty()
.extracting(ServiceProviderInterface::doStuff)
.containsExactly("doing Stuff");
}
}

View File

@ -0,0 +1,5 @@
package pro.taskana.common.internal.util.spi;
public interface ServiceProviderInterface {
String doStuff();
}

View File

@ -0,0 +1,9 @@
package pro.taskana.common.internal.util.spi;
public class ServiceProviderInterfaceImpl implements ServiceProviderInterface {
@Override
public String doStuff() {
return "doing Stuff";
}
}

View File

@ -0,0 +1 @@
pro.taskana.common.internal.util.spi.ServiceProviderInterfaceImpl

View File

@ -46,15 +46,18 @@ public class ClassificationServiceImpl implements ClassificationService {
private static final Logger LOGGER = LoggerFactory.getLogger(ClassificationServiceImpl.class);
private final HistoryEventManager historyEventManager;
private final PriorityServiceManager priorityServiceManager;
private final ClassificationMapper classificationMapper;
private final TaskMapper taskMapper;
private final InternalTaskanaEngine taskanaEngine;
public ClassificationServiceImpl(
InternalTaskanaEngine taskanaEngine,
PriorityServiceManager priorityServiceManager,
ClassificationMapper classificationMapper,
TaskMapper taskMapper) {
this.taskanaEngine = taskanaEngine;
this.priorityServiceManager = priorityServiceManager;
this.classificationMapper = classificationMapper;
this.taskMapper = taskMapper;
this.historyEventManager = taskanaEngine.getHistoryEventManager();
@ -132,7 +135,7 @@ public class ClassificationServiceImpl implements ClassificationService {
try {
this.classificationMapper.deleteClassification(classificationId);
if (HistoryEventManager.isHistoryEnabled()) {
if (historyEventManager.isEnabled()) {
String details =
ObjectAttributeChangeDetector.determineChangesInAttributes(
classification, newClassification("", "", ""));
@ -201,7 +204,7 @@ public class ClassificationServiceImpl implements ClassificationService {
classificationMapper.insert(classificationImpl);
if (HistoryEventManager.isHistoryEnabled()) {
if (historyEventManager.isEnabled()) {
String details =
ObjectAttributeChangeDetector.determineChangesInAttributes(
newClassification("", "", ""), classificationImpl);
@ -255,11 +258,11 @@ public class ClassificationServiceImpl implements ClassificationService {
this.checkExistenceOfParentClassification(oldClassification, classificationImpl);
classificationMapper.update(classificationImpl);
if (!PriorityServiceManager.isPriorityServiceEnabled()) {
if (!priorityServiceManager.isEnabled()) {
this.createJobIfPriorityOrServiceLevelHasChanged(oldClassification, classificationImpl);
}
if (HistoryEventManager.isHistoryEnabled()) {
if (historyEventManager.isEnabled()) {
String details =
ObjectAttributeChangeDetector.determineChangesInAttributes(
oldClassification, classificationImpl);

View File

@ -97,7 +97,6 @@ public class TaskanaEngineImpl implements TaskanaEngine {
createTransactionFactory(taskanaEngineConfiguration.getUseManagedTransactions());
this.sessionManager = createSqlSessionManager();
initializeDbSchema(taskanaEngineConfiguration);
createTaskPreprocessorManager = CreateTaskPreprocessorManager.getInstance();
this.internalTaskanaEngineImpl = new InternalTaskanaEngineImpl();
workingDaysToDaysConverter =
new WorkingDaysToDaysConverter(
@ -109,9 +108,10 @@ public class TaskanaEngineImpl implements TaskanaEngine {
// IMPORTANT: SPI has to be initialized last (and in this order) in order
// to provide a fully initialized TaskanaEngine instance during the SPI initialization!
historyEventManager = HistoryEventManager.getInstance(this);
taskRoutingManager = TaskRoutingManager.getInstance(this);
priorityServiceManager = PriorityServiceManager.getInstance();
historyEventManager = new HistoryEventManager(this);
taskRoutingManager = new TaskRoutingManager(this);
priorityServiceManager = new PriorityServiceManager();
createTaskPreprocessorManager = new CreateTaskPreprocessorManager();
}
public static TaskanaEngine createTaskanaEngine(
@ -138,6 +138,7 @@ public class TaskanaEngineImpl implements TaskanaEngine {
public WorkbasketService getWorkbasketService() {
return new WorkbasketServiceImpl(
internalTaskanaEngineImpl,
historyEventManager,
sessionManager.getMapper(WorkbasketMapper.class),
sessionManager.getMapper(DistributionTargetMapper.class),
sessionManager.getMapper(WorkbasketAccessMapper.class));
@ -147,10 +148,18 @@ public class TaskanaEngineImpl implements TaskanaEngine {
public ClassificationService getClassificationService() {
return new ClassificationServiceImpl(
internalTaskanaEngineImpl,
priorityServiceManager,
sessionManager.getMapper(ClassificationMapper.class),
sessionManager.getMapper(TaskMapper.class));
}
// This should be part of the InternalTaskanaEngine. Unfortunately the jobs don't have access to
// that engine.
// Therefore, this getter exits and will be removed as soon as our jobs will be refactored.
public PriorityServiceManager getPriorityServiceManager() {
return priorityServiceManager;
}
@Override
public JobService getJobService() {
return new JobServiceImpl(internalTaskanaEngineImpl, sessionManager.getMapper(JobMapper.class));
@ -174,7 +183,7 @@ public class TaskanaEngineImpl implements TaskanaEngine {
@Override
public boolean isHistoryEnabled() {
return HistoryEventManager.isHistoryEnabled();
return historyEventManager.isEnabled();
}
@Override

View File

@ -1,13 +1,12 @@
package pro.taskana.spi.history.internal;
import java.util.List;
import java.util.Objects;
import java.util.ServiceLoader;
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.CheckedConsumer;
import pro.taskana.common.internal.util.SpiLoader;
import pro.taskana.spi.history.api.TaskanaHistory;
import pro.taskana.spi.history.api.events.classification.ClassificationHistoryEvent;
import pro.taskana.spi.history.api.events.task.TaskHistoryEvent;
@ -17,107 +16,55 @@ import pro.taskana.spi.history.api.events.workbasket.WorkbasketHistoryEvent;
public final class HistoryEventManager {
private static final Logger LOGGER = LoggerFactory.getLogger(HistoryEventManager.class);
private static HistoryEventManager singleton;
private final ServiceLoader<TaskanaHistory> serviceLoader;
private boolean enabled = false;
private final List<TaskanaHistory> taskanaHistories;
private HistoryEventManager(TaskanaEngine taskanaEngine) {
serviceLoader = ServiceLoader.load(TaskanaHistory.class);
for (TaskanaHistory history : serviceLoader) {
public HistoryEventManager(TaskanaEngine taskanaEngine) {
taskanaHistories = SpiLoader.load(TaskanaHistory.class);
for (TaskanaHistory history : taskanaHistories) {
history.initialize(taskanaEngine);
LOGGER.info("Registered history provider: {}", history.getClass().getName());
enabled = true;
}
if (!enabled) {
LOGGER.info("No history provider found. Running without history.");
if (taskanaHistories.isEmpty()) {
LOGGER.info("No TaskanaHistory provider found. Running without History.");
}
}
public static synchronized HistoryEventManager getInstance(TaskanaEngine taskanaEngine) {
if (singleton == null) {
singleton = new HistoryEventManager(taskanaEngine);
}
return singleton;
}
public static boolean isHistoryEnabled() {
return Objects.nonNull(singleton) && singleton.enabled;
public boolean isEnabled() {
return !taskanaHistories.isEmpty();
}
public void createEvent(TaskHistoryEvent event) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Sending event to history service providers: {}", event);
}
serviceLoader.forEach(
historyProvider -> {
try {
historyProvider.create(event);
} catch (Exception e) {
LOGGER.error(
String.format(
"Caught an exception while trying to create TaskHistoryEvent in class %s",
historyProvider.getClass().getName()),
e);
throw new SystemException(e.getMessage(), e.getCause());
}
});
taskanaHistories.forEach(
CheckedConsumer.wrap(historyProvider -> historyProvider.create(event)));
}
public void createEvent(WorkbasketHistoryEvent event) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Sending event to history service providers: {}", event);
}
serviceLoader.forEach(
historyProvider -> {
try {
historyProvider.create(event);
} catch (Exception e) {
LOGGER.error(
String.format(
"Caught an exception while trying to create WorkbasketHistoryEvent in class %s",
historyProvider.getClass().getName()),
e);
throw new SystemException(e.getMessage(), e.getCause());
}
});
taskanaHistories.forEach(
CheckedConsumer.wrap(historyProvider -> historyProvider.create(event)));
}
public void createEvent(ClassificationHistoryEvent event) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Sending event to history service providers: {}", event);
}
serviceLoader.forEach(
historyProvider -> {
try {
historyProvider.create(event);
} catch (Exception e) {
LOGGER.error(
String.format(
"Caught an exception while trying to create "
+ "ClassificationHistoryEvent in class %s",
historyProvider.getClass().getName()),
e);
throw new SystemException(e.getMessage(), e.getCause());
}
});
taskanaHistories.forEach(
CheckedConsumer.wrap(historyProvider -> historyProvider.create(event)));
}
public void deleteEvents(List<String> taskIds) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Sending taskIds to history service providers: {}", taskIds);
}
serviceLoader.forEach(
historyProvider -> {
try {
historyProvider.deleteHistoryEventsByTaskIds(taskIds);
} catch (Exception e) {
LOGGER.error(
String.format(
"Caught an exception while trying to delete HistoryEvents in class %s",
historyProvider.getClass().getName()),
e);
throw new SystemException(e.getMessage(), e.getCause());
}
});
taskanaHistories.forEach(
CheckedConsumer.wrap(
historyProvider -> historyProvider.deleteHistoryEventsByTaskIds(taskIds)));
}
}

View File

@ -1,6 +1,6 @@
package pro.taskana.spi.priority.api;
import java.util.Optional;
import java.util.OptionalInt;
import pro.taskana.task.api.models.Task;
import pro.taskana.task.api.models.TaskSummary;
@ -21,5 +21,5 @@ public interface PriorityServiceProvider {
* priority} for
* @return the computed {@linkplain Task#getPriority() priority}
*/
Optional<Integer> calculatePriority(TaskSummary taskSummary);
OptionalInt calculatePriority(TaskSummary taskSummary);
}

View File

@ -1,88 +1,57 @@
package pro.taskana.spi.priority.internal;
import static pro.taskana.common.internal.util.CheckedFunction.wrap;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.OptionalInt;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pro.taskana.common.api.exceptions.SystemException;
import pro.taskana.common.internal.util.LogSanitizer;
import pro.taskana.common.internal.util.SpiLoader;
import pro.taskana.spi.priority.api.PriorityServiceProvider;
import pro.taskana.task.api.models.TaskSummary;
public class PriorityServiceManager {
private static final Logger LOGGER = LoggerFactory.getLogger(PriorityServiceManager.class);
private static PriorityServiceManager singleton;
private final ServiceLoader<PriorityServiceProvider> serviceLoader;
private boolean enabled = false;
private final List<PriorityServiceProvider> priorityServiceProviders;
private PriorityServiceManager() {
serviceLoader = ServiceLoader.load(PriorityServiceProvider.class);
for (PriorityServiceProvider priorityProvider : serviceLoader) {
public PriorityServiceManager() {
priorityServiceProviders = SpiLoader.load(PriorityServiceProvider.class);
for (PriorityServiceProvider priorityProvider : priorityServiceProviders) {
LOGGER.info("Registered PriorityServiceProvider: {}", priorityProvider.getClass().getName());
enabled = true;
}
if (!enabled) {
if (priorityServiceProviders.isEmpty()) {
LOGGER.info("No PriorityServiceProvider found. Running without PriorityServiceProvider.");
}
}
public static synchronized PriorityServiceManager getInstance() {
if (singleton == null) {
singleton = new PriorityServiceManager();
}
return singleton;
public boolean isEnabled() {
return !priorityServiceProviders.isEmpty();
}
public static boolean isPriorityServiceEnabled() {
return Objects.nonNull(singleton) && singleton.enabled;
}
public long countRegisteredServices() {
return StreamSupport.stream(serviceLoader.spliterator(), false).count();
}
public Optional<Integer> calculatePriorityOfTask(TaskSummary task) {
public OptionalInt calculatePriorityOfTask(TaskSummary task) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Sending task to PriorityServiceProviders: {}", task);
}
// ServiceLoader.stream() is only available in Java11.
List<Integer> priorities =
StreamSupport.stream(serviceLoader.spliterator(), false)
.map(provider -> getPriorityByProvider(task, provider))
.filter(Optional::isPresent)
.map(Optional::get)
.distinct()
.collect(Collectors.toList());
Set<OptionalInt> priorities =
priorityServiceProviders.stream()
.map(wrap(provider -> provider.calculatePriority(task)))
.filter(OptionalInt::isPresent)
.collect(Collectors.toSet());
if (priorities.size() <= 1) {
return priorities.stream().findFirst();
}
if (LOGGER.isErrorEnabled()) {
if (priorities.size() == 1) {
return priorities.iterator().next();
} else if (!priorities.isEmpty() && LOGGER.isErrorEnabled()) {
LOGGER.error(
"The PriorityServiceProviders determined more than one priority for Task {}.",
LogSanitizer.stripLineBreakingChars(task));
}
return Optional.empty();
}
private Optional<Integer> getPriorityByProvider(
TaskSummary task, PriorityServiceProvider provider) {
try {
return provider.calculatePriority(task);
} catch (Exception e) {
throw new SystemException(
String.format(
"Caught exception while calculating priority of Task in provider %s.",
provider.getClass().getName()),
e);
}
return OptionalInt.empty();
}
}

View File

@ -1,17 +1,16 @@
package pro.taskana.spi.routing.internal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.stream.Collectors;
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.CheckedFunction;
import pro.taskana.common.internal.util.LogSanitizer;
import pro.taskana.common.internal.util.SpiLoader;
import pro.taskana.spi.routing.api.TaskRoutingProvider;
import pro.taskana.task.api.models.Task;
@ -22,37 +21,20 @@ import pro.taskana.task.api.models.Task;
public final class TaskRoutingManager {
private static final Logger LOGGER = LoggerFactory.getLogger(TaskRoutingManager.class);
private static TaskRoutingManager singleton;
private final List<TaskRoutingProvider> theTaskRoutingProviders = new ArrayList<>();
private final ServiceLoader<TaskRoutingProvider> serviceLoader;
private boolean enabled = false;
private final List<TaskRoutingProvider> taskRoutingProviders;
private TaskRoutingManager(TaskanaEngine taskanaEngine) {
serviceLoader = ServiceLoader.load(TaskRoutingProvider.class);
for (TaskRoutingProvider router : serviceLoader) {
router.initialize(taskanaEngine);
theTaskRoutingProviders.add(router);
LOGGER.info("Registered TaskRouter provider: {}", router.getClass().getName());
public TaskRoutingManager(TaskanaEngine taskanaEngine) {
taskRoutingProviders = SpiLoader.load(TaskRoutingProvider.class);
for (TaskRoutingProvider taskRoutingProvider : taskRoutingProviders) {
taskRoutingProvider.initialize(taskanaEngine);
LOGGER.info("Registered TaskRouter provider: {}", taskRoutingProvider.getClass().getName());
}
if (theTaskRoutingProviders.isEmpty()) {
LOGGER.info("No TaskRouter provider found. Running without Task Routing.");
} else {
enabled = true;
if (taskRoutingProviders.isEmpty()) {
LOGGER.info("No TaskRouter provider found. Running without Task routing.");
}
}
public static synchronized TaskRoutingManager getInstance(TaskanaEngine taskanaEngine) {
if (singleton == null) {
singleton = new TaskRoutingManager(taskanaEngine);
}
return singleton;
}
public static boolean isTaskRoutingEnabled() {
return Objects.nonNull(singleton) && singleton.enabled;
}
/**
* Determines a workbasket id for a given task. Algorithm: The task that needs a workbasket id is
* passed to all registered TaskRoutingProviders. If they return no or more than one workbasketId,
@ -64,24 +46,12 @@ public final class TaskRoutingManager {
*/
public String determineWorkbasketId(Task task) {
String workbasketId = null;
if (isTaskRoutingEnabled()) {
// route to all TaskRoutingProviders
// collect in a set to see whether different workbasket ids are returned
if (isEnabled()) {
Set<String> workbasketIds =
theTaskRoutingProviders.stream()
taskRoutingProviders.stream()
.map(
rtr -> {
try {
return rtr.determineWorkbasketId(task);
} catch (Exception e) {
LOGGER.error(
String.format(
"Caught Exception while trying to determine workbasket in class %s",
rtr.getClass().getName()),
e);
throw new SystemException(e.getMessage(), e.getCause());
}
})
CheckedFunction.wrap(
taskRoutingProvider -> taskRoutingProvider.determineWorkbasketId(task)))
.filter(Objects::nonNull)
.collect(Collectors.toSet());
if (workbasketIds.isEmpty()) {
@ -97,9 +67,13 @@ public final class TaskRoutingManager {
LogSanitizer.stripLineBreakingChars(task));
}
} else {
workbasketId = workbasketIds.stream().findFirst().orElse(null);
workbasketId = workbasketIds.iterator().next();
}
}
return workbasketId;
}
public boolean isEnabled() {
return !taskRoutingProviders.isEmpty();
}
}

View File

@ -1,61 +1,43 @@
package pro.taskana.spi.task.internal;
import java.util.Objects;
import java.util.ServiceLoader;
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.api.exceptions.SystemException;
import pro.taskana.common.internal.util.SpiLoader;
import pro.taskana.spi.task.api.CreateTaskPreprocessor;
import pro.taskana.task.api.models.Task;
public class CreateTaskPreprocessorManager {
private static final Logger LOGGER = LoggerFactory.getLogger(CreateTaskPreprocessorManager.class);
private static CreateTaskPreprocessorManager singleton;
private final ServiceLoader<CreateTaskPreprocessor> serviceLoader;
private boolean enabled = false;
private final List<CreateTaskPreprocessor> createTaskPreprocessors;
private CreateTaskPreprocessorManager() {
serviceLoader = ServiceLoader.load(CreateTaskPreprocessor.class);
for (CreateTaskPreprocessor preprocessor : serviceLoader) {
public CreateTaskPreprocessorManager() {
createTaskPreprocessors = SpiLoader.load(CreateTaskPreprocessor.class);
for (CreateTaskPreprocessor preprocessor : createTaskPreprocessors) {
LOGGER.info(
"Registered CreateTaskPreprocessor provider: {}", preprocessor.getClass().getName());
enabled = true;
}
if (!enabled) {
if (createTaskPreprocessors.isEmpty()) {
LOGGER.info("No CreateTaskPreprocessor found. Running without CreateTaskPreprocessor.");
}
}
public static synchronized CreateTaskPreprocessorManager getInstance() {
if (singleton == null) {
singleton = new CreateTaskPreprocessorManager();
}
return singleton;
}
public static boolean isCreateTaskPreprocessorEnabled() {
return Objects.nonNull(singleton) && singleton.enabled;
}
public Task processTaskBeforeCreation(Task taskToProcess) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Sending task to CreateTaskPreprocessor providers: {}", taskToProcess);
}
serviceLoader.forEach(
createTaskPreprocessorProvider -> {
try {
createTaskPreprocessorProvider.processTaskBeforeCreation(taskToProcess);
} catch (Exception e) {
LOGGER.error(
String.format(
"Caught exception while processing task before creation in class %s",
createTaskPreprocessorProvider.getClass().getName()),
e);
throw new SystemException(e.getMessage(), e.getCause());
}
});
createTaskPreprocessors.forEach(
wrap(
createTaskPreprocessor ->
createTaskPreprocessor.processTaskBeforeCreation(taskToProcess)));
return taskToProcess;
}
public boolean isEnabled() {
return !createTaskPreprocessors.isEmpty();
}
}

View File

@ -170,7 +170,7 @@ public class TaskServiceImpl implements TaskService {
throws NotAuthorizedException, WorkbasketNotFoundException, ClassificationNotFoundException,
TaskAlreadyExistException, InvalidArgumentException, AttachmentPersistenceException {
if (CreateTaskPreprocessorManager.isCreateTaskPreprocessorEnabled()) {
if (createTaskPreprocessorManager.isEnabled()) {
taskToCreate = createTaskPreprocessorManager.processTaskBeforeCreation(taskToCreate);
}
@ -198,7 +198,7 @@ public class TaskServiceImpl implements TaskService {
workbasket = workbasketService.getWorkbasket(workbasketId);
task.setWorkbasketSummary(workbasket.asSummary());
} else {
throw new InvalidArgumentException("Cannot create a task outside a workbasket");
throw new InvalidArgumentException("Cannot create a Task outside a Workbasket");
}
}
@ -226,20 +226,14 @@ public class TaskServiceImpl implements TaskService {
ObjectReference.validate(task.getPrimaryObjRef(), "primary ObjectReference", "Task");
standardSettingsOnTaskCreation(task, classification);
setCallbackStateOnTaskCreation(task);
if (PriorityServiceManager.isPriorityServiceEnabled()) {
Optional<Integer> newPriority = priorityServiceManager.calculatePriorityOfTask(task);
if (newPriority.isPresent()) {
task.setPriority(newPriority.get());
}
}
priorityServiceManager.calculatePriorityOfTask(task).ifPresent(task::setPriority);
try {
this.taskMapper.insert(task);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Method createTask() created Task '{}'.", task.getId());
}
if (HistoryEventManager.isHistoryEnabled()) {
if (historyEventManager.isEnabled()) {
String details =
ObjectAttributeChangeDetector.determineChangesInAttributes(newTask(), task);
@ -426,12 +420,9 @@ public class TaskServiceImpl implements TaskService {
standardUpdateActions(oldTaskImpl, newTaskImpl);
if (PriorityServiceManager.isPriorityServiceEnabled()) {
Optional<Integer> newPriority = priorityServiceManager.calculatePriorityOfTask(newTaskImpl);
if (newPriority.isPresent()) {
newTaskImpl.setPriority(newPriority.get());
}
}
priorityServiceManager
.calculatePriorityOfTask(newTaskImpl)
.ifPresent(newTaskImpl::setPriority);
taskMapper.update(newTaskImpl);
@ -439,7 +430,7 @@ public class TaskServiceImpl implements TaskService {
LOGGER.debug("Method updateTask() updated task '{}' for user '{}'.", task.getId(), userId);
}
if (HistoryEventManager.isHistoryEnabled()) {
if (historyEventManager.isEnabled()) {
String changeDetails =
ObjectAttributeChangeDetector.determineChangesInAttributes(oldTaskImpl, newTaskImpl);
@ -771,7 +762,7 @@ public class TaskServiceImpl implements TaskService {
taskanaEngine.openConnection();
cancelledTask = terminateCancelCommonActions(taskId, TaskState.CANCELLED);
if (HistoryEventManager.isHistoryEnabled()) {
if (historyEventManager.isEnabled()) {
historyEventManager.createEvent(
new TaskCancelledEvent(
IdGenerator.generateWithPrefix(IdGenerator.ID_PREFIX_TASK_HISTORY_EVENT),
@ -797,7 +788,7 @@ public class TaskServiceImpl implements TaskService {
taskanaEngine.openConnection();
terminatedTask = terminateCancelCommonActions(taskId, TaskState.TERMINATED);
if (HistoryEventManager.isHistoryEnabled()) {
if (historyEventManager.isEnabled()) {
historyEventManager.createEvent(
new TaskTerminatedEvent(
IdGenerator.generateWithPrefix(IdGenerator.ID_PREFIX_TASK_HISTORY_EVENT),
@ -1124,7 +1115,7 @@ public class TaskServiceImpl implements TaskService {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Task '{}' claimed by user '{}'.", taskId, userId);
}
if (HistoryEventManager.isHistoryEnabled()) {
if (historyEventManager.isEnabled()) {
historyEventManager.createEvent(
new TaskClaimedEvent(
IdGenerator.generateWithPrefix(IdGenerator.ID_PREFIX_TASK_HISTORY_EVENT),
@ -1221,7 +1212,7 @@ public class TaskServiceImpl implements TaskService {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Task '{}' unclaimed by user '{}'.", taskId, userId);
}
if (HistoryEventManager.isHistoryEnabled()) {
if (historyEventManager.isEnabled()) {
historyEventManager.createEvent(
new TaskClaimCancelledEvent(
IdGenerator.generateWithPrefix(IdGenerator.ID_PREFIX_TASK_HISTORY_EVENT),
@ -1261,7 +1252,7 @@ public class TaskServiceImpl implements TaskService {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Task '{}' completed by user '{}'.", taskId, userId);
}
if (HistoryEventManager.isHistoryEnabled()) {
if (historyEventManager.isEnabled()) {
historyEventManager.createEvent(
new TaskCompletedEvent(
IdGenerator.generateWithPrefix(IdGenerator.ID_PREFIX_TASK_HISTORY_EVENT),
@ -1533,7 +1524,7 @@ public class TaskServiceImpl implements TaskService {
if (!updateClaimedTaskIds.isEmpty()) {
taskMapper.updateClaimed(updateClaimedTaskIds, claimedReference);
}
if (HistoryEventManager.isHistoryEnabled()) {
if (historyEventManager.isEnabled()) {
createTasksCompletedEvents(taskSummaryList);
}
}

View File

@ -113,7 +113,7 @@ final class TaskTransferrer {
applyTransferValuesForTask(task, destinationWorkbasket, setTransferFlag);
taskMapper.update(task);
if (HistoryEventManager.isHistoryEnabled()) {
if (historyEventManager.isEnabled()) {
createTransferredEvent(
oldTask, task, originWorkbasket.getId(), destinationWorkbasket.getId());
}
@ -250,7 +250,7 @@ final class TaskTransferrer {
taskMapper.updateTransfered(
taskSummaries.stream().map(TaskSummary::getId).collect(Collectors.toSet()), updateObject);
if (HistoryEventManager.isHistoryEnabled()) {
if (historyEventManager.isEnabled()) {
taskSummaries.forEach(
oldSummary -> {
TaskSummaryImpl newSummary = (TaskSummaryImpl) oldSummary.copy();

View File

@ -182,6 +182,16 @@ public class TaskBuilder {
return this;
}
public TaskBuilder priority(Integer priority) {
if (priority != null) {
testTask.setPriorityIgnoreFreeze(priority);
testTask.freezePriority();
} else {
testTask.unfreezePriority();
}
return this;
}
public Task buildAndStore(TaskService taskService)
throws TaskAlreadyExistException, InvalidArgumentException, WorkbasketNotFoundException,
ClassificationNotFoundException, NotAuthorizedException, AttachmentPersistenceException,

View File

@ -12,6 +12,7 @@ class TaskTestImpl extends TaskImpl {
private boolean freezeModified = false;
private boolean freezeRead = false;
private boolean freezeTransferred = false;
private boolean freezePriority = false;
@Override
public void setState(TaskState state) {
@ -68,6 +69,17 @@ class TaskTestImpl extends TaskImpl {
super.setTransferred(isTransferred);
}
@Override
public void setPriority(int priority) {
if (!freezePriority) {
super.setPriority(priority);
}
}
public void setPriorityIgnoreFreeze(int priority) {
super.setPriority(priority);
}
public void freezeState() {
freezeState = true;
}
@ -107,4 +119,12 @@ class TaskTestImpl extends TaskImpl {
public void unfreezeTransferred() {
freezeTransferred = false;
}
public void freezePriority() {
freezePriority = true;
}
public void unfreezePriority() {
freezePriority = false;
}
}

View File

@ -17,7 +17,7 @@ public class TaskUpdatePriorityBatchStatement {
preparedStatement = connection.prepareStatement("update TASK set PRIORITY = ? where ID = ?");
}
public void addPriorityUpdate(String taskId, Integer priority) throws SQLException {
public void addPriorityUpdate(String taskId, int priority) throws SQLException {
preparedStatement.setInt(1, priority);
preparedStatement.setString(2, taskId);
LOGGER.debug("Job update priority to {} for task {}.", priority, taskId);

View File

@ -2,11 +2,12 @@ package pro.taskana.task.internal.jobs.helper;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.OptionalInt;
import java.util.function.IntPredicate;
import pro.taskana.common.api.BaseQuery.SortDirection;
import pro.taskana.common.api.TaskanaEngine;
import pro.taskana.common.internal.TaskanaEngineImpl;
import pro.taskana.spi.priority.internal.PriorityServiceManager;
import pro.taskana.task.api.TaskQueryColumnName;
import pro.taskana.task.api.TaskState;
@ -16,14 +17,15 @@ public class TaskUpdatePriorityWorker {
private final SqlConnectionRunner sqlConnectionRunner;
private final TaskanaEngine taskanaEngine;
private final PriorityServiceManager priorityServiceManager;
public TaskUpdatePriorityWorker(TaskanaEngine taskanaEngine) {
this.taskanaEngine = taskanaEngine;
this.sqlConnectionRunner = new SqlConnectionRunner(taskanaEngine);
sqlConnectionRunner = new SqlConnectionRunner(taskanaEngine);
priorityServiceManager = ((TaskanaEngineImpl) taskanaEngine).getPriorityServiceManager();
}
public List<String> executeBatch(List<String> taskIds) {
List<String> updatedTaskIds = new ArrayList<>();
sqlConnectionRunner.runWithConnection(
connection -> {
@ -32,11 +34,11 @@ public class TaskUpdatePriorityWorker {
List<TaskSummary> list = getTaskSummariesByIds(taskIds);
for (TaskSummary taskSummary : list) {
Optional<Integer> calculatedPriority = getCalculatedPriority(taskSummary);
OptionalInt calculatedPriority = getCalculatedPriority(taskSummary);
if (calculatedPriority.isPresent()) {
final String taskId = taskSummary.getId();
updatedTaskIds.add(taskId);
taskUpdateBatch.addPriorityUpdate(taskId, calculatedPriority.get());
taskUpdateBatch.addPriorityUpdate(taskId, calculatedPriority.getAsInt());
}
}
taskUpdateBatch.executeBatch();
@ -71,13 +73,16 @@ public class TaskUpdatePriorityWorker {
.list();
}
public Optional<Integer> getCalculatedPriority(TaskSummary taskSummary) {
return PriorityServiceManager.getInstance()
.calculatePriorityOfTask(taskSummary)
.filter(hasDifferentPriority(taskSummary));
public OptionalInt getCalculatedPriority(TaskSummary taskSummary) {
OptionalInt computedPriority = priorityServiceManager.calculatePriorityOfTask(taskSummary);
if (computedPriority.isPresent()
&& hasDifferentPriority(taskSummary).test(computedPriority.getAsInt())) {
return computedPriority;
}
return OptionalInt.empty();
}
public static Predicate<Integer> hasDifferentPriority(TaskSummary taskSummary) {
public static IntPredicate hasDifferentPriority(TaskSummary taskSummary) {
return prio -> taskSummary != null && prio != taskSummary.getPriority();
}
}

View File

@ -67,6 +67,7 @@ public class WorkbasketServiceImpl implements WorkbasketService {
public WorkbasketServiceImpl(
InternalTaskanaEngine taskanaEngine,
HistoryEventManager historyEventManager,
WorkbasketMapper workbasketMapper,
DistributionTargetMapper distributionTargetMapper,
WorkbasketAccessMapper workbasketAccessMapper) {
@ -74,7 +75,7 @@ public class WorkbasketServiceImpl implements WorkbasketService {
this.workbasketMapper = workbasketMapper;
this.distributionTargetMapper = distributionTargetMapper;
this.workbasketAccessMapper = workbasketAccessMapper;
this.historyEventManager = taskanaEngine.getHistoryEventManager();
this.historyEventManager = historyEventManager;
}
@Override
@ -145,7 +146,7 @@ public class WorkbasketServiceImpl implements WorkbasketService {
workbasketMapper.insert(workbasket);
if (HistoryEventManager.isHistoryEnabled()) {
if (historyEventManager.isEnabled()) {
String details =
ObjectAttributeChangeDetector.determineChangesInAttributes(
newWorkbasket("", ""), newWorkbasket);
@ -202,7 +203,7 @@ public class WorkbasketServiceImpl implements WorkbasketService {
workbasketMapper.update(workbasketImplToUpdate);
}
if (HistoryEventManager.isHistoryEnabled()) {
if (historyEventManager.isEnabled()) {
String details =
ObjectAttributeChangeDetector.determineChangesInAttributes(
oldWorkbasket, workbasketToUpdate);
@ -266,7 +267,7 @@ public class WorkbasketServiceImpl implements WorkbasketService {
try {
workbasketAccessMapper.insert(accessItem);
if (HistoryEventManager.isHistoryEnabled()) {
if (historyEventManager.isEnabled()) {
String details =
ObjectAttributeChangeDetector.determineChangesInAttributes(
@ -326,7 +327,7 @@ public class WorkbasketServiceImpl implements WorkbasketService {
workbasketAccessMapper.update(accessItem);
if (HistoryEventManager.isHistoryEnabled()) {
if (historyEventManager.isEnabled()) {
String details =
ObjectAttributeChangeDetector.determineChangesInAttributes(originalItem, accessItem);
@ -359,13 +360,13 @@ public class WorkbasketServiceImpl implements WorkbasketService {
WorkbasketAccessItem accessItem = null;
if (HistoryEventManager.isHistoryEnabled()) {
if (historyEventManager.isEnabled()) {
accessItem = workbasketAccessMapper.findById(accessItemId);
}
workbasketAccessMapper.delete(accessItemId);
if (HistoryEventManager.isHistoryEnabled() && accessItem != null) {
if (historyEventManager.isEnabled() && accessItem != null) {
String details =
ObjectAttributeChangeDetector.determineChangesInAttributes(
@ -490,14 +491,14 @@ public class WorkbasketServiceImpl implements WorkbasketService {
List<WorkbasketAccessItemImpl> originalAccessItems = new ArrayList<>();
if (HistoryEventManager.isHistoryEnabled()) {
if (historyEventManager.isEnabled()) {
originalAccessItems = workbasketAccessMapper.findByWorkbasketId(workbasketId);
}
// delete all current ones
workbasketAccessMapper.deleteAllAccessItemsForWorkbasketId(workbasketId);
accessItems.forEach(workbasketAccessMapper::insert);
if (HistoryEventManager.isHistoryEnabled()) {
if (historyEventManager.isEnabled()) {
String details =
ObjectAttributeChangeDetector.determineChangesInAttributes(
@ -601,7 +602,7 @@ public class WorkbasketServiceImpl implements WorkbasketService {
List<String> originalTargetWorkbasketIds = new ArrayList<>();
if (HistoryEventManager.isHistoryEnabled()) {
if (historyEventManager.isEnabled()) {
originalTargetWorkbasketIds = distributionTargetMapper.findBySourceId(sourceWorkbasketId);
}
@ -624,7 +625,7 @@ public class WorkbasketServiceImpl implements WorkbasketService {
}
}
if (HistoryEventManager.isHistoryEnabled() && !targetWorkbasketIds.isEmpty()) {
if (historyEventManager.isEnabled() && !targetWorkbasketIds.isEmpty()) {
String details =
ObjectAttributeChangeDetector.determineChangesInAttributes(
@ -674,7 +675,7 @@ public class WorkbasketServiceImpl implements WorkbasketService {
} else {
distributionTargetMapper.insert(sourceWorkbasketId, targetWorkbasketId);
if (HistoryEventManager.isHistoryEnabled()) {
if (historyEventManager.isEnabled()) {
String details =
"{\"changes\":{\"newValue\":\"" + targetWorkbasketId + "\",\"oldValue\":\"\"}}";
@ -717,7 +718,7 @@ public class WorkbasketServiceImpl implements WorkbasketService {
if (numberOfDistTargets > 0) {
distributionTargetMapper.delete(sourceWorkbasketId, targetWorkbasketId);
if (HistoryEventManager.isHistoryEnabled()) {
if (historyEventManager.isEnabled()) {
Workbasket workbasket = workbasketMapper.findById(sourceWorkbasketId);
@ -805,7 +806,7 @@ public class WorkbasketServiceImpl implements WorkbasketService {
workbasketMapper.delete(workbasketId);
deleteReferencesToWorkbasket(workbasketId);
if (HistoryEventManager.isHistoryEnabled()) {
if (historyEventManager.isEnabled()) {
String details =
ObjectAttributeChangeDetector.determineChangesInAttributes(
@ -912,12 +913,12 @@ public class WorkbasketServiceImpl implements WorkbasketService {
}
List<WorkbasketAccessItemImpl> workbasketAccessItems = new ArrayList<>();
if (HistoryEventManager.isHistoryEnabled()) {
if (historyEventManager.isEnabled()) {
workbasketAccessItems = workbasketAccessMapper.findByAccessId(accessId);
}
workbasketAccessMapper.deleteAccessItemsForAccessId(accessId);
if (HistoryEventManager.isHistoryEnabled()) {
if (historyEventManager.isEnabled()) {
for (WorkbasketAccessItemImpl workbasketAccessItem : workbasketAccessItems) {
@ -1104,7 +1105,7 @@ public class WorkbasketServiceImpl implements WorkbasketService {
WorkbasketImpl workbasket = workbasketMapper.findById(workbasketId);
workbasket.setMarkedForDeletion(true);
workbasketMapper.update(workbasket);
if (HistoryEventManager.isHistoryEnabled()) {
if (historyEventManager.isEnabled()) {
historyEventManager.createEvent(
new WorkbasketMarkedForDeletionEvent(

View File

@ -8,6 +8,7 @@ import static com.tngtech.archunit.library.GeneralCodingRules.NO_CLASSES_SHOULD_
import static com.tngtech.archunit.library.dependencies.SlicesRuleDefinition.slices;
import static org.assertj.core.api.Assertions.assertThat;
import com.tngtech.archunit.base.DescribedPredicate;
import com.tngtech.archunit.base.Optional;
import com.tngtech.archunit.core.domain.JavaClass;
import com.tngtech.archunit.core.domain.JavaClass.Predicates;
@ -37,8 +38,11 @@ import org.apache.ibatis.annotations.UpdateProvider;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
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.function.ThrowingConsumer;
import testapi.TaskanaIntegrationTest;
@ -85,7 +89,8 @@ class ArchitectureTest {
@BeforeAll
static void init() {
// time intensive operation should only be done once
importedClasses = new ClassFileImporter().importPackages("pro.taskana", "acceptance");
importedClasses =
new ClassFileImporter().importPackages("pro.taskana", "acceptance", "testapi");
}
@Test
@ -93,8 +98,6 @@ class ArchitectureTest {
ArchRule myRule =
classes()
.that()
.haveNameNotMatching(".*ScheduledJob.Type")
.and()
.resideInAPackage("..api..")
.should()
.onlyDependOnClassesThat(
@ -184,7 +187,8 @@ class ArchitectureTest {
List<Pattern> excludePackages =
Stream.of(
"pro.taskana", // from TaskanaEngineConfiguration
"acceptance.*" // all our acceptance tests
"acceptance.*", // all our acceptance tests
"testapi.*" // our test API
)
.map(Pattern::compile)
.collect(Collectors.toList());
@ -233,8 +237,6 @@ class ArchitectureTest {
.and()
.haveSimpleNameNotEndingWith("AbstractTaskanaJob")
.and()
.haveNameNotMatching(".*ScheduledJob.Type")
.and()
.resideInAPackage("..common..")
.and()
.resideOutsideOfPackage("..common.test..")
@ -302,17 +304,59 @@ class ArchitectureTest {
classes()
.that()
.areAnnotatedWith(TaskanaIntegrationTest.class)
.or(areNestedTaskanaIntegrationTestClasses())
.should(onlyHaveFieldsWithNoModifier());
rule.check(importedClasses);
}
@Test
void nestedTaskanaIntegrationTestsShouldBeAnnotatedWithTestInstance() {
ArchRule rule =
classes()
.that(areNestedTaskanaIntegrationTestClasses())
.should(beAnnotatedWithTestInstancePerClass());
rule.check(importedClasses);
}
private ArchCondition<JavaClass> beAnnotatedWithTestInstancePerClass() {
return new ArchCondition<JavaClass>("be annotated with @TestInstance(Lifecycle.PER_CLASS)") {
@Override
public void check(JavaClass item, ConditionEvents events) {
Optional<TestInstance> testInstanceOptional =
item.tryGetAnnotationOfType(TestInstance.class);
if (!testInstanceOptional.isPresent()
|| testInstanceOptional.get().value() != Lifecycle.PER_CLASS) {
events.add(
SimpleConditionEvent.violated(
item,
String.format(
"Class '%s' is not annotated with @TestInstance(Lifecycle.PER_CLASS)",
item.getFullName())));
}
}
};
}
private DescribedPredicate<JavaClass> areNestedTaskanaIntegrationTestClasses() {
return new DescribedPredicate<JavaClass>("are nested TaskanaIntegrationTest classes") {
@Override
public boolean apply(JavaClass input) {
Optional<JavaClass> enclosingClass = input.getEnclosingClass();
return input.isAnnotatedWith(Nested.class)
&& enclosingClass.isPresent()
&& enclosingClass.get().isAnnotatedWith(TaskanaIntegrationTest.class);
}
};
}
private ArchCondition<JavaClass> onlyHaveFieldsWithNoModifier() {
return new ArchCondition<JavaClass>("only have fields with no modifier") {
@Override
public void check(JavaClass item, ConditionEvents events) {
for (JavaField field : item.getAllFields()) {
if (!field.getModifiers().isEmpty()) {
if (!field.reflect().isSynthetic() && !field.getModifiers().isEmpty()) {
events.add(
SimpleConditionEvent.violated(
item,

View File

@ -1,13 +1,23 @@
package acceptance;
import java.util.UUID;
import pro.taskana.classification.internal.builder.ClassificationBuilder;
import pro.taskana.task.internal.builder.ObjectReferenceBuilder;
import pro.taskana.workbasket.api.WorkbasketType;
import pro.taskana.workbasket.internal.builder.WorkbasketBuilder;
public class DefaultTestEntities {
public static ClassificationBuilder defaultTestClassification() {
return ClassificationBuilder.newClassification()
.key(UUID.randomUUID().toString().replace("-", ""))
.domain("DOMAIN_A");
}
public static WorkbasketBuilder defaultTestWorkbasket() {
return WorkbasketBuilder.newWorkbasket()
.key(UUID.randomUUID().toString())
.domain("DOMAIN_A")
.name("Megabasket")
.type(WorkbasketType.GROUP)

View File

@ -176,9 +176,7 @@ class TaskBuilderTest {
expectedTask.setTransferred(true);
expectedTask.setCreator("user-1-1");
expectedTask.addAttachment(attachment);
// ATTENTION: We have an SPI running which transforms custom1 of every created Task to that.
// Please fix after removal of that SPI during TSK-1704 ;)
expectedTask.setCustomAttribute(TaskCustomField.CUSTOM_1, "preprocessedCustomField");
expectedTask.setCustomAttribute(TaskCustomField.CUSTOM_1, "custom1");
expectedTask.setCustomAttribute(TaskCustomField.CUSTOM_2, "custom2");
expectedTask.setCustomAttribute(TaskCustomField.CUSTOM_3, "custom3");
expectedTask.setCustomAttribute(TaskCustomField.CUSTOM_4, "custom4");
@ -246,10 +244,9 @@ class TaskBuilderTest {
Task::getModified),
Quadruple.of("read", true, (b, v) -> b.read((Boolean) v), Task::isRead),
Quadruple.of(
"transferred",
true,
(b, v) -> b.transferred((Boolean) v),
Task::isTransferred));
"transferred", true, (b, v) -> b.transferred((Boolean) v), Task::isTransferred),
Quadruple.of(
"priority", 1337, (b, v) -> b.priority((Integer) v), Task::getPriority));
Stream<DynamicTest> applyBuilderFunction =
DynamicTest.stream(

View File

@ -1,134 +1,159 @@
package acceptance.jobs.helper;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assumptions.assumeThat;
import acceptance.AbstractAccTest;
import acceptance.DefaultTestEntities;
import acceptance.priorityservice.TestPriorityServiceProvider;
import java.time.Instant;
import java.util.List;
import java.util.function.Predicate;
import org.junit.jupiter.api.BeforeEach;
import java.util.function.IntPredicate;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import testapi.TaskanaInject;
import testapi.TaskanaIntegrationTest;
import testapi.WithServiceProvider;
import pro.taskana.common.api.exceptions.NotAuthorizedException;
import pro.taskana.common.test.security.JaasExtension;
import pro.taskana.classification.api.ClassificationService;
import pro.taskana.classification.api.models.ClassificationSummary;
import pro.taskana.common.api.TaskanaEngine;
import pro.taskana.common.test.security.WithAccessId;
import pro.taskana.spi.priority.internal.PriorityServiceManager;
import pro.taskana.task.api.exceptions.TaskNotFoundException;
import pro.taskana.spi.priority.api.PriorityServiceProvider;
import pro.taskana.task.api.TaskService;
import pro.taskana.task.api.TaskState;
import pro.taskana.task.api.models.Task;
import pro.taskana.task.api.models.TaskSummary;
import pro.taskana.task.internal.builder.TaskBuilder;
import pro.taskana.task.internal.jobs.helper.TaskUpdatePriorityWorker;
import pro.taskana.task.internal.models.TaskImpl;
import pro.taskana.workbasket.api.WorkbasketService;
import pro.taskana.workbasket.api.models.WorkbasketSummary;
/** Acceptance test for all "jobs tasks runner" scenarios. */
@ExtendWith(JaasExtension.class)
class TaskUpdatePriorityWorkerAccTest extends AbstractAccTest {
@TaskanaIntegrationTest
class TaskUpdatePriorityWorkerAccTest {
@BeforeEach
void before() throws Exception {
// required if single tests modify database
// TODO split test class into readOnly & modifying tests to improve performance
resetDb(true);
@TaskanaInject TaskanaEngine taskanaEngine;
@TaskanaInject TaskService taskService;
TaskUpdatePriorityWorker worker;
TaskSummary task1;
TaskSummary task2;
Task completedTask;
@WithAccessId(user = "admin")
@BeforeAll
void setUp(ClassificationService classificationService, WorkbasketService workbasketService)
throws Exception {
ClassificationSummary classificationSummary =
DefaultTestEntities.defaultTestClassification()
.buildAndStore(classificationService)
.asSummary();
WorkbasketSummary workbasketSummary =
DefaultTestEntities.defaultTestWorkbasket().buildAndStore(workbasketService).asSummary();
TaskBuilder taskBuilder =
TaskBuilder.newTask()
.classificationSummary(classificationSummary)
.workbasketSummary(workbasketSummary)
.primaryObjRef(DefaultTestEntities.defaultTestObjectReference().build());
task1 = taskBuilder.buildAndStore(taskService).asSummary();
task2 = taskBuilder.buildAndStore(taskService).asSummary();
completedTask = taskBuilder.state(TaskState.COMPLETED).buildAndStore(taskService);
worker = new TaskUpdatePriorityWorker(taskanaEngine);
}
@Test
@WithAccessId(user = "admin")
void should_loadAnyRelevantTaskId() {
// given
TaskUpdatePriorityWorker worker = new TaskUpdatePriorityWorker(taskanaEngine);
void should_loadAnyRelevantTaskIds() {
// when
final List<String> allRelevantTaskIds = worker.getAllRelevantTaskIds();
// then
assertThat(allRelevantTaskIds).hasSizeGreaterThan(0);
assertThat(allRelevantTaskIds).containsExactlyInAnyOrder(task1.getId(), task2.getId());
}
@Test
@WithAccessId(user = "admin")
void should_loadExistingTaskIds() {
// given
TaskUpdatePriorityWorker worker = new TaskUpdatePriorityWorker(taskanaEngine);
final List<String> allRelevantTaskIds = worker.getAllRelevantTaskIds();
final String foundTaskId = allRelevantTaskIds.get(0);
// when
final List<TaskSummary> taskSummariesByIds = worker.getTaskSummariesByIds(List.of(foundTaskId));
final List<TaskSummary> taskSummariesByIds =
worker.getTaskSummariesByIds(List.of(task1.getId(), task2.getId()));
// then
assertThat(taskSummariesByIds)
.hasSize(1)
.extracting(TaskSummary::getId)
.containsExactly(foundTaskId);
assertThat(taskSummariesByIds).containsExactlyInAnyOrder(task1, task2);
}
@Test
@WithAccessId(user = "admin")
void should_notLoadAnyIrrelevantTaskIds() {
// given
TaskUpdatePriorityWorker worker = new TaskUpdatePriorityWorker(taskanaEngine);
String completedTaskId = "TKI:000000000000000000000000000000000038";
// when
final List<String> allRelevantTaskIds = worker.getAllRelevantTaskIds();
// then
assertThat(allRelevantTaskIds).isNotEmpty().doesNotContain(completedTaskId);
}
@Test
@WithAccessId(user = "admin")
void should_loadExistingTaskSummariesById() {
// given
TaskUpdatePriorityWorker worker = new TaskUpdatePriorityWorker(taskanaEngine);
String taskId1 = "TKI:000000000000000000000000000000000050";
String taskId2 = "TKI:000000000000000000000000000000000051";
// when
final List<TaskSummary> taskSummariesByIds =
worker.getTaskSummariesByIds(List.of(taskId1, taskId2));
// then
assertThat(taskSummariesByIds)
.hasSizeGreaterThan(0)
.extracting(TaskSummary::getId)
.containsExactlyInAnyOrder(taskId1, taskId2);
}
@Test
@WithAccessId(user = "admin")
void should_executeBatch() throws TaskNotFoundException, NotAuthorizedException {
// given
TaskUpdatePriorityWorker worker = new TaskUpdatePriorityWorker(taskanaEngine);
final List<String> allRelevantTaskIds = worker.getAllRelevantTaskIds();
String taskId = "TKI:000000000000000000000000000000000050";
Task taskOld = taskanaEngine.getTaskService().getTask(taskId);
// when
final List<String> updatedTaskIds = worker.executeBatch(allRelevantTaskIds);
// then
final Task taskUpdated = taskanaEngine.getTaskService().getTask(taskId);
assumeThat(PriorityServiceManager.getInstance().countRegisteredServices())
.describedAs("SPI should be provided in order to check for modified priorities.")
.isPositive();
assertThat(updatedTaskIds).contains(taskId);
assertThat(taskUpdated.getPriority()).isNotEqualTo(taskOld.getPriority());
assertThat(allRelevantTaskIds).isNotEmpty().doesNotContain(completedTask.getId());
}
@Test
void should_noticeDifferentPriority_When_PriorityHasChanged() {
// given
final TaskImpl task = (TaskImpl) taskanaEngine.getTaskService().newTask();
TaskImpl task = (TaskImpl) taskService.newTask();
task.setPriority(232);
// when
final Predicate<Integer> differentPriority =
TaskUpdatePriorityWorker.hasDifferentPriority(task);
IntPredicate differentPriority = TaskUpdatePriorityWorker.hasDifferentPriority(task);
// then
assertThat(differentPriority).rejects(232).accepts(2, 3, 4, 5);
}
@Nested
@WithServiceProvider(
serviceProviderInterface = PriorityServiceProvider.class,
serviceProviders = TestPriorityServiceProvider.class)
@TestInstance(Lifecycle.PER_CLASS)
class WithSpi {
@TaskanaInject TaskService taskService;
TaskUpdatePriorityWorker worker;
@BeforeAll
void setup(TaskanaEngine taskanaEngine) {
worker = new TaskUpdatePriorityWorker(taskanaEngine);
}
@Test
@WithAccessId(user = "admin")
void should_executeBatch(
WorkbasketService workbasketService, ClassificationService classificationService)
throws Exception {
// given
ClassificationSummary classificationSummary =
DefaultTestEntities.defaultTestClassification()
.buildAndStore(classificationService)
.asSummary();
WorkbasketSummary workbasketSummary =
DefaultTestEntities.defaultTestWorkbasket().buildAndStore(workbasketService).asSummary();
Task oldTask =
TaskBuilder.newTask()
.classificationSummary(classificationSummary)
.workbasketSummary(workbasketSummary)
.created(Instant.parse("2020-04-30T07:12:00.000Z"))
.priority(1337)
.primaryObjRef(DefaultTestEntities.defaultTestObjectReference().build())
.buildAndStore(taskService);
// when
final List<String> updatedTaskIds = worker.executeBatch(List.of(oldTask.getId()));
// then
final Task updatedTask = taskService.getTask(oldTask.getId());
assertThat(updatedTaskIds).containsExactly(oldTask.getId());
assertThat(updatedTask.getPriority()).isNotEqualTo(oldTask.getPriority());
}
}
}

View File

@ -2,7 +2,7 @@ package acceptance.priorityservice;
import java.time.Duration;
import java.time.Instant;
import java.util.Optional;
import java.util.OptionalInt;
import pro.taskana.spi.priority.api.PriorityServiceProvider;
import pro.taskana.task.api.TaskCustomField;
@ -12,13 +12,13 @@ public class TestPriorityServiceProvider implements PriorityServiceProvider {
private static final int MULTIPLIER = 10;
@Override
public Optional<Integer> calculatePriority(TaskSummary taskSummary) {
public OptionalInt calculatePriority(TaskSummary taskSummary) {
long diffInDays = Duration.between(taskSummary.getCreated(), Instant.now()).toDays();
int priority = diffInDays >= 1 ? Math.toIntExact(diffInDays) : 1;
if ("true".equals(taskSummary.getCustomAttribute(TaskCustomField.CUSTOM_6))) {
priority *= MULTIPLIER;
}
return Optional.of(priority);
return OptionalInt.of(priority);
}
}

View File

@ -2,41 +2,76 @@ package acceptance.taskpreprocessing;
import static org.assertj.core.api.Assertions.assertThat;
import acceptance.AbstractAccTest;
import acceptance.DefaultTestEntities;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import testapi.TaskanaInject;
import testapi.TaskanaIntegrationTest;
import testapi.WithServiceProvider;
import pro.taskana.common.test.security.JaasExtension;
import pro.taskana.classification.api.ClassificationService;
import pro.taskana.classification.api.models.ClassificationSummary;
import pro.taskana.common.test.security.WithAccessId;
import pro.taskana.spi.task.api.CreateTaskPreprocessor;
import pro.taskana.task.api.TaskCustomField;
import pro.taskana.task.api.TaskService;
import pro.taskana.task.api.models.Task;
import pro.taskana.task.internal.models.TaskImpl;
import pro.taskana.workbasket.api.WorkbasketPermission;
import pro.taskana.workbasket.api.WorkbasketService;
import pro.taskana.workbasket.api.models.WorkbasketSummary;
import pro.taskana.workbasket.internal.builder.WorkbasketAccessItemBuilder;
/** Acceptance test for "task preprocessing" scenario. */
@ExtendWith(JaasExtension.class)
class CreateTaskPreprocessingAccTest extends AbstractAccTest {
@TaskanaIntegrationTest
@WithServiceProvider(
serviceProviderInterface = CreateTaskPreprocessor.class,
serviceProviders = CreateTaskPreprocessingAccTest.TestCreateTaskPreprocessorProvider.class)
class CreateTaskPreprocessingAccTest {
private final TaskService taskService = taskanaEngine.getTaskService();
@TaskanaInject TaskService taskService;
@WithAccessId(user = "admin")
WorkbasketSummary workbasketSummary;
ClassificationSummary classificationSummary;
@WithAccessId(user = "businessadmin")
@BeforeAll
void setup(ClassificationService classificationService, WorkbasketService workbasketService)
throws Exception {
classificationSummary =
DefaultTestEntities.defaultTestClassification()
.buildAndStore(classificationService)
.asSummary();
workbasketSummary =
DefaultTestEntities.defaultTestWorkbasket().buildAndStore(workbasketService).asSummary();
WorkbasketAccessItemBuilder.newWorkbasketAccessItem()
.accessId("user-1-1")
.workbasketId(workbasketSummary.getId())
.permission(WorkbasketPermission.READ)
.permission(WorkbasketPermission.OPEN)
.permission(WorkbasketPermission.APPEND)
.buildAndStore(workbasketService);
}
@WithAccessId(user = "user-1-1")
@Test
void should_processTaskBeforeCreation_When_CreateTaskPreprocessorEnabled() throws Exception {
TaskImpl newTaskToCreate = (TaskImpl) taskService.newTask();
newTaskToCreate.setClassificationKey("L10303");
newTaskToCreate.setPrimaryObjRef(
createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"));
newTaskToCreate.setWorkbasketKey("GPK_KSC");
newTaskToCreate.setDomain("DOMAIN_A");
Task newTaskToCreate = taskService.newTask(workbasketSummary.getId());
newTaskToCreate.setClassificationKey(classificationSummary.getKey());
newTaskToCreate.setPrimaryObjRef(DefaultTestEntities.defaultTestObjectReference().build());
Task createdTask = taskService.createTask(newTaskToCreate);
assertThat(createdTask.getCustomAttribute(TaskCustomField.CUSTOM_1))
.isEqualTo("preprocessedCustomField");
}
public static class TestCreateTaskPreprocessorProvider implements CreateTaskPreprocessor {
@Override
public void processTaskBeforeCreation(Task taskToProcess) {
taskToProcess.setCustomAttribute(TaskCustomField.CUSTOM_1, "preprocessedCustomField");
}
}
}

View File

@ -1,13 +0,0 @@
package acceptance.taskpreprocessing;
import pro.taskana.spi.task.api.CreateTaskPreprocessor;
import pro.taskana.task.api.TaskCustomField;
import pro.taskana.task.api.models.Task;
public class TestCreateTaskPreprocessorProvider implements CreateTaskPreprocessor {
@Override
public void processTaskBeforeCreation(Task taskToProcess) {
taskToProcess.setCustomAttribute(TaskCustomField.CUSTOM_1, "preprocessedCustomField");
}
}

View File

@ -3,92 +3,101 @@ package acceptance.taskrouting;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
import acceptance.AbstractAccTest;
import acceptance.DefaultTestEntities;
import acceptance.taskrouting.TaskRoutingAccTest.TaskRoutingProviderForDomainA;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import testapi.TaskanaInject;
import testapi.TaskanaIntegrationTest;
import testapi.WithServiceProvider;
import pro.taskana.classification.api.ClassificationService;
import pro.taskana.classification.api.models.ClassificationSummary;
import pro.taskana.common.api.TaskanaEngine;
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.TaskCustomField;
import pro.taskana.spi.routing.api.TaskRoutingProvider;
import pro.taskana.task.api.TaskService;
import pro.taskana.task.api.models.Task;
import pro.taskana.task.internal.models.TaskImpl;
import pro.taskana.workbasket.api.WorkbasketPermission;
import pro.taskana.workbasket.api.WorkbasketService;
import pro.taskana.workbasket.api.models.WorkbasketSummary;
import pro.taskana.workbasket.internal.builder.WorkbasketAccessItemBuilder;
/** Acceptance test for all "create task" scenarios. */
@ExtendWith(JaasExtension.class)
class TaskRoutingAccTest extends AbstractAccTest {
@WithServiceProvider(
serviceProviderInterface = TaskRoutingProvider.class,
serviceProviders = TaskRoutingProviderForDomainA.class)
@TaskanaIntegrationTest
class TaskRoutingAccTest {
@WithAccessId(user = "admin")
@Test
void testCreateTaskWithoutWorkbasketAndVoidNewTaskMethod() throws Exception {
TaskService taskService = taskanaEngine.getTaskService();
@TaskanaInject TaskanaEngine taskanaEngine;
@TaskanaInject TaskService taskService;
Task newTask = taskService.newTask();
newTask.setClassificationKey("L10303");
newTask.setPrimaryObjRef(
createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"));
final Task taskToCreate = newTask;
assertThatThrownBy(() -> taskService.createTask(taskToCreate))
.isInstanceOf(InvalidArgumentException.class);
((TaskImpl) taskToCreate).setDomain("DOMAIN_C");
assertThatThrownBy(() -> taskService.createTask(taskToCreate))
.isInstanceOf(InvalidArgumentException.class);
((TaskImpl) taskToCreate).setDomain("DOMAIN_B");
Task createdTask = taskService.createTask(taskToCreate);
assertThat(createdTask.getWorkbasketSummary().getId())
.isEqualTo("WBI:100000000000000000000000000000000011");
ClassificationSummary classificationSummary;
WorkbasketSummary domainAWorkbasket;
@WithAccessId(user = "businessadmin")
@BeforeAll
void setUp(ClassificationService classificationService, WorkbasketService workbasketService)
throws Exception {
classificationSummary =
DefaultTestEntities.defaultTestClassification()
.buildAndStore(classificationService)
.asSummary();
domainAWorkbasket =
DefaultTestEntities.defaultTestWorkbasket()
.key("DOMAIN_A_WORKBASKET")
.buildAndStore(workbasketService)
.asSummary();
WorkbasketAccessItemBuilder.newWorkbasketAccessItem()
.workbasketId(domainAWorkbasket.getId())
.accessId("user-1-1")
.permission(WorkbasketPermission.OPEN)
.permission(WorkbasketPermission.READ)
.permission(WorkbasketPermission.APPEND)
.buildAndStore(workbasketService);
TaskRoutingProviderForDomainA.domainAWorkbasketId = domainAWorkbasket.getId();
}
@WithAccessId(user = "admin")
@WithAccessId(user = "user-1-1")
@Test
void testCreateTaskWithNullWorkbasket() throws Exception {
TaskImpl createdTaskA = createTask("DOMAIN_A", "L12010");
assertThat(createdTaskA.getWorkbasketSummary().getId())
.isEqualTo("WBI:100000000000000000000000000000000001");
TaskImpl createdTaskB = createTask("DOMAIN_B", "T21001");
assertThat(createdTaskB.getWorkbasketSummary().getId())
.isEqualTo("WBI:100000000000000000000000000000000011");
assertThatThrownBy(() -> createTask(null, "L12010"))
.isInstanceOf(InvalidArgumentException.class);
void should_ThrowException_When_TaskRouterDoesNotRouteTask() {
Task task = taskService.newTask();
task.setClassificationKey(classificationSummary.getKey());
task.setPrimaryObjRef(DefaultTestEntities.defaultTestObjectReference().build());
assertThatThrownBy(() -> taskService.createTask(task))
.isInstanceOf(InvalidArgumentException.class)
.hasMessage("Cannot create a Task outside a Workbasket");
}
@WithAccessId(user = "admin")
@WithAccessId(user = "user-1-1")
@Test
void testCreateTaskWithNullRouting() throws Exception {
void should_SetWorkbasketForTask_When_TaskRouterDeterminesWorkbasket() throws Exception {
Task task = taskService.newTask(null, "DOMAIN_A");
task.setClassificationKey(classificationSummary.getKey());
task.setPrimaryObjRef(DefaultTestEntities.defaultTestObjectReference().build());
TaskService taskService = taskanaEngine.getTaskService();
Task newTask = taskService.newTask(null, "DOMAIN_A");
newTask.setClassificationKey("L12010");
newTask.setPrimaryObjRef(
createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"));
newTask.setCustomAttribute(TaskCustomField.CUSTOM_7, "noRouting");
assertThatThrownBy(() -> taskService.createTask(newTask))
.isInstanceOf(InvalidArgumentException.class);
Task createdTask = taskService.createTask(task);
assertThat(createdTask.getWorkbasketSummary()).isEqualTo(domainAWorkbasket);
}
@WithAccessId(user = "admin")
@Test
void testCreateTaskWithRoutingToMultipleWorkbaskets() throws Exception {
public static class TaskRoutingProviderForDomainA implements TaskRoutingProvider {
TaskService taskService = taskanaEngine.getTaskService();
Task newTask = taskService.newTask(null, "DOMAIN_B");
newTask.setClassificationKey("L12010");
newTask.setPrimaryObjRef(
createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"));
newTask.setCustomAttribute(TaskCustomField.CUSTOM_7, "multipleWorkbaskets");
assertThatThrownBy(() -> taskService.createTask(newTask))
.isInstanceOf(InvalidArgumentException.class);
}
static String domainAWorkbasketId;
private TaskImpl createTask(String domain, String classificationKey) throws Exception {
TaskService taskService = taskanaEngine.getTaskService();
@Override
public void initialize(TaskanaEngine taskanaEngine) {}
Task newTask = taskService.newTask(null, domain);
newTask.setClassificationKey(classificationKey);
newTask.setPrimaryObjRef(
createObjectReference("COMPANY_A", "SYSTEM_A", "INSTANCE_A", "VNR", "1234567"));
return (TaskImpl) taskService.createTask(newTask);
@Override
public String determineWorkbasketId(Task task) {
if ("DOMAIN_A".equals(task.getDomain())) {
return domainAWorkbasketId;
}
return null;
}
}
}

View File

@ -1,32 +0,0 @@
package acceptance.taskrouting;
import pro.taskana.common.api.TaskanaEngine;
import pro.taskana.spi.routing.api.TaskRoutingProvider;
import pro.taskana.task.api.TaskCustomField;
import pro.taskana.task.api.models.Task;
/** This is a sample implementation of TaskRouter. */
public class TestTaskRoutingProviderForDomainA implements TaskRoutingProvider {
@Override
public void initialize(TaskanaEngine taskanaEngine) {
// no-op
}
@Override
public String determineWorkbasketId(Task task) {
String att7 = task.getCustomAttribute(TaskCustomField.CUSTOM_7);
if (att7 != null && att7.equals("multipleWorkbaskets")) {
return "WBI:100000000000000000000000000000000005";
} else if ("DOMAIN_A".equals(task.getDomain())) {
if (att7 != null && att7.equals("noRouting")) {
return null;
} else {
return "WBI:100000000000000000000000000000000001";
}
} else {
return null;
}
}
}

View File

@ -1,25 +0,0 @@
package acceptance.taskrouting;
import pro.taskana.common.api.TaskanaEngine;
import pro.taskana.spi.routing.api.TaskRoutingProvider;
import pro.taskana.task.api.models.Task;
/** This is a sample implementation of TaskRouter. */
public class TestTaskRoutingProviderForDomainB implements TaskRoutingProvider {
TaskanaEngine theEngine;
@Override
public void initialize(TaskanaEngine taskanaEngine) {
theEngine = taskanaEngine;
}
@Override
public String determineWorkbasketId(Task task) {
if ("DOMAIN_B".equals(task.getDomain())) {
return "WBI:100000000000000000000000000000000011";
} else {
return null;
}
}
}

View File

@ -30,6 +30,7 @@ import pro.taskana.TaskanaEngineConfiguration;
import pro.taskana.common.api.TaskanaEngine;
import pro.taskana.common.api.exceptions.ConcurrencyException;
import pro.taskana.common.internal.InternalTaskanaEngine;
import pro.taskana.spi.history.internal.HistoryEventManager;
import pro.taskana.task.api.TaskQuery;
import pro.taskana.task.api.TaskService;
import pro.taskana.workbasket.api.WorkbasketType;
@ -66,9 +67,12 @@ class WorkbasketServiceImplTest {
@Mock private TaskanaEngineConfiguration taskanaEngineConfigurationMock;
@Mock private HistoryEventManager historyEventManager;
@BeforeEach
void setup() {
lenient().when(internalTaskanaEngineMock.getEngine()).thenReturn(taskanaEngine);
lenient().when(historyEventManager.isEnabled()).thenReturn(false);
}
@Test
@ -93,7 +97,7 @@ class WorkbasketServiceImplTest {
verify(taskanaEngine, times(4)).checkRoleMembership(any());
verify(internalTaskanaEngineMock, times(4)).getEngine();
verify(internalTaskanaEngineMock, times(3)).domainExists(any());
verify(internalTaskanaEngineMock, times(1)).getHistoryEventManager();
verify(historyEventManager, times(5)).isEnabled();
verifyNoMoreInteractions(
taskQueryMock,
taskServiceMock,
@ -139,7 +143,7 @@ class WorkbasketServiceImplTest {
verify(internalTaskanaEngineMock, times(1)).domainExists(any());
verify(distributionTargetMapperMock).deleteAllDistributionTargetsBySourceId(expectedWb.getId());
verify(workbasketMapperMock).update(expectedWb);
verify(internalTaskanaEngineMock, times(1)).getHistoryEventManager();
verify(historyEventManager, times(2)).isEnabled();
verifyNoMoreInteractions(
taskQueryMock,

View File

@ -0,0 +1,24 @@
package testapi;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import testapi.WithServiceProvider.WithServiceProviders;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(WithServiceProviders.class)
public @interface WithServiceProvider {
Class<?> serviceProviderInterface();
Class<?>[] serviceProviders();
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface WithServiceProviders {
WithServiceProvider[] value();
}
}

View File

@ -10,8 +10,12 @@ import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ExtensionContext.Store;
import org.junit.jupiter.api.extension.TestInstancePostProcessor;
import org.junit.platform.commons.JUnitException;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import testapi.CleanTaskanaContext;
import testapi.TaskanaEngineConfigurationModifier;
import testapi.WithServiceProvider;
import testapi.util.ServiceProviderExtractor;
import pro.taskana.TaskanaEngineConfiguration;
import pro.taskana.classification.api.ClassificationService;
@ -24,6 +28,7 @@ import pro.taskana.common.api.security.CurrentUserContext;
import pro.taskana.common.internal.JobServiceImpl;
import pro.taskana.common.internal.TaskanaEngineImpl;
import pro.taskana.common.internal.security.CurrentUserContextImpl;
import pro.taskana.common.internal.util.SpiLoader;
import pro.taskana.monitor.api.MonitorService;
import pro.taskana.monitor.internal.MonitorServiceImpl;
import pro.taskana.task.api.TaskService;
@ -41,6 +46,7 @@ public class TaskanaInitializationExtension implements TestInstancePostProcessor
Class<?> testClass = testInstance.getClass();
if (isTopLevelClass(testClass)
|| isAnnotated(testClass, CleanTaskanaContext.class)
|| isAnnotated(testClass, WithServiceProvider.class)
|| testInstance instanceof TaskanaEngineConfigurationModifier) {
Store store = getClassLevelStore(context);
TaskanaEngineConfiguration taskanaEngineConfiguration =
@ -52,7 +58,14 @@ public class TaskanaInitializationExtension implements TestInstancePostProcessor
modifier.modify(taskanaEngineConfiguration);
}
TaskanaEngine taskanaEngine = taskanaEngineConfiguration.buildTaskanaEngine();
TaskanaEngine taskanaEngine;
try (MockedStatic<SpiLoader> staticMock = Mockito.mockStatic(SpiLoader.class)) {
ServiceProviderExtractor.extractServiceProviders(testClass)
.forEach(
(spi, serviceProviders) ->
staticMock.when(() -> SpiLoader.load(spi)).thenReturn(serviceProviders));
taskanaEngine = taskanaEngineConfiguration.buildTaskanaEngine();
}
taskanaEngine.setConnectionManagementMode(ConnectionManagementMode.AUTOCOMMIT);
store.put(STORE_TASKANA_ENTITY_MAP, generateTaskanaEntityMap(taskanaEngine));

View File

@ -20,6 +20,7 @@ import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.JdbcDatabaseContainer;
import testapi.CleanTaskanaContext;
import testapi.TaskanaEngineConfigurationModifier;
import testapi.WithServiceProvider;
import testapi.util.DockerContainerCreator;
import pro.taskana.common.internal.configuration.DB;
@ -51,7 +52,8 @@ public class TestContainerExtension implements AfterAllCallback, InvocationInter
},
() -> store.put(STORE_DATA_SOURCE, createDataSourceForH2()));
} else if (TaskanaEngineConfigurationModifier.class.isAssignableFrom(testClass)) {
} else if (TaskanaEngineConfigurationModifier.class.isAssignableFrom(testClass)
|| isAnnotated(testClass, WithServiceProvider.class)) {
// since the implementation of TaskanaEngineConfigurationModifier implies the generation of a
// new TaskanaEngine, we have to copy the schema name and datasource from the enclosing class'
// store to the testClass store.

View File

@ -2,15 +2,20 @@ package testapi.tests;
import static org.assertj.core.api.Assertions.assertThat;
import acceptance.priorityservice.TestPriorityServiceProvider;
import java.util.List;
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 testapi.CleanTaskanaContext;
import testapi.TaskanaEngineConfigurationModifier;
import testapi.TaskanaInject;
import testapi.TaskanaIntegrationTest;
import testapi.WithServiceProvider;
import pro.taskana.TaskanaEngineConfiguration;
import pro.taskana.spi.priority.api.PriorityServiceProvider;
@TaskanaIntegrationTest
class TaskanaInitializationExtensionTest {
@ -24,6 +29,7 @@ class TaskanaInitializationExtensionTest {
}
@Nested
@TestInstance(Lifecycle.PER_CLASS)
class ReuseTaskana {
@TaskanaInject TaskanaEngineConfiguration taskanaEngineConfiguration;
@ -36,6 +42,7 @@ class TaskanaInitializationExtensionTest {
}
@Nested
@TestInstance(Lifecycle.PER_CLASS)
class ModifiedTaskanaEngineConfig implements TaskanaEngineConfigurationModifier {
@TaskanaInject TaskanaEngineConfiguration taskanaEngineConfiguration;
@ -59,6 +66,7 @@ class TaskanaInitializationExtensionTest {
@CleanTaskanaContext
@Nested
@TestInstance(Lifecycle.PER_CLASS)
class NestedTestClassAnnotatedWithCleanTaskanaContext {
@TaskanaInject TaskanaEngineConfiguration taskanaEngineConfiguration;
@ -75,4 +83,26 @@ class TaskanaInitializationExtensionTest {
.containsExactlyInAnyOrder("DOMAIN_A", "DOMAIN_B");
}
}
@WithServiceProvider(
serviceProviderInterface = PriorityServiceProvider.class,
serviceProviders = TestPriorityServiceProvider.class)
@Nested
@TestInstance(Lifecycle.PER_CLASS)
class NestedTestClassWithServiceProvider {
@TaskanaInject TaskanaEngineConfiguration taskanaEngineConfiguration;
@Test
void should_createNewTaskanaInstance_For_NestedTestClassAnnotatedWithCleanTaskanaContext() {
assertThat(taskanaEngineConfiguration)
.isNotSameAs(TaskanaInitializationExtensionTest.this.taskanaEngineConfiguration);
}
@Test
void should_UseDefaultTaskanaEngine_When_NestedClassDoesNotImplementModifier() {
assertThat(taskanaEngineConfiguration.getDomains())
.containsExactlyInAnyOrder("DOMAIN_A", "DOMAIN_B");
}
}
}

View File

@ -2,15 +2,20 @@ package testapi.tests;
import static org.assertj.core.api.Assertions.assertThat;
import acceptance.priorityservice.TestPriorityServiceProvider;
import javax.sql.DataSource;
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 testapi.CleanTaskanaContext;
import testapi.TaskanaEngineConfigurationModifier;
import testapi.TaskanaInject;
import testapi.TaskanaIntegrationTest;
import testapi.WithServiceProvider;
import pro.taskana.TaskanaEngineConfiguration;
import pro.taskana.spi.priority.api.PriorityServiceProvider;
@TaskanaIntegrationTest
class TestContainerExtensionTest {
@ -25,6 +30,7 @@ class TestContainerExtensionTest {
}
@Nested
@TestInstance(Lifecycle.PER_CLASS)
class NestedTestClass {
@TaskanaInject TaskanaEngineConfiguration taskanaEngineConfiguration;
@ -39,6 +45,7 @@ class TestContainerExtensionTest {
}
@Nested
@TestInstance(Lifecycle.PER_CLASS)
class NestedTestClassWithConfigurationModifier implements TaskanaEngineConfigurationModifier {
@TaskanaInject TaskanaEngineConfiguration taskanaEngineConfiguration;
@ -59,6 +66,7 @@ class TestContainerExtensionTest {
@CleanTaskanaContext
@Nested
@TestInstance(Lifecycle.PER_CLASS)
class NestedTestClassAnnotatedWithCleanTaskanaContext {
@TaskanaInject TaskanaEngineConfiguration taskanaEngineConfiguration;
@ -74,6 +82,7 @@ class TestContainerExtensionTest {
@CleanTaskanaContext
@Nested
@TestInstance(Lifecycle.PER_CLASS)
class NestedTestClassAnnotatedWithCleanTaskanaContextAndConfigModifier
implements TaskanaEngineConfigurationModifier {
@TaskanaInject TaskanaEngineConfiguration taskanaEngineConfiguration;
@ -92,4 +101,22 @@ class TestContainerExtensionTest {
assertThat(nestedDataSource).isNotSameAs(topLevelDataSource).isNotNull();
}
}
@WithServiceProvider(
serviceProviderInterface = PriorityServiceProvider.class,
serviceProviders = TestPriorityServiceProvider.class)
@Nested
@TestInstance(Lifecycle.PER_CLASS)
class NestedTestClassWithServiceProvider {
@TaskanaInject TaskanaEngineConfiguration taskanaEngineConfiguration;
@Test
void should_ReuseDataSource_For_NestedTestClassWithServiceProvider() {
DataSource nestedDataSource = taskanaEngineConfiguration.getDatasource();
DataSource topLevelDataSource =
TestContainerExtensionTest.this.taskanaEngineConfiguration.getDatasource();
assertThat(nestedDataSource).isSameAs(topLevelDataSource).isNotNull();
}
}
}

View File

@ -7,6 +7,7 @@ import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
import org.junit.jupiter.api.extension.ExtensionContext.Store;
import testapi.CleanTaskanaContext;
import testapi.TaskanaEngineConfigurationModifier;
import testapi.WithServiceProvider;
public class ExtensionCommunicator {
@ -30,7 +31,9 @@ public class ExtensionCommunicator {
if (isTopLevelClass(testClass)) {
return Namespace.create(testClass);
} else if (isAnnotated(testClass, CleanTaskanaContext.class)) {
return Namespace.create(testClass.getEnclosingClass(), testClass);
return Namespace.create(testClass.getEnclosingClass(), testClass, CleanTaskanaContext.class);
} else if (isAnnotated(testClass, WithServiceProvider.class)) {
return Namespace.create(testClass.getEnclosingClass(), testClass, WithServiceProvider.class);
} else if (TaskanaEngineConfigurationModifier.class.isAssignableFrom(testClass)) {
return Namespace.create(
testClass.getEnclosingClass(), testClass, TaskanaEngineConfigurationModifier.class);

View File

@ -0,0 +1,73 @@
package testapi.util;
import static org.junit.platform.commons.support.AnnotationSupport.findRepeatableAnnotations;
import static pro.taskana.common.internal.util.CheckedFunction.wrap;
import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.platform.commons.JUnitException;
import testapi.WithServiceProvider;
import pro.taskana.spi.history.api.TaskanaHistory;
import pro.taskana.spi.priority.api.PriorityServiceProvider;
import pro.taskana.spi.routing.api.TaskRoutingProvider;
import pro.taskana.spi.task.api.CreateTaskPreprocessor;
public class ServiceProviderExtractor {
private static final Set<Class<?>> TASKANA_SERVICE_PROVIDER_INTERFACES =
Set.of(
TaskanaHistory.class,
PriorityServiceProvider.class,
TaskRoutingProvider.class,
CreateTaskPreprocessor.class);
private ServiceProviderExtractor() {
throw new IllegalStateException("utility class");
}
public static Map<Class<?>, List<Object>> extractServiceProviders(Class<?> testClass) {
List<WithServiceProvider> withServiceProviders =
findRepeatableAnnotations(testClass, WithServiceProvider.class);
return groupServiceProvidersByServiceProviderInterface(withServiceProviders).entrySet().stream()
.peek(entry -> validateServiceProviders(entry.getKey(), entry.getValue()))
.collect(
Collectors.toMap(
Entry::getKey, entry -> instantiateServiceProviders(entry.getValue())));
}
private static void validateServiceProviders(Class<?> spi, List<Class<?>> serviceProviders) {
if (!TASKANA_SERVICE_PROVIDER_INTERFACES.contains(spi)) {
throw new JUnitException(String.format("SPI '%s' is not a TASKANA SPI.", spi));
}
if (!serviceProviders.stream().allMatch(spi::isAssignableFrom)) {
throw new JUnitException(
String.format(
"At least one ServiceProvider does not implement the requested SPI '%s'", spi));
}
}
private static Map<Class<?>, List<Class<?>>> groupServiceProvidersByServiceProviderInterface(
List<WithServiceProvider> withServiceProviders) {
return withServiceProviders.stream()
.collect(
Collectors.groupingBy(
WithServiceProvider::serviceProviderInterface,
Collectors.flatMapping(
annotation -> Arrays.stream(annotation.serviceProviders()),
Collectors.toList())));
}
private static List<Object> instantiateServiceProviders(List<Class<?>> serviceProviders) {
return serviceProviders.stream()
.map(wrap(Class::getDeclaredConstructor))
.map(wrap(Constructor::newInstance))
.collect(Collectors.toList());
}
}

View File

@ -1,2 +0,0 @@
acceptance.taskrouting.TestTaskRoutingProviderForDomainA
acceptance.taskrouting.TestTaskRoutingProviderForDomainB

View File

@ -1 +0,0 @@
acceptance.taskpreprocessing.TestCreateTaskPreprocessorProvider

View File

@ -36,7 +36,7 @@ class DmnTaskRouterAccTest extends AbstractAccTest {
@WithAccessId(user = "taskadmin")
@Test
void should_ThrowException_When_DmnTaskRouterFindsNoRule() throws Exception {
void should_ThrowException_When_DmnTaskRouterFindsNoRule() {
Task taskToRoute = taskService.newTask();
taskToRoute.setClassificationKey("T2100");
@ -48,6 +48,6 @@ class DmnTaskRouterAccTest extends AbstractAccTest {
assertThatThrownBy(call)
.isInstanceOf(InvalidArgumentException.class)
.extracting(Throwable::getMessage)
.isEqualTo("Cannot create a task outside a workbasket");
.isEqualTo("Cannot create a Task outside a Workbasket");
}
}