TSK-1628: New DmnTaskRouter module
This commit is contained in:
parent
9247e70092
commit
3ddcd2ae97
|
@ -0,0 +1,194 @@
|
||||||
|
package pro.taskana.common.test.config;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Properties;
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
import org.apache.ibatis.datasource.pooled.PooledDataSource;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/** Integration Test for TaskanaEngineConfiguration. */
|
||||||
|
public final class TaskanaEngineTestConfiguration {
|
||||||
|
|
||||||
|
private static final Logger LOGGER =
|
||||||
|
LoggerFactory.getLogger(TaskanaEngineTestConfiguration.class);
|
||||||
|
private static final int POOL_TIME_TO_WAIT = 50;
|
||||||
|
private static final DataSource DATA_SOURCE;
|
||||||
|
private static String schemaName = null;
|
||||||
|
|
||||||
|
static {
|
||||||
|
String userHomeDirectroy = System.getProperty("user.home");
|
||||||
|
String propertiesFileName = userHomeDirectroy + "/taskanaUnitTest.properties";
|
||||||
|
File f = new File(propertiesFileName);
|
||||||
|
if (f.exists() && !f.isDirectory()) {
|
||||||
|
DATA_SOURCE = createDataSourceFromProperties(propertiesFileName);
|
||||||
|
} else {
|
||||||
|
DATA_SOURCE = createDefaultDataSource();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TaskanaEngineTestConfiguration() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the Datasource used for Junit test. If the file {user.home}/taskanaUnitTest.properties
|
||||||
|
* is present, the Datasource is created according to the properties jdbcDriver, jdbcUrl,
|
||||||
|
* dbUserName and dbPassword. Assuming, the database has the name tskdb, a sample properties file
|
||||||
|
* for DB2 looks as follows: jdbcDriver=com.ibm.db2.jcc.DB2Driver
|
||||||
|
* jdbcUrl=jdbc:db2://localhost:50000/tskdb dbUserName=db2user dbPassword=db2password If any of
|
||||||
|
* these properties is missing, or the file doesn't exist, the default Datasource for h2 in-memory
|
||||||
|
* db is created.
|
||||||
|
*
|
||||||
|
* @return dataSource for unit test
|
||||||
|
*/
|
||||||
|
public static DataSource getDataSource() {
|
||||||
|
return DATA_SOURCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the SchemaName used for Junit test. If the file {user.home}/taskanaUnitTest.properties
|
||||||
|
* is present, the SchemaName is created according to the property schemaName. a sample properties
|
||||||
|
* file for DB2 looks as follows: jdbcDriver=com.ibm.db2.jcc.DB2Driver
|
||||||
|
* jdbcUrl=jdbc:db2://localhost:50000/tskdb dbUserName=db2user dbPassword=db2password
|
||||||
|
* schemaName=TASKANA If any of these properties is missing, or the file doesn't exist, the
|
||||||
|
* default schemaName TASKANA is created used.
|
||||||
|
*
|
||||||
|
* @return String for unit test
|
||||||
|
*/
|
||||||
|
public static String getSchemaName() {
|
||||||
|
if (schemaName == null) {
|
||||||
|
String userHomeDirectroy = System.getProperty("user.home");
|
||||||
|
String propertiesFileName = userHomeDirectroy + "/taskanaUnitTest.properties";
|
||||||
|
File f = new File(propertiesFileName);
|
||||||
|
if (f.exists() && !f.isDirectory()) {
|
||||||
|
schemaName = getSchemaNameFromPropertiesObject(propertiesFileName);
|
||||||
|
} else {
|
||||||
|
schemaName = "TASKANA";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return schemaName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create data source from properties file.
|
||||||
|
*
|
||||||
|
* @param propertiesFileName the name of the property file
|
||||||
|
* @return the parsed datasource.
|
||||||
|
*/
|
||||||
|
public static DataSource createDataSourceFromProperties(String propertiesFileName) {
|
||||||
|
DataSource ds;
|
||||||
|
try (InputStream input = new FileInputStream(propertiesFileName)) {
|
||||||
|
Properties prop = new Properties();
|
||||||
|
prop.load(input);
|
||||||
|
boolean propertiesFileIsComplete = true;
|
||||||
|
String warningMessage = "";
|
||||||
|
String jdbcDriver = prop.getProperty("jdbcDriver");
|
||||||
|
if (jdbcDriver == null || jdbcDriver.length() == 0) {
|
||||||
|
propertiesFileIsComplete = false;
|
||||||
|
warningMessage += ", jdbcDriver property missing";
|
||||||
|
}
|
||||||
|
String jdbcUrl = prop.getProperty("jdbcUrl");
|
||||||
|
if (jdbcUrl == null || jdbcUrl.length() == 0) {
|
||||||
|
propertiesFileIsComplete = false;
|
||||||
|
warningMessage += ", jdbcUrl property missing";
|
||||||
|
}
|
||||||
|
String dbUserName = prop.getProperty("dbUserName");
|
||||||
|
if (dbUserName == null || dbUserName.length() == 0) {
|
||||||
|
propertiesFileIsComplete = false;
|
||||||
|
warningMessage += ", dbUserName property missing";
|
||||||
|
}
|
||||||
|
String dbPassword = prop.getProperty("dbPassword");
|
||||||
|
if (dbPassword == null || dbPassword.length() == 0) {
|
||||||
|
propertiesFileIsComplete = false;
|
||||||
|
warningMessage += ", dbPassword property missing";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (propertiesFileIsComplete) {
|
||||||
|
ds =
|
||||||
|
new PooledDataSource(
|
||||||
|
Thread.currentThread().getContextClassLoader(),
|
||||||
|
jdbcDriver,
|
||||||
|
jdbcUrl,
|
||||||
|
dbUserName,
|
||||||
|
dbPassword);
|
||||||
|
((PooledDataSource) ds)
|
||||||
|
.forceCloseAll(); // otherwise the MyBatis pool is not initialized correctly
|
||||||
|
} else {
|
||||||
|
LOGGER.warn("propertiesFile " + propertiesFileName + " is incomplete" + warningMessage);
|
||||||
|
LOGGER.warn("Using default Datasource for Test");
|
||||||
|
ds = createDefaultDataSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.warn("createDataSourceFromProperties caught Exception " + e);
|
||||||
|
LOGGER.warn("Using default Datasource for Test");
|
||||||
|
ds = createDefaultDataSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ds;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getSchemaNameFromPropertiesObject(String propertiesFileName) {
|
||||||
|
String schemaName = "TASKANA";
|
||||||
|
try (InputStream input = new FileInputStream(propertiesFileName)) {
|
||||||
|
Properties prop = new Properties();
|
||||||
|
prop.load(input);
|
||||||
|
boolean propertiesFileIsComplete = true;
|
||||||
|
String warningMessage = "";
|
||||||
|
schemaName = prop.getProperty("schemaName");
|
||||||
|
if (schemaName == null || schemaName.length() == 0) {
|
||||||
|
propertiesFileIsComplete = false;
|
||||||
|
warningMessage += ", schemaName property missing";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!propertiesFileIsComplete) {
|
||||||
|
LOGGER.warn("propertiesFile " + propertiesFileName + " is incomplete" + warningMessage);
|
||||||
|
LOGGER.warn("Using default Datasource for Test");
|
||||||
|
schemaName = "TASKANA";
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
LOGGER.warn("getSchemaNameFromPropertiesObject caught Exception " + e);
|
||||||
|
LOGGER.warn("Using default schemaName for Test");
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.warn("createDataSourceFromProperties caught Exception " + e);
|
||||||
|
LOGGER.warn("Using default Datasource for Test");
|
||||||
|
}
|
||||||
|
|
||||||
|
return schemaName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create Default Datasource for in-memory database.
|
||||||
|
*
|
||||||
|
* @return the default datasource.
|
||||||
|
*/
|
||||||
|
private static DataSource createDefaultDataSource() {
|
||||||
|
// JdbcDataSource ds = new JdbcDataSource();
|
||||||
|
// ds.setURL("jdbc:h2:mem:taskana;IGNORECASE=TRUE;LOCK_MODE=0");
|
||||||
|
// ds.setPassword("sa");
|
||||||
|
// ds.setUser("sa");
|
||||||
|
|
||||||
|
String jdbcDriver = "org.h2.Driver";
|
||||||
|
String jdbcUrl =
|
||||||
|
"jdbc:h2:mem:taskana;IGNORECASE=TRUE;LOCK_MODE=0;"
|
||||||
|
+ "INIT=CREATE SCHEMA IF NOT EXISTS TASKANA\\;"
|
||||||
|
+ "SET COLLATION DEFAULT_de_DE ";
|
||||||
|
String dbUserName = "sa";
|
||||||
|
String dbPassword = "sa";
|
||||||
|
PooledDataSource ds =
|
||||||
|
new PooledDataSource(
|
||||||
|
Thread.currentThread().getContextClassLoader(),
|
||||||
|
jdbcDriver,
|
||||||
|
jdbcUrl,
|
||||||
|
dbUserName,
|
||||||
|
dbPassword);
|
||||||
|
ds.setPoolTimeToWait(POOL_TIME_TO_WAIT);
|
||||||
|
ds.forceCloseAll(); // otherwise the MyBatis pool is not initialized correctly
|
||||||
|
|
||||||
|
return ds;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package pro.taskana.common.internal.util;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
public class FileLoaderUtil {
|
||||||
|
|
||||||
|
public static boolean loadFromClasspath(String fileToLoad) {
|
||||||
|
boolean loadFromClasspath = true;
|
||||||
|
File f = new File(fileToLoad);
|
||||||
|
if (f.exists() && !f.isDirectory()) {
|
||||||
|
loadFromClasspath = false;
|
||||||
|
}
|
||||||
|
return loadFromClasspath;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -40,6 +40,7 @@ import pro.taskana.common.api.exceptions.WrongCustomHolidayFormatException;
|
||||||
import pro.taskana.common.internal.TaskanaEngineImpl;
|
import pro.taskana.common.internal.TaskanaEngineImpl;
|
||||||
import pro.taskana.common.internal.configuration.DB;
|
import pro.taskana.common.internal.configuration.DB;
|
||||||
import pro.taskana.common.internal.util.CheckedFunction;
|
import pro.taskana.common.internal.util.CheckedFunction;
|
||||||
|
import pro.taskana.common.internal.util.FileLoaderUtil;
|
||||||
import pro.taskana.common.internal.util.Pair;
|
import pro.taskana.common.internal.util.Pair;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -555,9 +556,14 @@ public class TaskanaEngineConfiguration {
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Properties readPropertiesFromFile() {
|
||||||
|
return readPropertiesFromFile(this.propertiesFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private Properties readPropertiesFromFile(String propertiesFile) {
|
private Properties readPropertiesFromFile(String propertiesFile) {
|
||||||
Properties props = new Properties();
|
Properties props = new Properties();
|
||||||
boolean loadFromClasspath = loadFromClasspath(propertiesFile);
|
boolean loadFromClasspath = FileLoaderUtil.loadFromClasspath(propertiesFile);
|
||||||
try {
|
try {
|
||||||
if (loadFromClasspath) {
|
if (loadFromClasspath) {
|
||||||
InputStream inputStream =
|
InputStream inputStream =
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package pro.taskana.common.api;
|
package pro.taskana.common.api;
|
||||||
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import pro.taskana.TaskanaEngineConfiguration;
|
import pro.taskana.TaskanaEngineConfiguration;
|
||||||
import pro.taskana.classification.api.ClassificationService;
|
import pro.taskana.classification.api.ClassificationService;
|
||||||
|
@ -115,6 +116,17 @@ public interface TaskanaEngine {
|
||||||
*/
|
*/
|
||||||
void checkRoleMembership(TaskanaRole... roles) throws NotAuthorizedException;
|
void checkRoleMembership(TaskanaRole... roles) throws NotAuthorizedException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is supposed to skip further permission checks if we are already in a secured
|
||||||
|
* environment. With great power comes great responsibility.
|
||||||
|
*
|
||||||
|
* @param supplier will be executed with admin privileges
|
||||||
|
* @param <T> defined with the supplier return value
|
||||||
|
* @return output from supplier
|
||||||
|
*/
|
||||||
|
<T> T runAsAdmin(Supplier<T> supplier);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the CurrentUserContext class.
|
* Returns the CurrentUserContext class.
|
||||||
*
|
*
|
||||||
|
|
|
@ -84,13 +84,4 @@ public interface InternalTaskanaEngine {
|
||||||
*/
|
*/
|
||||||
CreateTaskPreprocessorManager getCreateTaskPreprocessorManager();
|
CreateTaskPreprocessorManager getCreateTaskPreprocessorManager();
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is supposed to skip further permission checks if we are already in a secured
|
|
||||||
* environment. With great power comes great responsibility.
|
|
||||||
*
|
|
||||||
* @param supplier will be executed with admin privileges
|
|
||||||
* @param <T> defined with the supplier return value
|
|
||||||
* @return output from supplier
|
|
||||||
*/
|
|
||||||
<T> T runAsAdmin(Supplier<T> supplier);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
package pro.taskana.common.internal;
|
package pro.taskana.common.internal;
|
||||||
|
|
||||||
import java.security.AccessController;
|
|
||||||
import java.security.Principal;
|
|
||||||
import java.security.PrivilegedAction;
|
import java.security.PrivilegedAction;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
@ -42,7 +40,7 @@ import pro.taskana.common.api.exceptions.NotAuthorizedException;
|
||||||
import pro.taskana.common.api.exceptions.SystemException;
|
import pro.taskana.common.api.exceptions.SystemException;
|
||||||
import pro.taskana.common.api.exceptions.TaskanaRuntimeException;
|
import pro.taskana.common.api.exceptions.TaskanaRuntimeException;
|
||||||
import pro.taskana.common.api.security.CurrentUserContext;
|
import pro.taskana.common.api.security.CurrentUserContext;
|
||||||
import pro.taskana.common.api.security.GroupPrincipal;
|
import pro.taskana.common.api.security.UserPrincipal;
|
||||||
import pro.taskana.common.internal.configuration.DB;
|
import pro.taskana.common.internal.configuration.DB;
|
||||||
import pro.taskana.common.internal.configuration.DbSchemaCreator;
|
import pro.taskana.common.internal.configuration.DbSchemaCreator;
|
||||||
import pro.taskana.common.internal.configuration.SecurityVerifier;
|
import pro.taskana.common.internal.configuration.SecurityVerifier;
|
||||||
|
@ -252,6 +250,19 @@ public class TaskanaEngineImpl implements TaskanaEngine {
|
||||||
return currentUserContext;
|
return currentUserContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public <T> T runAsAdmin(Supplier<T> supplier) {
|
||||||
|
|
||||||
|
String adminName =
|
||||||
|
this.getConfiguration().getRoleMap().get(TaskanaRole.ADMIN).stream()
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow(() -> new TaskanaRuntimeException("There is no admin configured"));
|
||||||
|
|
||||||
|
Subject subject = new Subject();
|
||||||
|
subject.getPrincipals().add(new UserPrincipal(adminName));
|
||||||
|
|
||||||
|
return Subject.doAs(subject, (PrivilegedAction<T>) supplier::get);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method creates the sqlSessionManager of myBatis. It integrates all the SQL mappers and
|
* This method creates the sqlSessionManager of myBatis. It integrates all the SQL mappers and
|
||||||
* sets the databaseId attribute.
|
* sets the databaseId attribute.
|
||||||
|
@ -460,30 +471,5 @@ public class TaskanaEngineImpl implements TaskanaEngine {
|
||||||
public CreateTaskPreprocessorManager getCreateTaskPreprocessorManager() {
|
public CreateTaskPreprocessorManager getCreateTaskPreprocessorManager() {
|
||||||
return createTaskPreprocessorManager;
|
return createTaskPreprocessorManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> T runAsAdmin(Supplier<T> supplier) {
|
|
||||||
|
|
||||||
Subject subject = Subject.getSubject(AccessController.getContext());
|
|
||||||
if (subject == null) {
|
|
||||||
// dont add authorisation if none is available.
|
|
||||||
return supplier.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<Principal> principalsCopy = new HashSet<>(subject.getPrincipals());
|
|
||||||
Set<Object> privateCredentialsCopy = new HashSet<>(subject.getPrivateCredentials());
|
|
||||||
Set<Object> publicCredentialsCopy = new HashSet<>(subject.getPublicCredentials());
|
|
||||||
|
|
||||||
String adminName =
|
|
||||||
this.getEngine().getConfiguration().getRoleMap().get(TaskanaRole.ADMIN).stream()
|
|
||||||
.findFirst()
|
|
||||||
.orElseThrow(() -> new TaskanaRuntimeException("There is no admin configured"));
|
|
||||||
|
|
||||||
principalsCopy.add(new GroupPrincipal(adminName));
|
|
||||||
Subject subject1 =
|
|
||||||
new Subject(true, principalsCopy, privateCredentialsCopy, publicCredentialsCopy);
|
|
||||||
|
|
||||||
return Subject.doAs(subject1, (PrivilegedAction<T>) supplier::get);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ public class TaskStatusReportBuilderImpl implements TaskStatusReport.Builder {
|
||||||
TaskStatusReport report = new TaskStatusReport(this.states);
|
TaskStatusReport report = new TaskStatusReport(this.states);
|
||||||
report.addItems(tasks);
|
report.addItems(tasks);
|
||||||
Map<String, String> displayMap =
|
Map<String, String> displayMap =
|
||||||
taskanaEngine.runAsAdmin(
|
taskanaEngine.getEngine().runAsAdmin(
|
||||||
() ->
|
() ->
|
||||||
workbasketService.createWorkbasketQuery()
|
workbasketService.createWorkbasketQuery()
|
||||||
.keyIn(report.getRows().keySet().toArray(new String[0]))
|
.keyIn(report.getRows().keySet().toArray(new String[0]))
|
||||||
|
|
|
@ -68,7 +68,7 @@ public class WorkbasketReportBuilderImpl
|
||||||
this.columnHeaders, converter, this.inWorkingDays));
|
this.columnHeaders, converter, this.inWorkingDays));
|
||||||
|
|
||||||
Map<String, String> displayMap =
|
Map<String, String> displayMap =
|
||||||
taskanaEngine.runAsAdmin(
|
taskanaEngine.getEngine().runAsAdmin(
|
||||||
() ->
|
() ->
|
||||||
workbasketService
|
workbasketService
|
||||||
.createWorkbasketQuery()
|
.createWorkbasketQuery()
|
||||||
|
|
|
@ -902,7 +902,7 @@ public class TaskServiceImpl implements TaskService {
|
||||||
serviceLevelHandler.refreshPriorityAndDueDatesOfTasks(
|
serviceLevelHandler.refreshPriorityAndDueDatesOfTasks(
|
||||||
tasks, serviceLevelChanged, priorityChanged);
|
tasks, serviceLevelChanged, priorityChanged);
|
||||||
} else {
|
} else {
|
||||||
taskanaEngine.runAsAdmin(
|
taskanaEngine.getEngine().runAsAdmin(
|
||||||
() -> {
|
() -> {
|
||||||
serviceLevelHandler.refreshPriorityAndDueDatesOfTasks(
|
serviceLevelHandler.refreshPriorityAndDueDatesOfTasks(
|
||||||
tasks, serviceLevelChanged, priorityChanged);
|
tasks, serviceLevelChanged, priorityChanged);
|
||||||
|
|
|
@ -845,7 +845,9 @@ public class WorkbasketServiceImpl implements WorkbasketService {
|
||||||
}
|
}
|
||||||
|
|
||||||
long countTasksNotCompletedInWorkbasket =
|
long countTasksNotCompletedInWorkbasket =
|
||||||
taskanaEngine.runAsAdmin(() -> getCountTasksNotCompletedByWorkbasketId(workbasketId));
|
taskanaEngine
|
||||||
|
.getEngine()
|
||||||
|
.runAsAdmin(() -> getCountTasksNotCompletedByWorkbasketId(workbasketId));
|
||||||
|
|
||||||
if (countTasksNotCompletedInWorkbasket > 0) {
|
if (countTasksNotCompletedInWorkbasket > 0) {
|
||||||
String errorMessage =
|
String errorMessage =
|
||||||
|
@ -856,7 +858,7 @@ public class WorkbasketServiceImpl implements WorkbasketService {
|
||||||
}
|
}
|
||||||
|
|
||||||
long countTasksInWorkbasket =
|
long countTasksInWorkbasket =
|
||||||
taskanaEngine.runAsAdmin(() -> getCountTasksByWorkbasketId(workbasketId));
|
taskanaEngine.getEngine().runAsAdmin(() -> getCountTasksByWorkbasketId(workbasketId));
|
||||||
|
|
||||||
boolean canBeDeletedNow = countTasksInWorkbasket == 0;
|
boolean canBeDeletedNow = countTasksInWorkbasket == 0;
|
||||||
|
|
||||||
|
|
|
@ -156,7 +156,7 @@ class SetOwnerAccTest extends AbstractAccTest {
|
||||||
resetDb(false);
|
resetDb(false);
|
||||||
List<TaskSummary> allTaskSummaries =
|
List<TaskSummary> allTaskSummaries =
|
||||||
new TaskanaEngineProxy(taskanaEngine)
|
new TaskanaEngineProxy(taskanaEngine)
|
||||||
.getEngine()
|
.getEngine().getEngine()
|
||||||
.runAsAdmin(() -> taskanaEngine.getTaskService().createTaskQuery().list());
|
.runAsAdmin(() -> taskanaEngine.getTaskService().createTaskQuery().list());
|
||||||
List<String> allTaskIds =
|
List<String> allTaskIds =
|
||||||
allTaskSummaries.stream().map(TaskSummary::getId).collect(Collectors.toList());
|
allTaskSummaries.stream().map(TaskSummary::getId).collect(Collectors.toList());
|
||||||
|
|
|
@ -37,7 +37,7 @@ class TaskEngineAccTest extends AbstractAccTest {
|
||||||
assertThat(taskanaEngine.isUserInRole(TaskanaRole.ADMIN)).isFalse();
|
assertThat(taskanaEngine.isUserInRole(TaskanaRole.ADMIN)).isFalse();
|
||||||
|
|
||||||
new TaskanaEngineProxy(taskanaEngine)
|
new TaskanaEngineProxy(taskanaEngine)
|
||||||
.getEngine()
|
.getEngine().getEngine()
|
||||||
.runAsAdmin(() -> assertThat(taskanaEngine.isUserInRole(TaskanaRole.ADMIN)).isTrue());
|
.runAsAdmin(() -> assertThat(taskanaEngine.isUserInRole(TaskanaRole.ADMIN)).isTrue());
|
||||||
|
|
||||||
assertThat(taskanaEngine.isUserInRole(TaskanaRole.ADMIN)).isFalse();
|
assertThat(taskanaEngine.isUserInRole(TaskanaRole.ADMIN)).isFalse();
|
||||||
|
|
5
pom.xml
5
pom.xml
|
@ -18,6 +18,7 @@
|
||||||
<!-- History is an optional module. -->
|
<!-- History is an optional module. -->
|
||||||
<module>history</module>
|
<module>history</module>
|
||||||
<module>ci/taskana-sonar-test-coverage</module>
|
<module>ci/taskana-sonar-test-coverage</module>
|
||||||
|
<module>taskana-routing-parent</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
@ -58,6 +59,10 @@
|
||||||
<!-- wildfly dependencies -->
|
<!-- wildfly dependencies -->
|
||||||
<version.wildfly>13.0.0.Final</version.wildfly>
|
<version.wildfly>13.0.0.Final</version.wildfly>
|
||||||
|
|
||||||
|
<!-- camunda dependencies -->
|
||||||
|
<version.camunda.dmn>7.14.0</version.camunda.dmn>
|
||||||
|
|
||||||
|
|
||||||
<!-- java ee dependencies -->
|
<!-- java ee dependencies -->
|
||||||
<version.resteasy>4.6.0.Final</version.resteasy>
|
<version.resteasy>4.6.0.Final</version.resteasy>
|
||||||
<version.thorntail>2.7.0.Final</version.thorntail>
|
<version.thorntail>2.7.0.Final</version.thorntail>
|
||||||
|
|
|
@ -10,6 +10,9 @@ import org.springframework.context.annotation.DependsOn;
|
||||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||||
import org.springframework.transaction.PlatformTransactionManager;
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
|
|
||||||
|
import pro.taskana.TaskanaEngineConfiguration;
|
||||||
|
import pro.taskana.common.api.TaskanaEngine;
|
||||||
|
import pro.taskana.common.internal.configuration.DbSchemaCreator;
|
||||||
import pro.taskana.sampledata.SampleDataGenerator;
|
import pro.taskana.sampledata.SampleDataGenerator;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
|
@ -21,18 +24,30 @@ public class ExampleRestConfiguration {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@DependsOn("getTaskanaEngine") // generate sample data after schema was inserted
|
@DependsOn("taskanaEngineConfiguration") // generate sample data after schema was inserted
|
||||||
public SampleDataGenerator generateSampleData(
|
public SampleDataGenerator generateSampleData(
|
||||||
|
TaskanaEngineConfiguration taskanaEngineConfiguration,
|
||||||
DataSource dataSource,
|
DataSource dataSource,
|
||||||
@Value("${taskana.schemaName:TASKANA}") String schemaName,
|
@Value("${generateSampleData:true}") boolean generateSampleData)
|
||||||
@Value("${generateSampleData:true}") boolean generateSampleData) {
|
throws SQLException {
|
||||||
SampleDataGenerator sampleDataGenerator = new SampleDataGenerator(dataSource, schemaName);
|
DbSchemaCreator dbSchemaCreator =
|
||||||
|
new DbSchemaCreator(dataSource, taskanaEngineConfiguration.getSchemaName());
|
||||||
|
dbSchemaCreator.run();
|
||||||
|
SampleDataGenerator sampleDataGenerator =
|
||||||
|
new SampleDataGenerator(dataSource, taskanaEngineConfiguration.getSchemaName());
|
||||||
if (generateSampleData) {
|
if (generateSampleData) {
|
||||||
sampleDataGenerator.generateSampleData();
|
sampleDataGenerator.generateSampleData();
|
||||||
}
|
}
|
||||||
return sampleDataGenerator;
|
return sampleDataGenerator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@DependsOn("generateSampleData")
|
||||||
|
public TaskanaEngine getTaskanaEngine(TaskanaEngineConfiguration taskanaEngineConfiguration)
|
||||||
|
throws SQLException {
|
||||||
|
return taskanaEngineConfiguration.buildTaskanaEngine();
|
||||||
|
}
|
||||||
|
|
||||||
// only required to let the adapter example connect to the same database
|
// only required to let the adapter example connect to the same database
|
||||||
@Bean(initMethod = "start", destroyMethod = "stop")
|
@Bean(initMethod = "start", destroyMethod = "stop")
|
||||||
public Server inMemoryH2DatabaseaServer() throws SQLException {
|
public Server inMemoryH2DatabaseaServer() throws SQLException {
|
||||||
|
|
|
@ -53,6 +53,7 @@ public class RestConfiguration {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
@ConditionalOnMissingBean(TaskanaEngine.class)
|
||||||
public TaskanaEngine getTaskanaEngine(TaskanaEngineConfiguration taskanaEngineConfiguration)
|
public TaskanaEngine getTaskanaEngine(TaskanaEngineConfiguration taskanaEngineConfiguration)
|
||||||
throws SQLException {
|
throws SQLException {
|
||||||
return taskanaEngineConfiguration.buildTaskanaEngine();
|
return taskanaEngineConfiguration.buildTaskanaEngine();
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>taskana-routing-parent</artifactId>
|
||||||
|
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
|
<name>${project.groupId}:${project.artifactId}</name>
|
||||||
|
<description>This pom is parent to all taskana routing modules</description>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>pro.taskana</groupId>
|
||||||
|
<artifactId>taskana-parent</artifactId>
|
||||||
|
<version>4.5.2-SNAPSHOT</version>
|
||||||
|
<relativePath>../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<modules>
|
||||||
|
<module>taskana-spi-routing-dmn-router</module>
|
||||||
|
</modules>
|
||||||
|
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,63 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>taskana-routing-parent</artifactId>
|
||||||
|
<groupId>pro.taskana</groupId>
|
||||||
|
<version>4.5.2-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>taskana-spi-routing-dmn-router</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>pro.taskana</groupId>
|
||||||
|
<artifactId>taskana-core</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.camunda.bpm.model</groupId>
|
||||||
|
<artifactId>camunda-dmn-model</artifactId>
|
||||||
|
<version>${version.camunda.dmn}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.camunda.bpm.dmn</groupId>
|
||||||
|
<artifactId>camunda-engine-dmn</artifactId>
|
||||||
|
<version>${version.camunda.dmn}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- test dependencies start -->
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>pro.taskana</groupId>
|
||||||
|
<artifactId>taskana-common-data</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>pro.taskana</groupId>
|
||||||
|
<artifactId>taskana-common-test</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.h2database</groupId>
|
||||||
|
<artifactId>h2</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<!-- test dependencies end -->
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,211 @@
|
||||||
|
package pro.taskana.routing.dmn;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.camunda.bpm.dmn.engine.DmnDecision;
|
||||||
|
import org.camunda.bpm.dmn.engine.DmnDecisionTableResult;
|
||||||
|
import org.camunda.bpm.dmn.engine.DmnEngine;
|
||||||
|
import org.camunda.bpm.dmn.engine.DmnEngineConfiguration;
|
||||||
|
import org.camunda.bpm.engine.variable.VariableMap;
|
||||||
|
import org.camunda.bpm.engine.variable.Variables;
|
||||||
|
import org.camunda.bpm.model.dmn.Dmn;
|
||||||
|
import org.camunda.bpm.model.dmn.DmnModelInstance;
|
||||||
|
import org.camunda.bpm.model.dmn.instance.OutputEntry;
|
||||||
|
import org.camunda.bpm.model.dmn.instance.Rule;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import pro.taskana.common.api.TaskanaEngine;
|
||||||
|
import pro.taskana.common.api.exceptions.NotAuthorizedException;
|
||||||
|
import pro.taskana.common.api.exceptions.SystemException;
|
||||||
|
import pro.taskana.common.internal.util.FileLoaderUtil;
|
||||||
|
import pro.taskana.common.internal.util.Pair;
|
||||||
|
import pro.taskana.spi.routing.api.TaskRoutingProvider;
|
||||||
|
import pro.taskana.task.api.models.Task;
|
||||||
|
import pro.taskana.workbasket.api.WorkbasketService;
|
||||||
|
import pro.taskana.workbasket.api.exceptions.WorkbasketNotFoundException;
|
||||||
|
|
||||||
|
public class DmnTaskRouter implements TaskRoutingProvider {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(DmnTaskRouter.class);
|
||||||
|
private static final String DECISION_ID = "workbasketRouting";
|
||||||
|
private static final String DECISION_VARIABLE_MAP_NAME = "task";
|
||||||
|
private static final String OUTPUT_WORKBASKET_KEY = "workbasketKey";
|
||||||
|
private static final String OUTPUT_DOMAIN = "domain";
|
||||||
|
private static final String DMN_TABLE_PROPERTY = "taskana.routing.dmn";
|
||||||
|
private TaskanaEngine taskanaEngine;
|
||||||
|
private DmnEngine dmnEngine;
|
||||||
|
private DmnDecision decision;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(TaskanaEngine taskanaEngine) {
|
||||||
|
|
||||||
|
if (LOGGER.isDebugEnabled()) {
|
||||||
|
LOGGER.debug("Entering initialize()");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.taskanaEngine = taskanaEngine;
|
||||||
|
dmnEngine = DmnEngineConfiguration.createDefaultDmnEngineConfiguration().buildEngine();
|
||||||
|
|
||||||
|
DmnModelInstance dmnModel = readModelFromDmnTable();
|
||||||
|
|
||||||
|
decision = dmnEngine.parseDecision(DECISION_ID, dmnModel);
|
||||||
|
|
||||||
|
validateOutputs(dmnModel);
|
||||||
|
|
||||||
|
if (LOGGER.isDebugEnabled()) {
|
||||||
|
LOGGER.debug("Exiting initialize()");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String determineWorkbasketId(Task task) {
|
||||||
|
|
||||||
|
if (LOGGER.isDebugEnabled()) {
|
||||||
|
LOGGER.debug("Entering determineWorkbasketId(task = {})", task);
|
||||||
|
}
|
||||||
|
|
||||||
|
VariableMap variables = Variables.putValue(DECISION_VARIABLE_MAP_NAME, task);
|
||||||
|
|
||||||
|
DmnDecisionTableResult result = dmnEngine.evaluateDecisionTable(decision, variables);
|
||||||
|
|
||||||
|
if (result.getSingleResult() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String workbasketKey = result.getSingleResult().getEntry(OUTPUT_WORKBASKET_KEY);
|
||||||
|
String domain = result.getSingleResult().getEntry(OUTPUT_DOMAIN);
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
String determinedWorkbasketId =
|
||||||
|
taskanaEngine.getWorkbasketService().getWorkbasket(workbasketKey, domain).getId();
|
||||||
|
if (LOGGER.isDebugEnabled()) {
|
||||||
|
LOGGER.debug(
|
||||||
|
String.format("Exiting determineWorkbasketId, returning %s", determinedWorkbasketId));
|
||||||
|
}
|
||||||
|
return determinedWorkbasketId;
|
||||||
|
} catch (WorkbasketNotFoundException e) {
|
||||||
|
throw new SystemException(
|
||||||
|
String.format(
|
||||||
|
"Unknown workbasket defined in DMN Table. key: '%s', domain: '%s'",
|
||||||
|
workbasketKey, domain));
|
||||||
|
} catch (NotAuthorizedException e) {
|
||||||
|
throw new SystemException(
|
||||||
|
String.format(
|
||||||
|
"The current user is not authorized to create a task in the routed workbasket. "
|
||||||
|
+ "key: '%s', domain: '%s'",
|
||||||
|
workbasketKey, domain));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Set<Pair<String, String>> getAllWorkbasketAndDomainOutputs(DmnModelInstance dmnModel) {
|
||||||
|
|
||||||
|
if (LOGGER.isDebugEnabled()) {
|
||||||
|
LOGGER.debug("Entering getAllWorkbasketAndDomainOutputs()");
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Pair<String, String>> allWorkbasketAndDomainOutputs = new HashSet<>();
|
||||||
|
|
||||||
|
for (Rule rule : dmnModel.getModelElementsByType(Rule.class)) {
|
||||||
|
|
||||||
|
List<OutputEntry> outputEntries = new ArrayList<>(rule.getOutputEntries());
|
||||||
|
String workbasketKey = outputEntries.get(0).getTextContent();
|
||||||
|
String domain = outputEntries.get(1).getTextContent();
|
||||||
|
|
||||||
|
allWorkbasketAndDomainOutputs.add(Pair.of(workbasketKey, domain));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LOGGER.isDebugEnabled()) {
|
||||||
|
LOGGER.debug("Exiting getAllWorkbasketAndDomainOutputs()");
|
||||||
|
}
|
||||||
|
|
||||||
|
return allWorkbasketAndDomainOutputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected DmnModelInstance readModelFromDmnTable() {
|
||||||
|
|
||||||
|
if (LOGGER.isDebugEnabled()) {
|
||||||
|
LOGGER.debug("Entering readModelFromDmnTable()");
|
||||||
|
}
|
||||||
|
|
||||||
|
String pathToDmn =
|
||||||
|
taskanaEngine.getConfiguration().readPropertiesFromFile().getProperty(DMN_TABLE_PROPERTY);
|
||||||
|
|
||||||
|
if (FileLoaderUtil.loadFromClasspath(pathToDmn)) {
|
||||||
|
try (InputStream inputStream = DmnTaskRouter.class.getResourceAsStream(pathToDmn)) {
|
||||||
|
if (inputStream == null) {
|
||||||
|
LOGGER.error("dmn file {} was not found on classpath.", pathToDmn);
|
||||||
|
} else {
|
||||||
|
return Dmn.readModelFromStream(inputStream);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.error("caught IOException when processing dmn file");
|
||||||
|
throw new SystemException("Internal System error when processing dmn file", e.getCause());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try (FileInputStream inputStream = new FileInputStream(pathToDmn)) {
|
||||||
|
return Dmn.readModelFromStream(inputStream);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new SystemException(
|
||||||
|
String.format("Could not find a dmn file with provided path %s", pathToDmn));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateOutputs(DmnModelInstance dmnModel) {
|
||||||
|
|
||||||
|
if (LOGGER.isDebugEnabled()) {
|
||||||
|
LOGGER.debug("Entering validateOutputs()");
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Pair<String, String>> allWorkbasketAndDomainOutputs =
|
||||||
|
getAllWorkbasketAndDomainOutputs(dmnModel);
|
||||||
|
|
||||||
|
validate(allWorkbasketAndDomainOutputs);
|
||||||
|
|
||||||
|
if (LOGGER.isDebugEnabled()) {
|
||||||
|
LOGGER.debug("Exiting validateOutputs()");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validate(Set<Pair<String, String>> allWorkbasketAndDomainOutputs) {
|
||||||
|
|
||||||
|
if (LOGGER.isDebugEnabled()) {
|
||||||
|
LOGGER.debug(
|
||||||
|
"Entering validate(allWorkbasketAndDomainOutputs = {}", allWorkbasketAndDomainOutputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
WorkbasketService workbasketService = taskanaEngine.getWorkbasketService();
|
||||||
|
|
||||||
|
for (Pair<String, String> pair : allWorkbasketAndDomainOutputs) {
|
||||||
|
String workbasketKey = pair.getLeft().replace("\"", "");
|
||||||
|
String domain = pair.getRight().replace("\"", "");
|
||||||
|
// This can be replaced with a workbasketQuery call.
|
||||||
|
// Unfortunately the WorkbasketQuery does not support a keyDomainIn operation.
|
||||||
|
// Therefore we fetch every workbasket separately
|
||||||
|
|
||||||
|
taskanaEngine.runAsAdmin(
|
||||||
|
() -> {
|
||||||
|
try {
|
||||||
|
return workbasketService.getWorkbasket(workbasketKey, domain);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new SystemException(
|
||||||
|
String.format(
|
||||||
|
"Unknown workbasket defined in DMN Table. key: '%s', domain: '%s'",
|
||||||
|
workbasketKey, domain),
|
||||||
|
e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LOGGER.isDebugEnabled()) {
|
||||||
|
LOGGER.debug("Exiting validate()");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package acceptance;
|
||||||
|
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
|
||||||
|
import pro.taskana.TaskanaEngineConfiguration;
|
||||||
|
import pro.taskana.common.api.TaskanaEngine;
|
||||||
|
import pro.taskana.common.api.TaskanaEngine.ConnectionManagementMode;
|
||||||
|
import pro.taskana.common.api.WorkingDaysToDaysConverter;
|
||||||
|
import pro.taskana.common.internal.configuration.DbSchemaCreator;
|
||||||
|
import pro.taskana.common.test.config.TaskanaEngineTestConfiguration;
|
||||||
|
import pro.taskana.sampledata.SampleDataGenerator;
|
||||||
|
import pro.taskana.task.api.models.ObjectReference;
|
||||||
|
|
||||||
|
public abstract class AbstractAccTest {
|
||||||
|
|
||||||
|
protected static TaskanaEngineConfiguration taskanaEngineConfiguration;
|
||||||
|
protected static TaskanaEngine taskanaEngine;
|
||||||
|
protected static WorkingDaysToDaysConverter converter;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
protected static void setupTest() throws Exception {
|
||||||
|
resetDb(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void resetDb(boolean dropTables) throws Exception {
|
||||||
|
|
||||||
|
DataSource dataSource = TaskanaEngineTestConfiguration.getDataSource();
|
||||||
|
String schemaName = TaskanaEngineTestConfiguration.getSchemaName();
|
||||||
|
SampleDataGenerator sampleDataGenerator = new SampleDataGenerator(dataSource, schemaName);
|
||||||
|
if (dropTables) {
|
||||||
|
sampleDataGenerator.dropDb();
|
||||||
|
}
|
||||||
|
dataSource = TaskanaEngineTestConfiguration.getDataSource();
|
||||||
|
taskanaEngineConfiguration = new TaskanaEngineConfiguration(dataSource, false, schemaName);
|
||||||
|
taskanaEngineConfiguration.setGermanPublicHolidaysEnabled(true);
|
||||||
|
DbSchemaCreator dbSchemaCreator =
|
||||||
|
new DbSchemaCreator(dataSource, taskanaEngineConfiguration.getSchemaName());
|
||||||
|
dbSchemaCreator.run();
|
||||||
|
sampleDataGenerator.clearDb();
|
||||||
|
sampleDataGenerator.generateTestData();
|
||||||
|
taskanaEngine = taskanaEngineConfiguration.buildTaskanaEngine();
|
||||||
|
taskanaEngine.setConnectionManagementMode(ConnectionManagementMode.AUTOCOMMIT);
|
||||||
|
converter = taskanaEngine.getWorkingDaysToDaysConverter();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ObjectReference createObjectReference(
|
||||||
|
String company, String system, String systemInstance, String type, String value) {
|
||||||
|
ObjectReference objectReference = new ObjectReference();
|
||||||
|
objectReference.setCompany(company);
|
||||||
|
objectReference.setSystem(system);
|
||||||
|
objectReference.setSystemInstance(systemInstance);
|
||||||
|
objectReference.setType(type);
|
||||||
|
objectReference.setValue(value);
|
||||||
|
return objectReference;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
package pro.taskana.routing.dmn;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
|
||||||
|
import acceptance.AbstractAccTest;
|
||||||
|
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
|
||||||
|
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.TaskService;
|
||||||
|
import pro.taskana.task.api.models.ObjectReference;
|
||||||
|
import pro.taskana.task.api.models.Task;
|
||||||
|
|
||||||
|
@ExtendWith(JaasExtension.class)
|
||||||
|
public class DmnTaskRouterTest extends AbstractAccTest {
|
||||||
|
|
||||||
|
private final TaskService taskService = taskanaEngine.getTaskService();
|
||||||
|
|
||||||
|
@WithAccessId(user = "taskadmin")
|
||||||
|
@Test
|
||||||
|
void should_RouteTaskToCorrectWorkbasket_When_DmnTaskRouterFindsRule() throws Exception {
|
||||||
|
|
||||||
|
Task taskToRoute = taskService.newTask();
|
||||||
|
taskToRoute.setClassificationKey("T2100");
|
||||||
|
ObjectReference objectReference =
|
||||||
|
createObjectReference("company", null, null, "MyType1", "00000001");
|
||||||
|
taskToRoute.setPrimaryObjRef(objectReference);
|
||||||
|
|
||||||
|
Task routedTask = taskService.createTask(taskToRoute);
|
||||||
|
assertThat(routedTask.getWorkbasketKey()).isEqualTo("GPK_KSC");
|
||||||
|
}
|
||||||
|
|
||||||
|
@WithAccessId(user = "taskadmin")
|
||||||
|
@Test
|
||||||
|
void should_ThrowException_When_DmnTaskRouterFindsNoRule() throws Exception {
|
||||||
|
|
||||||
|
Task taskToRoute = taskService.newTask();
|
||||||
|
taskToRoute.setClassificationKey("T2100");
|
||||||
|
ObjectReference objectReference =
|
||||||
|
createObjectReference("company", null, null, "MyTeö", "000002");
|
||||||
|
taskToRoute.setPrimaryObjRef(objectReference);
|
||||||
|
|
||||||
|
ThrowingCallable call = () -> taskService.createTask(taskToRoute);
|
||||||
|
assertThatThrownBy(call)
|
||||||
|
.isInstanceOf(InvalidArgumentException.class)
|
||||||
|
.extracting(ex -> ex.getMessage())
|
||||||
|
.isEqualTo("Cannot create a task outside a workbasket");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
pro.taskana.routing.dmn.DmnTaskRouter
|
|
@ -0,0 +1,64 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<definitions xmlns:ns0="http://camunda.org/schema/1.0/dmn" xmlns="http://www.omg.org/spec/DMN/20151101/dmn.xsd" xmlns:biodi="http://bpmn.io/schema/dmn/biodi/1.0" id="definitions" name="definitions" namespace="http://camunda.org/schema/1.0/dmn" exporter="Camunda Modeler" exporterVersion="3.3.5">
|
||||||
|
<decision id="workbasketRouting" name="Workbasket Routing">
|
||||||
|
<extensionElements>
|
||||||
|
<biodi:bounds x="150" y="150" width="180" height="80" />
|
||||||
|
</extensionElements>
|
||||||
|
<decisionTable id="workbasketRouting_decisionTable" hitPolicy="FIRST">
|
||||||
|
<input id="workbasketRouting_decisionTable-input_2" label="porValue" ns0:inputVariable="input">
|
||||||
|
<inputExpression id="workbasketRouting_decisionTable-inputExpression_2" typeRef="string">
|
||||||
|
<text>task.primaryObjRef.value</text>
|
||||||
|
</inputExpression>
|
||||||
|
</input>
|
||||||
|
<input id="InputClause_1qkek3h" label="porType" ns0:inputVariable="input">
|
||||||
|
<inputExpression id="LiteralExpression_1k84ufl" typeRef="string">
|
||||||
|
<text>task.primaryObjRef.type</text>
|
||||||
|
</inputExpression>
|
||||||
|
</input>
|
||||||
|
<output id="workbasketRouting_decisionTable-output_workbasketKey" label="Workbasket key" name="workbasketKey" typeRef="string" />
|
||||||
|
<output id="workbasketRouting_decisionTable-output_domain" label="Domain" name="domain" typeRef="string" />
|
||||||
|
<rule id="workbasketRouting_decisionTable-rule_0">
|
||||||
|
<inputEntry id="workbasketRouting_decisionTable-rule_0-inputEntry_2">
|
||||||
|
<text>"00000001"</text>
|
||||||
|
</inputEntry>
|
||||||
|
<inputEntry id="UnaryTests_0n5ac31">
|
||||||
|
<text>"MyType1"</text>
|
||||||
|
</inputEntry>
|
||||||
|
<outputEntry id="workbasketRouting_decisionTable-rule_0-outputEntry_workbasketKey">
|
||||||
|
<text>"GPK_KSC"</text>
|
||||||
|
</outputEntry>
|
||||||
|
<outputEntry id="workbasketRouting_decisionTable-rule_0-outputEntry_domain">
|
||||||
|
<text>"DOMAIN_A"</text>
|
||||||
|
</outputEntry>
|
||||||
|
</rule>
|
||||||
|
<rule id="DecisionRule_0ua6ja6">
|
||||||
|
<inputEntry id="UnaryTests_1kdwhvv">
|
||||||
|
<text>"00000001"</text>
|
||||||
|
</inputEntry>
|
||||||
|
<inputEntry id="UnaryTests_03hbryc">
|
||||||
|
<text></text>
|
||||||
|
</inputEntry>
|
||||||
|
<outputEntry id="workbasketRouting_decisionTable-rule_1-outputEntry_workbasketKey">
|
||||||
|
<text>"GPK_KSC_1"</text>
|
||||||
|
</outputEntry>
|
||||||
|
<outputEntry id="workbasketRouting_decisionTable-rule_1-outputEntry_domain">
|
||||||
|
<text>"DOMAIN_A"</text>
|
||||||
|
</outputEntry>
|
||||||
|
</rule>
|
||||||
|
<rule id="DecisionRule_0elr8ov">
|
||||||
|
<inputEntry id="UnaryTests_0n7qv3k">
|
||||||
|
<text></text>
|
||||||
|
</inputEntry>
|
||||||
|
<inputEntry id="UnaryTests_0jwi2ra">
|
||||||
|
<text>"MyType1"</text>
|
||||||
|
</inputEntry>
|
||||||
|
<outputEntry id="workbasketRouting_decisionTable-rule_2-outputEntry_workbasketKey">
|
||||||
|
<text>"GPK_KSC_2"</text>
|
||||||
|
</outputEntry>
|
||||||
|
<outputEntry id="workbasketRouting_decisionTable-rule_2-outputEntry_domain">
|
||||||
|
<text>"DOMAIN_A"</text>
|
||||||
|
</outputEntry>
|
||||||
|
</rule>
|
||||||
|
</decisionTable>
|
||||||
|
</decision>
|
||||||
|
</definitions>
|
|
@ -0,0 +1,25 @@
|
||||||
|
taskana.roles.user=teamlead-1 | teamlead-2 | user-1-1 | user-1-2 | user-2-1 | user-2-2 | user-b-1 | user-b-2
|
||||||
|
taskana.roles.admin=admin | uid=admin,cn=users,OU=Test,O=TASKANA
|
||||||
|
taskana.roles.businessadmin=businessadmin | cn=business-admins,cn=groups,OU=Test,O=TASKANA
|
||||||
|
taskana.roles.monitor=monitor | cn=monitor-users,cn=groups,OU=Test,O=TASKANA
|
||||||
|
taskana.roles.taskadmin=taskadmin
|
||||||
|
|
||||||
|
taskana.domains=DOMAIN_A,DOMAIN_B,DOMAIN_C
|
||||||
|
taskana.classification.types=TASK,DOCUMENT
|
||||||
|
taskana.classification.categories.task= EXTERNAL, manual, autoMAtic, Process
|
||||||
|
taskana.classification.categories.document= EXTERNAL
|
||||||
|
|
||||||
|
taskana.jobs.maxRetries=3
|
||||||
|
taskana.jobs.batchSize=50
|
||||||
|
taskana.jobs.cleanup.runEvery=P1D
|
||||||
|
taskana.jobs.cleanup.firstRunAt=2018-07-25T08:00:00Z
|
||||||
|
taskana.jobs.cleanup.minimumAge=P14D
|
||||||
|
taskana.jobs.history.batchSize=50
|
||||||
|
taskana.jobs.history.cleanup.firstRunAt=2018-07-25T08:00:00Z
|
||||||
|
taskana.jobs.history.cleanup.minimumAge=P14D
|
||||||
|
taskana.jobs.history.cleanup.runEvery=P1D
|
||||||
|
taskana.german.holidays.enabled=true
|
||||||
|
taskana.german.holidays.corpus-christi.enabled=true
|
||||||
|
taskana.historylogger.name=AUDIT
|
||||||
|
|
||||||
|
taskana.routing.dmn=/dmn-table.dmn
|
Loading…
Reference in New Issue