TSK-1737: created ConfigurationService which manages custom attributes

This commit is contained in:
Mustapha Zorgati 2021-09-29 09:58:48 +02:00
parent 4ae94ff108
commit 05fce27222
31 changed files with 607 additions and 169 deletions

View File

@ -44,7 +44,6 @@ public class DbSchemaCreator {
private DataSource dataSource; private DataSource dataSource;
public DbSchemaCreator(DataSource dataSource, String schema) { public DbSchemaCreator(DataSource dataSource, String schema) {
super();
this.dataSource = dataSource; this.dataSource = dataSource;
this.schemaName = schema; this.schemaName = schema;
} }

View File

@ -1,98 +0,0 @@
package pro.taskana.common.internal.configuration;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.apache.ibatis.jdbc.SqlRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pro.taskana.common.api.exceptions.SystemException;
public class SecurityVerifier {
public static final String SECURITY_FLAG_COLUMN_NAME = "ENFORCE_SECURITY";
public static final String INSERT_SECURITY_FLAG_SQL =
"INSERT INTO %s.CONFIGURATION (" + SECURITY_FLAG_COLUMN_NAME + " ) VALUES (%b)";
public static final String SELECT_SECURITY_FLAG_SQL = "SELECT %s FROM %s.CONFIGURATION";
private static final Logger LOGGER = LoggerFactory.getLogger(SecurityVerifier.class);
private final String schemaName;
private final DataSource dataSource;
public SecurityVerifier(DataSource dataSource, String schema) {
this.dataSource = dataSource;
this.schemaName = schema;
}
public void checkSecureAccess(boolean securityEnabled) {
try (Connection connection = dataSource.getConnection()) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(connection.getMetaData().toString());
}
SqlRunner sqlRunner = new SqlRunner(connection);
String querySecurity =
String.format(SELECT_SECURITY_FLAG_SQL, SECURITY_FLAG_COLUMN_NAME, schemaName);
if ((boolean) sqlRunner.selectOne(querySecurity).get(SECURITY_FLAG_COLUMN_NAME)
&& !securityEnabled) {
LOGGER.error("Tried to start TASKANA in unsecured mode while secured mode is enforced!");
throw new SystemException(
"Secured TASKANA mode is enforced, can't start in unsecured mode");
}
} catch (SQLException ex) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(
String.format(
"Security-mode is not yet set. Setting security flag to %b", securityEnabled));
}
setInitialSecurityMode(securityEnabled);
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Security-mode is enabled");
}
}
private void setInitialSecurityMode(boolean securityEnabled) {
try (Connection connection = dataSource.getConnection()) {
String setSecurityFlagSql =
String.format(INSERT_SECURITY_FLAG_SQL, schemaName, securityEnabled);
try (PreparedStatement preparedStatement = connection.prepareStatement(setSecurityFlagSql)) {
preparedStatement.execute();
if (!connection.getAutoCommit()) {
connection.commit();
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("Successfully set security-mode to %b", securityEnabled));
}
} catch (SQLException ex) {
LOGGER.error(
"Caught exception while trying to set the initial TASKANA security mode. "
+ "Aborting start-up process!",
ex);
throw new SystemException(
"Couldn't set initial TASKANA security mode. Aborting start-up process!");
}
} catch (SQLException ex) {
LOGGER.error("Caught exception while trying to retrieve connection from datasource ", ex);
}
}
}

View File

@ -0,0 +1,19 @@
package pro.taskana.common.internal.util;
import pro.taskana.common.api.exceptions.SystemException;
@FunctionalInterface
public interface CheckedRunnable {
static Runnable wrap(CheckedRunnable checkedRunnable) {
return () -> {
try {
checkedRunnable.run();
} catch (Exception e) {
throw new SystemException("Caught exception", e);
}
};
}
void run() throws Exception;
}

View File

@ -0,0 +1,28 @@
package pro.taskana.common.internal.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.util.stream.Collectors;
public class ResourceUtil {
private ResourceUtil() {
throw new IllegalStateException("utility class");
}
public static String readResourceAsString(Class<?> clazz, String resource) throws IOException {
try (InputStream fileStream = clazz.getResourceAsStream(resource)) {
if (fileStream == null) {
return null;
}
try (Reader inputStreamReader = new InputStreamReader(fileStream, StandardCharsets.UTF_8);
BufferedReader reader = new BufferedReader(inputStreamReader)) {
return reader.lines().collect(Collectors.joining(System.lineSeparator()));
}
}
}
}

View File

@ -2,7 +2,7 @@ CREATE SCHEMA IF NOT EXISTS %schemaName%;
SET SCHEMA %schemaName%; SET SCHEMA %schemaName%;
-- MOved to h2 jdbc connect string, because of taskana-spring-example project is using two schemas. -- Moved to h2 jdbc connect string, because of taskana-spring-example project is using two schemas.
-- There the order of the schema setup's (dbcustom, taskana) should be switched then we can set collation -- There the order of the schema setup's (dbcustom, taskana) should be switched then we can set collation
-- here in this file! -- here in this file!
-- SET COLLATION DEFAULT_de_DE; -- SET COLLATION DEFAULT_de_DE;

View File

@ -0,0 +1,22 @@
package pro.taskana.common.internal.util;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;
public class ResourceUtilTest {
@Test
void should_loadResource() throws Exception {
String resourceAsString = ResourceUtil.readResourceAsString(getClass(), "fileInClasspath.txt");
assertThat(resourceAsString).isEqualTo("This file is in the classpath");
}
@Test
void should_ReturnNull_When_ResourceDoesNotExist() throws Exception {
String resourceAsString = ResourceUtil.readResourceAsString(getClass(), "doesNotExist");
assertThat(resourceAsString).isNull();
}
}

View File

@ -115,11 +115,12 @@ public abstract class AbstractAccTest {
dataSource, dataSource,
false, false,
schemaName != null && !schemaName.isEmpty() ? schemaName : getSchemaName()); schemaName != null && !schemaName.isEmpty() ? schemaName : getSchemaName());
taskanaEngine = taskanaEngineConfiguration.buildTaskanaEngine(); taskanaEngine =
taskanaEngine.setConnectionManagementMode(ConnectionManagementMode.AUTOCOMMIT); taskanaEngineConfiguration.buildTaskanaEngine(ConnectionManagementMode.AUTOCOMMIT);
taskanaHistoryEngine = TaskanaHistoryEngineImpl.createTaskanaEngine(taskanaEngine); taskanaHistoryEngine = TaskanaHistoryEngineImpl.createTaskanaEngine(taskanaEngine);
historyService = new SimpleHistoryServiceImpl(); historyService = new SimpleHistoryServiceImpl();
historyService.initialize(taskanaEngineConfiguration.buildTaskanaEngine()); historyService.initialize(
taskanaEngineConfiguration.buildTaskanaEngine(ConnectionManagementMode.AUTOCOMMIT));
SampleDataGenerator sampleDataGenerator = new SampleDataGenerator(dataSource, getSchemaName()); SampleDataGenerator sampleDataGenerator = new SampleDataGenerator(dataSource, getSchemaName());
sampleDataGenerator.clearDb(); sampleDataGenerator.clearDb();

View File

@ -30,6 +30,7 @@ import org.slf4j.LoggerFactory;
import pro.taskana.common.api.CustomHoliday; import pro.taskana.common.api.CustomHoliday;
import pro.taskana.common.api.TaskanaEngine; import pro.taskana.common.api.TaskanaEngine;
import pro.taskana.common.api.TaskanaEngine.ConnectionManagementMode;
import pro.taskana.common.api.TaskanaRole; import pro.taskana.common.api.TaskanaRole;
import pro.taskana.common.api.exceptions.SystemException; import pro.taskana.common.api.exceptions.SystemException;
import pro.taskana.common.api.exceptions.WrongCustomHolidayFormatException; import pro.taskana.common.api.exceptions.WrongCustomHolidayFormatException;
@ -211,6 +212,11 @@ public class TaskanaEngineConfiguration {
return TaskanaEngineImpl.createTaskanaEngine(this); return TaskanaEngineImpl.createTaskanaEngine(this);
} }
public TaskanaEngine buildTaskanaEngine(ConnectionManagementMode connectionManagementMode)
throws SQLException {
return TaskanaEngineImpl.createTaskanaEngine(this, connectionManagementMode);
}
/** /**
* This method creates a PooledDataSource, if the needed properties are provided. * This method creates a PooledDataSource, if the needed properties are provided.
* *

View File

@ -0,0 +1,13 @@
package pro.taskana.common.api;
import java.util.Map;
import java.util.Optional;
public interface ConfigurationService {
Map<String, Object> getAllCustomAttributes();
void setAllCustomAttributes(Map<String, ?> customAttributes);
Optional<Object> getValue(String attribute);
}

View File

@ -52,6 +52,8 @@ public interface TaskanaEngine {
UserService getUserService(); UserService getUserService();
ConfigurationService getConfigurationService();
/** /**
* The Taskana configuration. * The Taskana configuration.
* *

View File

@ -0,0 +1,22 @@
package pro.taskana.common.internal;
import java.util.Map;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
public interface ConfigurationMapper {
@Select("SELECT ENFORCE_SECURITY FROM CONFIGURATION")
Boolean isSecurityEnabled();
@Insert("INSERT INTO CONFIGURATION(ENFORCE_SECURITY) VALUES (#{securityEnabled})")
void setSecurityEnabled(@Param("securityEnabled") boolean securityEnabled);
@Select("SELECT CUSTOM_ATTRIBUTES FROM TASKANA.CONFIGURATION")
Map<String, Object> getAllCustomAttributes();
@Update("UPDATE TASKANA.CONFIGURATION SET CUSTOM_ATTRIBUTES = #{customAttributes}")
void setAllCustomAttributes(@Param("customAttributes") Map<String, ?> customAttributes);
}

View File

@ -0,0 +1,81 @@
package pro.taskana.common.internal;
import java.io.IOException;
import java.util.Map;
import java.util.Optional;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pro.taskana.common.api.ConfigurationService;
import pro.taskana.common.api.exceptions.SystemException;
import pro.taskana.common.internal.util.CheckedRunnable;
import pro.taskana.common.internal.util.ResourceUtil;
public class ConfigurationServiceImpl implements ConfigurationService {
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationServiceImpl.class);
private final InternalTaskanaEngine internalTaskanaEngine;
private final ConfigurationMapper mapper;
public ConfigurationServiceImpl(
InternalTaskanaEngine internalTaskanaEngine, ConfigurationMapper mapper) {
this.internalTaskanaEngine = internalTaskanaEngine;
this.mapper = mapper;
}
public void checkSecureAccess(boolean securityEnabled) {
Boolean isSecurityEnabled =
internalTaskanaEngine.executeInDatabaseConnection(mapper::isSecurityEnabled);
if (isSecurityEnabled == null) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Security-mode is not yet set. Setting security flag to {}", securityEnabled);
}
mapper.setSecurityEnabled(securityEnabled);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Successfully set security mode to {}", securityEnabled);
}
} else if (isSecurityEnabled && !securityEnabled) {
LOGGER.error("Tried to start TASKANA in unsecured mode while secured mode is enforced!");
throw new SystemException("Secured TASKANA mode is enforced, can't start in unsecured mode");
}
}
public void setupDefaultCustomAttributes() {
internalTaskanaEngine.executeInDatabaseConnection(
CheckedRunnable.wrap(
() -> {
if (mapper.getAllCustomAttributes() == null) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("custom attributes are not set. Setting default value");
}
setAllCustomAttributes(generateDefaultCustomAttributes());
}
}));
}
@Override
public Map<String, Object> getAllCustomAttributes() {
return internalTaskanaEngine.executeInDatabaseConnection(mapper::getAllCustomAttributes);
}
@Override
public void setAllCustomAttributes(Map<String, ?> customAttributes) {
internalTaskanaEngine.executeInDatabaseConnection(
() -> mapper.setAllCustomAttributes(customAttributes));
}
@Override
public Optional<Object> getValue(String attribute) {
return Optional.ofNullable(getAllCustomAttributes().get(attribute));
}
private Map<String, Object> generateDefaultCustomAttributes() throws IOException {
JSONObject jsonObject =
new JSONObject(
ResourceUtil.readResourceAsString(getClass(), "defaultCustomAttributes.json"));
return jsonObject.toMap();
}
}

View File

@ -17,7 +17,7 @@ import pro.taskana.spi.task.internal.CreateTaskPreprocessorManager;
public interface InternalTaskanaEngine { public interface InternalTaskanaEngine {
/** /**
* Opens the connection to the database. Has to be called at the begin of each Api call that * Opens the connection to the database. Has to be called at the beginning of each Api call that
* accesses the database * accesses the database
*/ */
void openConnection(); void openConnection();

View File

@ -30,6 +30,7 @@ import pro.taskana.classification.api.ClassificationService;
import pro.taskana.classification.internal.ClassificationMapper; import pro.taskana.classification.internal.ClassificationMapper;
import pro.taskana.classification.internal.ClassificationQueryMapper; import pro.taskana.classification.internal.ClassificationQueryMapper;
import pro.taskana.classification.internal.ClassificationServiceImpl; import pro.taskana.classification.internal.ClassificationServiceImpl;
import pro.taskana.common.api.ConfigurationService;
import pro.taskana.common.api.JobService; import pro.taskana.common.api.JobService;
import pro.taskana.common.api.TaskanaEngine; import pro.taskana.common.api.TaskanaEngine;
import pro.taskana.common.api.TaskanaRole; import pro.taskana.common.api.TaskanaRole;
@ -43,7 +44,6 @@ import pro.taskana.common.api.security.CurrentUserContext;
import pro.taskana.common.api.security.UserPrincipal; 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.persistence.InstantTypeHandler; import pro.taskana.common.internal.persistence.InstantTypeHandler;
import pro.taskana.common.internal.persistence.MapTypeHandler; import pro.taskana.common.internal.persistence.MapTypeHandler;
import pro.taskana.common.internal.security.CurrentUserContextImpl; import pro.taskana.common.internal.security.CurrentUserContextImpl;
@ -85,19 +85,20 @@ public class TaskanaEngineImpl implements TaskanaEngine {
private final WorkingDaysToDaysConverter workingDaysToDaysConverter; private final WorkingDaysToDaysConverter workingDaysToDaysConverter;
private final HistoryEventManager historyEventManager; private final HistoryEventManager historyEventManager;
private final CurrentUserContext currentUserContext; private final CurrentUserContext currentUserContext;
private final ConfigurationServiceImpl configurationService;
protected TaskanaEngineConfiguration taskanaEngineConfiguration; protected TaskanaEngineConfiguration taskanaEngineConfiguration;
protected TransactionFactory transactionFactory; protected TransactionFactory transactionFactory;
protected SqlSessionManager sessionManager; protected SqlSessionManager sessionManager;
protected ConnectionManagementMode mode = ConnectionManagementMode.PARTICIPATE; protected ConnectionManagementMode mode;
protected Connection connection = null; protected Connection connection;
protected TaskanaEngineImpl(TaskanaEngineConfiguration taskanaEngineConfiguration) protected TaskanaEngineImpl(
TaskanaEngineConfiguration taskanaEngineConfiguration,
ConnectionManagementMode connectionManagementMode)
throws SQLException { throws SQLException {
this.taskanaEngineConfiguration = taskanaEngineConfiguration; this.taskanaEngineConfiguration = taskanaEngineConfiguration;
createTransactionFactory(taskanaEngineConfiguration.getUseManagedTransactions()); this.mode = connectionManagementMode;
this.sessionManager = createSqlSessionManager(); internalTaskanaEngineImpl = new InternalTaskanaEngineImpl();
initializeDbSchema(taskanaEngineConfiguration);
this.internalTaskanaEngineImpl = new InternalTaskanaEngineImpl();
workingDaysToDaysConverter = workingDaysToDaysConverter =
new WorkingDaysToDaysConverter( new WorkingDaysToDaysConverter(
taskanaEngineConfiguration.isGermanPublicHolidaysEnabled(), taskanaEngineConfiguration.isGermanPublicHolidaysEnabled(),
@ -105,18 +106,31 @@ public class TaskanaEngineImpl implements TaskanaEngine {
taskanaEngineConfiguration.getCustomHolidays()); taskanaEngineConfiguration.getCustomHolidays());
currentUserContext = currentUserContext =
new CurrentUserContextImpl(TaskanaEngineConfiguration.shouldUseLowerCaseForAccessIds()); new CurrentUserContextImpl(TaskanaEngineConfiguration.shouldUseLowerCaseForAccessIds());
createTransactionFactory(taskanaEngineConfiguration.getUseManagedTransactions());
sessionManager = createSqlSessionManager();
configurationService =
new ConfigurationServiceImpl(
internalTaskanaEngineImpl, sessionManager.getMapper(ConfigurationMapper.class));
initializeDbSchema(taskanaEngineConfiguration);
// IMPORTANT: SPI has to be initialized last (and in this order) in order // IMPORTANT: SPI has to be initialized last (and in this order) in order
// to provide a fully initialized TaskanaEngine instance during the SPI initialization! // to provide a fully initialized TaskanaEngine instance during the SPI initialization!
historyEventManager = new HistoryEventManager(this);
taskRoutingManager = new TaskRoutingManager(this);
priorityServiceManager = new PriorityServiceManager(); priorityServiceManager = new PriorityServiceManager();
createTaskPreprocessorManager = new CreateTaskPreprocessorManager(); createTaskPreprocessorManager = new CreateTaskPreprocessorManager();
historyEventManager = new HistoryEventManager(this);
taskRoutingManager = new TaskRoutingManager(this);
} }
public static TaskanaEngine createTaskanaEngine( public static TaskanaEngine createTaskanaEngine(
TaskanaEngineConfiguration taskanaEngineConfiguration) throws SQLException { TaskanaEngineConfiguration taskanaEngineConfiguration) throws SQLException {
return new TaskanaEngineImpl(taskanaEngineConfiguration); return createTaskanaEngine(taskanaEngineConfiguration, ConnectionManagementMode.PARTICIPATE);
}
public static TaskanaEngine createTaskanaEngine(
TaskanaEngineConfiguration taskanaEngineConfiguration,
ConnectionManagementMode connectionManagementMode)
throws SQLException {
return new TaskanaEngineImpl(taskanaEngineConfiguration, connectionManagementMode);
} }
@Override @Override
@ -171,6 +185,11 @@ public class TaskanaEngineImpl implements TaskanaEngine {
internalTaskanaEngineImpl, sessionManager.getMapper(UserMapper.class)); internalTaskanaEngineImpl, sessionManager.getMapper(UserMapper.class));
} }
@Override
public ConfigurationService getConfigurationService() {
return configurationService;
}
@Override @Override
public TaskanaEngineConfiguration getConfiguration() { public TaskanaEngineConfiguration getConfiguration() {
return this.taskanaEngineConfiguration; return this.taskanaEngineConfiguration;
@ -330,6 +349,7 @@ public class TaskanaEngineImpl implements TaskanaEngine {
configuration.addMapper(AttachmentMapper.class); configuration.addMapper(AttachmentMapper.class);
configuration.addMapper(JobMapper.class); configuration.addMapper(JobMapper.class);
configuration.addMapper(UserMapper.class); configuration.addMapper(UserMapper.class);
configuration.addMapper(ConfigurationMapper.class);
SqlSessionFactory localSessionFactory = new SqlSessionFactoryBuilder().build(configuration); SqlSessionFactory localSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
return SqlSessionManager.newInstance(localSessionFactory); return SqlSessionManager.newInstance(localSessionFactory);
} }
@ -346,9 +366,8 @@ public class TaskanaEngineImpl implements TaskanaEngine {
"The Database Schema Version doesn't match the expected minimal version " "The Database Schema Version doesn't match the expected minimal version "
+ MINIMAL_TASKANA_SCHEMA_VERSION); + MINIMAL_TASKANA_SCHEMA_VERSION);
} }
new SecurityVerifier( configurationService.checkSecureAccess(taskanaEngineConfiguration.isSecurityEnabled());
taskanaEngineConfiguration.getDatasource(), taskanaEngineConfiguration.getSchemaName()) configurationService.setupDefaultCustomAttributes();
.checkSecureAccess(taskanaEngineConfiguration.isSecurityEnabled());
} }
/** /**

View File

@ -0,0 +1,106 @@
{
"nameHighPrio": "High Prio",
"nameMediumPrio": "Medium P",
"nameLowPrio": "Low Prio",
"intervalHighPrio": [
100,
200
],
"intervalMediumPrio": [
21,
100
],
"intervalLowPrio": [
1,
20
],
"colorHighPrio": "#ff0000",
"colorLowPrio": "#5FAD00",
"colorMediumPrio": "#FFEE00",
"filter": {
"Filter": {
"custom-1": [
"true"
],
"custom-5": [
"3"
]
},
"Filter neu": {
"custom-5": [
"7",
"5"
]
},
"Filter Doppelt": {
"custom-5": [
"1",
"2",
"90"
]
}
},
"schema": {
"Priority-Report": {
"displayName": "Priority Report",
"members": {
"nameHighPrio": {
"displayName": "High Prio Name",
"type": "text",
"min": 1
},
"nameMediumPrio": {
"displayName": "Medium Prio Name",
"type": "text",
"min": 1,
"max": 8
},
"nameLowPrio": {
"displayName": "Low Prio Name",
"type": "text",
"min": 2,
"max": 64
},
"intervalHighPrio": {
"displayName": "High Prio Interval",
"type": "interval",
"min": 0,
"max": 300
},
"intervalMediumPrio": {
"displayName": "Medium Prio Interval",
"type": "interval",
"min": -5,
"max": 300
},
"intervalLowPrio": {
"displayName": "Low Prio Interval",
"type": "interval",
"min": 0
},
"colorHighPrio": {
"displayName": "High Prio Color",
"type": "color"
},
"colorMediumPrio": {
"displayName": "Medium Prio Color",
"type": "color"
},
"colorLowPrio": {
"displayName": "Low Prio Color",
"type": "color"
}
}
},
"Filter": {
"displayName": "Filter for Task-Priority-Report",
"members": {
"filter": {
"displayName": "Filter values",
"type": "json",
"min": 1
}
}
}
}
}

View File

@ -56,8 +56,8 @@ public abstract class AbstractAccTest {
if (dropTables) { if (dropTables) {
sampleDataGenerator.dropDb(); sampleDataGenerator.dropDb();
} }
taskanaEngine = taskanaEngineConfiguration.buildTaskanaEngine(); taskanaEngine =
taskanaEngine.setConnectionManagementMode(ConnectionManagementMode.AUTOCOMMIT); taskanaEngineConfiguration.buildTaskanaEngine(ConnectionManagementMode.AUTOCOMMIT);
converter = taskanaEngine.getWorkingDaysToDaysConverter(); converter = taskanaEngine.getWorkingDaysToDaysConverter();
sampleDataGenerator.clearDb(); sampleDataGenerator.clearDb();
sampleDataGenerator.generateTestData(); sampleDataGenerator.generateTestData();

View File

@ -0,0 +1,90 @@
package acceptance.config;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Map;
import java.util.Optional;
import org.json.JSONObject;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.junit.jupiter.api.TestMethodOrder;
import testapi.TaskanaInject;
import testapi.TaskanaIntegrationTest;
import pro.taskana.common.internal.ConfigurationMapper;
import pro.taskana.common.internal.ConfigurationServiceImpl;
import pro.taskana.common.internal.util.ResourceUtil;
@TaskanaIntegrationTest
public class ConfigurationServiceImplAccTest {
@TaskanaInject ConfigurationServiceImpl configurationService;
@TaskanaInject ConfigurationMapper configurationMapper;
@Nested
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@TestInstance(Lifecycle.PER_CLASS)
class CustomAttribute {
@Test
@Order(1)
void
should_SetDefaultCustomAttributesDuringTaskanaInitialization_When_NoCustomAttributesAreSet()
throws Exception {
Map<String, Object> expectedCustomAttributes =
new JSONObject(
ResourceUtil.readResourceAsString(
ConfigurationServiceImpl.class, "defaultCustomAttributes.json"))
.toMap();
Map<String, Object> allCustomAttributes = configurationMapper.getAllCustomAttributes();
assertThat(allCustomAttributes).isEqualTo(expectedCustomAttributes);
}
@Test
void should_NotSetDefaultCustomAttributes_When_CustomAttributesAreAlreadySet() {
Map<String, String> foo = Map.of("foo", "bar");
configurationService.setAllCustomAttributes(foo);
configurationService.setupDefaultCustomAttributes();
Map<String, Object> allCustomAttributes = configurationService.getAllCustomAttributes();
assertThat(allCustomAttributes).isEqualTo(foo);
}
@Test
void should_SetCustomAttributes() {
Map<String, String> foo = Map.of("foo1", "bar");
configurationService.setAllCustomAttributes(foo);
Map<String, Object> allCustomAttributes = configurationService.getAllCustomAttributes();
assertThat(allCustomAttributes).isEqualTo(foo);
}
@Test
void should_RetrieveCustomAttributes() {
Map<String, String> foo = Map.of("foo2", "bar");
configurationService.setAllCustomAttributes(foo);
Optional<Object> customAttribute = configurationService.getValue("foo2");
assertThat(customAttribute).hasValue("bar");
}
@Test
void should_ReturnNull_When_CustomAttributeDoesNotExist() {
Map<String, String> foo = Map.of("foo3", "bar");
configurationService.setAllCustomAttributes(foo);
Optional<Object> customAttribute = configurationService.getValue("doesNotExist");
assertThat(customAttribute).isEmpty();
}
}
}

View File

@ -15,7 +15,6 @@ import org.junit.jupiter.api.Test;
import pro.taskana.TaskanaEngineConfiguration; import pro.taskana.TaskanaEngineConfiguration;
import pro.taskana.common.api.exceptions.SystemException; import pro.taskana.common.api.exceptions.SystemException;
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.test.config.DataSourceGenerator; import pro.taskana.common.test.config.DataSourceGenerator;
import pro.taskana.sampledata.SampleDataGenerator; import pro.taskana.sampledata.SampleDataGenerator;
@ -89,9 +88,7 @@ class TaskanaSecurityConfigAccTest {
String selectSecurityFlagSql = String selectSecurityFlagSql =
String.format( String.format(
SecurityVerifier.SELECT_SECURITY_FLAG_SQL, "SELECT ENFORCE_SECURITY FROM %s.CONFIGURATION", DataSourceGenerator.getSchemaName());
SecurityVerifier.SECURITY_FLAG_COLUMN_NAME,
DataSourceGenerator.getSchemaName());
Statement statement = connection.createStatement(); Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(selectSecurityFlagSql); ResultSet resultSet = statement.executeQuery(selectSecurityFlagSql);
@ -110,9 +107,8 @@ class TaskanaSecurityConfigAccTest {
String sql = String sql =
String.format( String.format(
SecurityVerifier.INSERT_SECURITY_FLAG_SQL, "INSERT INTO %s.CONFIGURATION (ENFORCE_SECURITY) VALUES (%b)",
DataSourceGenerator.getSchemaName(), DataSourceGenerator.getSchemaName(), securityFlag);
securityFlag);
Statement statement = connection.createStatement(); Statement statement = connection.createStatement();
statement.execute(sql); statement.execute(sql);

View File

@ -8,6 +8,7 @@ import java.time.Instant;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.List; import java.util.List;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.mockito.internal.stubbing.answers.CallsRealMethods; import org.mockito.internal.stubbing.answers.CallsRealMethods;
@ -23,6 +24,7 @@ import pro.taskana.common.internal.jobs.PlainJavaTransactionProvider;
import pro.taskana.common.test.config.DataSourceGenerator; import pro.taskana.common.test.config.DataSourceGenerator;
import pro.taskana.task.internal.jobs.TaskCleanupJob; import pro.taskana.task.internal.jobs.TaskCleanupJob;
@Disabled
class JobRunnerAccTest extends AbstractAccTest { class JobRunnerAccTest extends AbstractAccTest {
private final JobServiceImpl jobService = (JobServiceImpl) taskanaEngine.getJobService(); private final JobServiceImpl jobService = (JobServiceImpl) taskanaEngine.getJobService();
@ -38,8 +40,8 @@ class JobRunnerAccTest extends AbstractAccTest {
runInThread( runInThread(
() -> { () -> {
try { try {
TaskanaEngine taskanaEngine = taskanaEngineConfiguration.buildTaskanaEngine(); TaskanaEngine taskanaEngine =
taskanaEngine.setConnectionManagementMode(ConnectionManagementMode.AUTOCOMMIT); taskanaEngineConfiguration.buildTaskanaEngine(ConnectionManagementMode.AUTOCOMMIT);
DataSource dataSource = DataSourceGenerator.getDataSource(); DataSource dataSource = DataSourceGenerator.getDataSource();
// We have to slow down the transaction. // We have to slow down the transaction.
// This is necessary to guarantee the execution of // This is necessary to guarantee the execution of

View File

@ -4,8 +4,10 @@ import static org.junit.platform.commons.support.AnnotationSupport.isAnnotated;
import static testapi.util.ExtensionCommunicator.getClassLevelStore; import static testapi.util.ExtensionCommunicator.getClassLevelStore;
import static testapi.util.ExtensionCommunicator.isTopLevelClass; import static testapi.util.ExtensionCommunicator.isTopLevelClass;
import acceptance.TaskanaEngineProxy;
import java.util.Map; import java.util.Map;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ExtensionContext.Store; import org.junit.jupiter.api.extension.ExtensionContext.Store;
import org.junit.jupiter.api.extension.TestInstancePostProcessor; import org.junit.jupiter.api.extension.TestInstancePostProcessor;
@ -20,11 +22,15 @@ import testapi.util.ServiceProviderExtractor;
import pro.taskana.TaskanaEngineConfiguration; import pro.taskana.TaskanaEngineConfiguration;
import pro.taskana.classification.api.ClassificationService; import pro.taskana.classification.api.ClassificationService;
import pro.taskana.classification.internal.ClassificationServiceImpl; import pro.taskana.classification.internal.ClassificationServiceImpl;
import pro.taskana.common.api.ConfigurationService;
import pro.taskana.common.api.JobService; import pro.taskana.common.api.JobService;
import pro.taskana.common.api.TaskanaEngine; import pro.taskana.common.api.TaskanaEngine;
import pro.taskana.common.api.TaskanaEngine.ConnectionManagementMode; import pro.taskana.common.api.TaskanaEngine.ConnectionManagementMode;
import pro.taskana.common.api.WorkingDaysToDaysConverter; import pro.taskana.common.api.WorkingDaysToDaysConverter;
import pro.taskana.common.api.security.CurrentUserContext; import pro.taskana.common.api.security.CurrentUserContext;
import pro.taskana.common.internal.ConfigurationMapper;
import pro.taskana.common.internal.ConfigurationServiceImpl;
import pro.taskana.common.internal.InternalTaskanaEngine;
import pro.taskana.common.internal.JobServiceImpl; import pro.taskana.common.internal.JobServiceImpl;
import pro.taskana.common.internal.TaskanaEngineImpl; import pro.taskana.common.internal.TaskanaEngineImpl;
import pro.taskana.common.internal.security.CurrentUserContextImpl; import pro.taskana.common.internal.security.CurrentUserContextImpl;
@ -64,9 +70,9 @@ public class TaskanaInitializationExtension implements TestInstancePostProcessor
.forEach( .forEach(
(spi, serviceProviders) -> (spi, serviceProviders) ->
staticMock.when(() -> SpiLoader.load(spi)).thenReturn(serviceProviders)); staticMock.when(() -> SpiLoader.load(spi)).thenReturn(serviceProviders));
taskanaEngine = taskanaEngineConfiguration.buildTaskanaEngine(); taskanaEngine =
taskanaEngineConfiguration.buildTaskanaEngine(ConnectionManagementMode.AUTOCOMMIT);
} }
taskanaEngine.setConnectionManagementMode(ConnectionManagementMode.AUTOCOMMIT);
store.put(STORE_TASKANA_ENTITY_MAP, generateTaskanaEntityMap(taskanaEngine)); store.put(STORE_TASKANA_ENTITY_MAP, generateTaskanaEntityMap(taskanaEngine));
} }
@ -85,17 +91,21 @@ public class TaskanaInitializationExtension implements TestInstancePostProcessor
return new TaskanaEngineConfiguration(dataSource, false, schemaName); return new TaskanaEngineConfiguration(dataSource, false, schemaName);
} }
private static Map<Class<?>, Object> generateTaskanaEntityMap(TaskanaEngine taskanaEngine) { private static Map<Class<?>, Object> generateTaskanaEntityMap(TaskanaEngine taskanaEngine)
throws Exception {
TaskService taskService = taskanaEngine.getTaskService(); TaskService taskService = taskanaEngine.getTaskService();
TaskanaEngineProxy taskanaEngineProxy = new TaskanaEngineProxy(taskanaEngine);
MonitorService monitorService = taskanaEngine.getMonitorService(); MonitorService monitorService = taskanaEngine.getMonitorService();
WorkbasketService workbasketService = taskanaEngine.getWorkbasketService(); WorkbasketService workbasketService = taskanaEngine.getWorkbasketService();
ClassificationService classificationService = taskanaEngine.getClassificationService(); ClassificationService classificationService = taskanaEngine.getClassificationService();
JobService jobService = taskanaEngine.getJobService(); JobService jobService = taskanaEngine.getJobService();
CurrentUserContext currentUserContext = taskanaEngine.getCurrentUserContext(); CurrentUserContext currentUserContext = taskanaEngine.getCurrentUserContext();
SqlSession sqlSession = taskanaEngineProxy.getSqlSession();
return Map.ofEntries( return Map.ofEntries(
Map.entry(TaskanaEngineConfiguration.class, taskanaEngine.getConfiguration()), Map.entry(TaskanaEngineConfiguration.class, taskanaEngine.getConfiguration()),
Map.entry(TaskanaEngineImpl.class, taskanaEngine), Map.entry(TaskanaEngineImpl.class, taskanaEngine),
Map.entry(TaskanaEngine.class, taskanaEngine), Map.entry(TaskanaEngine.class, taskanaEngine),
Map.entry(InternalTaskanaEngine.class, taskanaEngineProxy.getEngine()),
Map.entry(TaskService.class, taskService), Map.entry(TaskService.class, taskService),
Map.entry(TaskServiceImpl.class, taskService), Map.entry(TaskServiceImpl.class, taskService),
Map.entry(MonitorService.class, monitorService), Map.entry(MonitorService.class, monitorService),
@ -104,10 +114,13 @@ public class TaskanaInitializationExtension implements TestInstancePostProcessor
Map.entry(WorkbasketServiceImpl.class, workbasketService), Map.entry(WorkbasketServiceImpl.class, workbasketService),
Map.entry(ClassificationService.class, classificationService), Map.entry(ClassificationService.class, classificationService),
Map.entry(ClassificationServiceImpl.class, classificationService), Map.entry(ClassificationServiceImpl.class, classificationService),
Map.entry(ConfigurationService.class, taskanaEngine.getConfigurationService()),
Map.entry(ConfigurationServiceImpl.class, taskanaEngine.getConfigurationService()),
Map.entry(JobService.class, jobService), Map.entry(JobService.class, jobService),
Map.entry(JobServiceImpl.class, jobService), Map.entry(JobServiceImpl.class, jobService),
Map.entry(CurrentUserContext.class, currentUserContext), Map.entry(CurrentUserContext.class, currentUserContext),
Map.entry(CurrentUserContextImpl.class, currentUserContext), Map.entry(CurrentUserContextImpl.class, currentUserContext),
Map.entry(WorkingDaysToDaysConverter.class, taskanaEngine.getWorkingDaysToDaysConverter())); Map.entry(WorkingDaysToDaysConverter.class, taskanaEngine.getWorkingDaysToDaysConverter()),
Map.entry(ConfigurationMapper.class, sqlSession.getMapper(ConfigurationMapper.class)));
} }
} }

View File

@ -9,10 +9,14 @@ import testapi.TaskanaIntegrationTest;
import pro.taskana.TaskanaEngineConfiguration; import pro.taskana.TaskanaEngineConfiguration;
import pro.taskana.classification.api.ClassificationService; import pro.taskana.classification.api.ClassificationService;
import pro.taskana.classification.internal.ClassificationServiceImpl; import pro.taskana.classification.internal.ClassificationServiceImpl;
import pro.taskana.common.api.ConfigurationService;
import pro.taskana.common.api.JobService; import pro.taskana.common.api.JobService;
import pro.taskana.common.api.TaskanaEngine; import pro.taskana.common.api.TaskanaEngine;
import pro.taskana.common.api.WorkingDaysToDaysConverter; import pro.taskana.common.api.WorkingDaysToDaysConverter;
import pro.taskana.common.api.security.CurrentUserContext; import pro.taskana.common.api.security.CurrentUserContext;
import pro.taskana.common.internal.ConfigurationMapper;
import pro.taskana.common.internal.ConfigurationServiceImpl;
import pro.taskana.common.internal.InternalTaskanaEngine;
import pro.taskana.common.internal.JobServiceImpl; import pro.taskana.common.internal.JobServiceImpl;
import pro.taskana.common.internal.TaskanaEngineImpl; import pro.taskana.common.internal.TaskanaEngineImpl;
import pro.taskana.common.internal.security.CurrentUserContextImpl; import pro.taskana.common.internal.security.CurrentUserContextImpl;
@ -31,6 +35,7 @@ class TaskanaDependencyInjectionExtensionTest {
@TaskanaInject TaskanaEngine taskanaEngine; @TaskanaInject TaskanaEngine taskanaEngine;
@TaskanaInject TaskanaEngine taskanaEngine2; @TaskanaInject TaskanaEngine taskanaEngine2;
@TaskanaInject TaskanaEngineImpl taskanaEngineImpl; @TaskanaInject TaskanaEngineImpl taskanaEngineImpl;
@TaskanaInject InternalTaskanaEngine internalTaskanaEngine;
@TaskanaInject ClassificationService classificationService; @TaskanaInject ClassificationService classificationService;
@TaskanaInject ClassificationServiceImpl classificationServiceImpl; @TaskanaInject ClassificationServiceImpl classificationServiceImpl;
@TaskanaInject WorkbasketService workbasketService; @TaskanaInject WorkbasketService workbasketService;
@ -41,9 +46,12 @@ class TaskanaDependencyInjectionExtensionTest {
@TaskanaInject MonitorServiceImpl monitorServiceImpl; @TaskanaInject MonitorServiceImpl monitorServiceImpl;
@TaskanaInject JobService jobService; @TaskanaInject JobService jobService;
@TaskanaInject JobServiceImpl jobServiceImpl; @TaskanaInject JobServiceImpl jobServiceImpl;
@TaskanaInject ConfigurationService configurationService;
@TaskanaInject ConfigurationServiceImpl configurationServiceImpl;
@TaskanaInject WorkingDaysToDaysConverter workingDaysToDaysConverter; @TaskanaInject WorkingDaysToDaysConverter workingDaysToDaysConverter;
@TaskanaInject CurrentUserContext currentUserContext; @TaskanaInject CurrentUserContext currentUserContext;
@TaskanaInject CurrentUserContextImpl currentUserContextImpl; @TaskanaInject CurrentUserContextImpl currentUserContextImpl;
@TaskanaInject ConfigurationMapper configurationMapper;
@Test @Test
void should_NotInjectTaskanaEngineConfiguration_When_FieldIsNotAnnotated() { void should_NotInjectTaskanaEngineConfiguration_When_FieldIsNotAnnotated() {
@ -73,6 +81,12 @@ class TaskanaDependencyInjectionExtensionTest {
assertThat(taskanaEngineImpl).isSameAs(this.taskanaEngineImpl).isNotNull(); assertThat(taskanaEngineImpl).isSameAs(this.taskanaEngineImpl).isNotNull();
} }
@Test
void should_InjectInternalTaskanaEngine_When_FieldIsAnnotatedOrDeclaredAsParameter(
InternalTaskanaEngine internalTaskanaEngine) {
assertThat(internalTaskanaEngine).isSameAs(this.internalTaskanaEngine).isNotNull();
}
@Test @Test
void should_InjectClassificationService_When_FieldIsAnnotatedOrDeclaredAsParameter( void should_InjectClassificationService_When_FieldIsAnnotatedOrDeclaredAsParameter(
ClassificationService classificationService) { ClassificationService classificationService) {
@ -132,6 +146,18 @@ class TaskanaDependencyInjectionExtensionTest {
assertThat(jobServiceImpl).isSameAs(this.jobServiceImpl).isNotNull(); assertThat(jobServiceImpl).isSameAs(this.jobServiceImpl).isNotNull();
} }
@Test
void should_InjectConfigurationService_When_FieldIsAnnotatedOrDeclaredAsParameter(
ConfigurationService configurationService) {
assertThat(configurationService).isSameAs(this.configurationService).isNotNull();
}
@Test
void should_InjectConfigurationServiceImpl_When_FieldIsAnnotatedOrDeclaredAsParameter(
ConfigurationServiceImpl configurationServiceImpl) {
assertThat(configurationServiceImpl).isSameAs(this.configurationServiceImpl).isNotNull();
}
@Test @Test
void should_InjectWorkingDaysToDaysConverter_When_FieldIsAnnotatedOrDeclaredAsParameter( void should_InjectWorkingDaysToDaysConverter_When_FieldIsAnnotatedOrDeclaredAsParameter(
WorkingDaysToDaysConverter workingDaysToDaysConverter) { WorkingDaysToDaysConverter workingDaysToDaysConverter) {
@ -149,4 +175,10 @@ class TaskanaDependencyInjectionExtensionTest {
CurrentUserContextImpl currentUserContextImpl) { CurrentUserContextImpl currentUserContextImpl) {
assertThat(currentUserContextImpl).isSameAs(this.currentUserContextImpl).isNotNull(); assertThat(currentUserContextImpl).isSameAs(this.currentUserContextImpl).isNotNull();
} }
@Test
void should_InjectConfigurationMapper_When_FieldIsAnnotatedOrDeclaredAsParameter(
ConfigurationMapper configurationMapper) {
assertThat(configurationMapper).isSameAs(this.configurationMapper).isNotNull();
}
} }

View File

@ -28,10 +28,7 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId> <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency> <dependency>
<groupId>pro.taskana</groupId> <groupId>pro.taskana</groupId>
<artifactId>taskana-spring</artifactId> <artifactId>taskana-spring</artifactId>
@ -44,6 +41,17 @@
<artifactId>junit-jupiter</artifactId> <artifactId>junit-jupiter</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>com.vaadin.external.google</groupId>
<artifactId>android-json</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency> <dependency>
<groupId>org.assertj</groupId> <groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId> <artifactId>assertj-core</artifactId>

View File

@ -4,6 +4,7 @@ import java.sql.SQLException;
import javax.sql.DataSource; import javax.sql.DataSource;
import pro.taskana.common.api.TaskanaEngine; import pro.taskana.common.api.TaskanaEngine;
import pro.taskana.common.api.TaskanaEngine.ConnectionManagementMode;
import pro.taskana.common.internal.SpringTaskanaEngineImpl; import pro.taskana.common.internal.SpringTaskanaEngineImpl;
/** This class configures the TaskanaEngineConfiguration for spring. */ /** This class configures the TaskanaEngineConfiguration for spring. */
@ -41,7 +42,7 @@ public class SpringTaskanaEngineConfiguration extends TaskanaEngineConfiguration
@Override @Override
public TaskanaEngine buildTaskanaEngine() throws SQLException { public TaskanaEngine buildTaskanaEngine() throws SQLException {
this.useManagedTransactions = true; this.useManagedTransactions = true;
return new SpringTaskanaEngineImpl(this); return new SpringTaskanaEngineImpl(this, ConnectionManagementMode.PARTICIPATE);
} }
public void setDataSource(DataSource dataSource) { public void setDataSource(DataSource dataSource) {

View File

@ -9,9 +9,10 @@ import pro.taskana.SpringTaskanaEngineConfiguration;
/** This class configures the TaskanaEngine for spring. */ /** This class configures the TaskanaEngine for spring. */
public class SpringTaskanaEngineImpl extends TaskanaEngineImpl { public class SpringTaskanaEngineImpl extends TaskanaEngineImpl {
public SpringTaskanaEngineImpl(SpringTaskanaEngineConfiguration taskanaEngineConfiguration) public SpringTaskanaEngineImpl(
SpringTaskanaEngineConfiguration taskanaEngineConfiguration, ConnectionManagementMode mode)
throws SQLException { throws SQLException {
super(taskanaEngineConfiguration); super(taskanaEngineConfiguration, mode);
} }
@PostConstruct @PostConstruct

View File

@ -1,16 +1,13 @@
package pro.taskana.example.rest.controllers; package pro.taskana.example.rest.controllers;
import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.stream.Collectors;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import pro.taskana.common.internal.util.ResourceUtil;
@Controller @Controller
public class ResourcesController { public class ResourcesController {
@ -32,14 +29,10 @@ public class ResourcesController {
// } // }
private String readResourceAsString(String resource) throws IOException { private String readResourceAsString(String resource) throws IOException {
try (InputStream fileStream = getClass().getResourceAsStream(resource)) { String resourceAsString = ResourceUtil.readResourceAsString(getClass(), resource);
if (fileStream == null) { if (resourceAsString == null) {
return "{}"; return "{}";
}
try (Reader inputStreamReader = new InputStreamReader(fileStream);
BufferedReader reader = new BufferedReader(inputStreamReader)) {
return reader.lines().collect(Collectors.joining(System.lineSeparator()));
}
} }
return resourceAsString;
} }
} }

View File

@ -15,7 +15,9 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
import pro.taskana.SpringTaskanaEngineConfiguration; import pro.taskana.SpringTaskanaEngineConfiguration;
import pro.taskana.TaskanaEngineConfiguration; import pro.taskana.TaskanaEngineConfiguration;
import pro.taskana.classification.api.ClassificationService; import pro.taskana.classification.api.ClassificationService;
import pro.taskana.common.api.ConfigurationService;
import pro.taskana.common.api.TaskanaEngine; import pro.taskana.common.api.TaskanaEngine;
import pro.taskana.common.api.security.CurrentUserContext;
import pro.taskana.monitor.api.MonitorService; import pro.taskana.monitor.api.MonitorService;
import pro.taskana.task.api.TaskService; import pro.taskana.task.api.TaskService;
import pro.taskana.user.api.UserService; import pro.taskana.user.api.UserService;
@ -58,6 +60,16 @@ public class RestConfiguration {
return taskanaEngine.getUserService(); return taskanaEngine.getUserService();
} }
@Bean
public ConfigurationService configurationService(TaskanaEngine taskanaEngine) {
return taskanaEngine.getConfigurationService();
}
@Bean
public CurrentUserContext currentUserContext(TaskanaEngine taskanaEngine) {
return taskanaEngine.getCurrentUserContext();
}
@Bean @Bean
@ConditionalOnMissingBean(TaskanaEngine.class) @ConditionalOnMissingBean(TaskanaEngine.class)
public TaskanaEngine getTaskanaEngine(TaskanaEngineConfiguration taskanaEngineConfiguration) public TaskanaEngine getTaskanaEngine(TaskanaEngineConfiguration taskanaEngineConfiguration)

View File

@ -14,6 +14,7 @@ public final class RestEndpoints {
public static final String URL_CLASSIFICATION_CATEGORIES_BY_TYPES = public static final String URL_CLASSIFICATION_CATEGORIES_BY_TYPES =
API_V1 + "classifications-by-type"; API_V1 + "classifications-by-type";
public static final String URL_HISTORY_ENABLED = API_V1 + "history-provider-enabled"; public static final String URL_HISTORY_ENABLED = API_V1 + "history-provider-enabled";
public static final String URL_CUSTOM_ATTRIBUTES = API_V1 + "/config/custom-attributes";
// access id endpoints // access id endpoints
public static final String URL_ACCESS_ID = API_V1 + "access-ids"; public static final String URL_ACCESS_ID = API_V1 + "access-ids";

View File

@ -2,15 +2,21 @@ package pro.taskana.common.rest;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.config.EnableHypermediaSupport; import org.springframework.hateoas.config.EnableHypermediaSupport;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import pro.taskana.TaskanaEngineConfiguration; import pro.taskana.TaskanaEngineConfiguration;
import pro.taskana.common.api.ConfigurationService;
import pro.taskana.common.api.TaskanaEngine; import pro.taskana.common.api.TaskanaEngine;
import pro.taskana.common.api.TaskanaRole; import pro.taskana.common.api.security.CurrentUserContext;
import pro.taskana.common.rest.models.CustomAttributesRepresentationModel;
import pro.taskana.common.rest.models.TaskanaUserInfoRepresentationModel; import pro.taskana.common.rest.models.TaskanaUserInfoRepresentationModel;
import pro.taskana.common.rest.models.VersionRepresentationModel; import pro.taskana.common.rest.models.VersionRepresentationModel;
@ -20,13 +26,20 @@ import pro.taskana.common.rest.models.VersionRepresentationModel;
public class TaskanaEngineController { public class TaskanaEngineController {
private final TaskanaEngineConfiguration taskanaEngineConfiguration; private final TaskanaEngineConfiguration taskanaEngineConfiguration;
private final TaskanaEngine taskanaEngine; private final TaskanaEngine taskanaEngine;
private final CurrentUserContext currentUserContext;
private final ConfigurationService configurationService;
@Autowired
TaskanaEngineController( TaskanaEngineController(
TaskanaEngineConfiguration taskanaEngineConfiguration, TaskanaEngine taskanaEngine) { TaskanaEngineConfiguration taskanaEngineConfiguration,
TaskanaEngine taskanaEngine,
CurrentUserContext currentUserContext,
ConfigurationService configurationService) {
this.taskanaEngineConfiguration = taskanaEngineConfiguration; this.taskanaEngineConfiguration = taskanaEngineConfiguration;
this.taskanaEngine = taskanaEngine; this.taskanaEngine = taskanaEngine;
this.currentUserContext = currentUserContext;
this.configurationService = configurationService;
} }
/** /**
@ -35,6 +48,7 @@ public class TaskanaEngineController {
* @return An array with the domain-names as strings * @return An array with the domain-names as strings
*/ */
@GetMapping(path = RestEndpoints.URL_DOMAIN) @GetMapping(path = RestEndpoints.URL_DOMAIN)
@Transactional(readOnly = true, rollbackFor = Exception.class)
public ResponseEntity<List<String>> getDomains() { public ResponseEntity<List<String>> getDomains() {
return ResponseEntity.ok(taskanaEngineConfiguration.getDomains()); return ResponseEntity.ok(taskanaEngineConfiguration.getDomains());
} }
@ -48,6 +62,7 @@ public class TaskanaEngineController {
* @return the classification categories for the requested type. * @return the classification categories for the requested type.
*/ */
@GetMapping(path = RestEndpoints.URL_CLASSIFICATION_CATEGORIES) @GetMapping(path = RestEndpoints.URL_CLASSIFICATION_CATEGORIES)
@Transactional(readOnly = true, rollbackFor = Exception.class)
public ResponseEntity<List<String>> getClassificationCategories( public ResponseEntity<List<String>> getClassificationCategories(
@RequestParam(required = false) String type) { @RequestParam(required = false) String type) {
if (type != null) { if (type != null) {
@ -62,6 +77,7 @@ public class TaskanaEngineController {
* @return the configured classification types. * @return the configured classification types.
*/ */
@GetMapping(path = RestEndpoints.URL_CLASSIFICATION_TYPES) @GetMapping(path = RestEndpoints.URL_CLASSIFICATION_TYPES)
@Transactional(readOnly = true, rollbackFor = Exception.class)
public ResponseEntity<List<String>> getClassificationTypes() { public ResponseEntity<List<String>> getClassificationTypes() {
return ResponseEntity.ok(taskanaEngineConfiguration.getClassificationTypes()); return ResponseEntity.ok(taskanaEngineConfiguration.getClassificationTypes());
} }
@ -73,6 +89,7 @@ public class TaskanaEngineController {
* @return the configured classification categories * @return the configured classification categories
*/ */
@GetMapping(path = RestEndpoints.URL_CLASSIFICATION_CATEGORIES_BY_TYPES) @GetMapping(path = RestEndpoints.URL_CLASSIFICATION_CATEGORIES_BY_TYPES)
@Transactional(readOnly = true, rollbackFor = Exception.class)
public ResponseEntity<Map<String, List<String>>> getClassificationCategoriesByTypeMap() { public ResponseEntity<Map<String, List<String>>> getClassificationCategoriesByTypeMap() {
return ResponseEntity.ok(taskanaEngineConfiguration.getClassificationCategoriesByTypeMap()); return ResponseEntity.ok(taskanaEngineConfiguration.getClassificationCategoriesByTypeMap());
} }
@ -83,15 +100,14 @@ public class TaskanaEngineController {
* @return the information of the current user. * @return the information of the current user.
*/ */
@GetMapping(path = RestEndpoints.URL_CURRENT_USER) @GetMapping(path = RestEndpoints.URL_CURRENT_USER)
@Transactional(readOnly = true, rollbackFor = Exception.class)
public ResponseEntity<TaskanaUserInfoRepresentationModel> getCurrentUserInfo() { public ResponseEntity<TaskanaUserInfoRepresentationModel> getCurrentUserInfo() {
TaskanaUserInfoRepresentationModel resource = new TaskanaUserInfoRepresentationModel(); TaskanaUserInfoRepresentationModel resource = new TaskanaUserInfoRepresentationModel();
resource.setUserId(taskanaEngine.getCurrentUserContext().getUserid()); resource.setUserId(currentUserContext.getUserid());
resource.setGroupIds(taskanaEngine.getCurrentUserContext().getGroupIds()); resource.setGroupIds(currentUserContext.getGroupIds());
for (TaskanaRole role : taskanaEngineConfiguration.getRoleMap().keySet()) { taskanaEngineConfiguration.getRoleMap().keySet().stream()
if (taskanaEngine.isUserInRole(role)) { .filter(taskanaEngine::isUserInRole)
resource.getRoles().add(role); .forEach(resource.getRoles()::add);
}
}
return ResponseEntity.ok(resource); return ResponseEntity.ok(resource);
} }
@ -101,16 +117,33 @@ public class TaskanaEngineController {
* @return true, when the history is enabled, otherwise false * @return true, when the history is enabled, otherwise false
*/ */
@GetMapping(path = RestEndpoints.URL_HISTORY_ENABLED) @GetMapping(path = RestEndpoints.URL_HISTORY_ENABLED)
@Transactional(readOnly = true, rollbackFor = Exception.class)
public ResponseEntity<Boolean> getIsHistoryProviderEnabled() { public ResponseEntity<Boolean> getIsHistoryProviderEnabled() {
return ResponseEntity.ok(taskanaEngine.isHistoryEnabled()); return ResponseEntity.ok(taskanaEngine.isHistoryEnabled());
} }
@GetMapping(path = RestEndpoints.URL_CUSTOM_ATTRIBUTES)
@Transactional(readOnly = true, rollbackFor = Exception.class)
public ResponseEntity<CustomAttributesRepresentationModel> getCustomAttributes() {
Map<String, Object> allCustomAttributes = configurationService.getAllCustomAttributes();
return ResponseEntity.ok(new CustomAttributesRepresentationModel(allCustomAttributes));
}
@PutMapping(path = RestEndpoints.URL_CUSTOM_ATTRIBUTES)
@Transactional(rollbackFor = Exception.class)
public ResponseEntity<CustomAttributesRepresentationModel> setCustomAttributes(
@RequestBody CustomAttributesRepresentationModel customAttributes) {
configurationService.setAllCustomAttributes(customAttributes.getCustomAttributes());
return ResponseEntity.ok(customAttributes);
}
/** /**
* Get the current application version. * Get the current application version.
* *
* @return The current version. * @return The current version.
*/ */
@GetMapping(path = RestEndpoints.URL_VERSION) @GetMapping(path = RestEndpoints.URL_VERSION)
@Transactional(readOnly = true, rollbackFor = Exception.class)
public ResponseEntity<VersionRepresentationModel> currentVersion() { public ResponseEntity<VersionRepresentationModel> currentVersion() {
VersionRepresentationModel resource = new VersionRepresentationModel(); VersionRepresentationModel resource = new VersionRepresentationModel();
resource.setVersion(TaskanaEngineConfiguration.class.getPackage().getImplementationVersion()); resource.setVersion(TaskanaEngineConfiguration.class.getPackage().getImplementationVersion());

View File

@ -0,0 +1,20 @@
package pro.taskana.common.rest.models;
import java.beans.ConstructorProperties;
import java.util.Map;
import org.springframework.hateoas.RepresentationModel;
public class CustomAttributesRepresentationModel
extends RepresentationModel<CustomAttributesRepresentationModel> {
private final Map<String, Object> customAttributes;
@ConstructorProperties({"customAttributes"})
public CustomAttributesRepresentationModel(Map<String, Object> customAttributes) {
this.customAttributes = customAttributes;
}
public Map<String, Object> getCustomAttributes() {
return customAttributes;
}
}

View File

@ -12,6 +12,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import pro.taskana.common.api.TaskanaRole; import pro.taskana.common.api.TaskanaRole;
import pro.taskana.common.rest.models.CustomAttributesRepresentationModel;
import pro.taskana.common.rest.models.TaskanaUserInfoRepresentationModel; import pro.taskana.common.rest.models.TaskanaUserInfoRepresentationModel;
import pro.taskana.common.test.rest.RestHelper; import pro.taskana.common.test.rest.RestHelper;
import pro.taskana.common.test.rest.TaskanaSpringBootTest; import pro.taskana.common.test.rest.TaskanaSpringBootTest;
@ -32,7 +33,7 @@ class TaskanaEngineControllerIntTest {
@Test @Test
void testDomains() { void testDomains() {
String url = restHelper.toUrl(RestEndpoints.URL_DOMAIN); String url = restHelper.toUrl(RestEndpoints.URL_DOMAIN);
HttpEntity<Object> auth = new HttpEntity<>(RestHelper.generateHeadersForUser("teamlead-1")); HttpEntity<?> auth = new HttpEntity<>(RestHelper.generateHeadersForUser("teamlead-1"));
ResponseEntity<List<String>> response = ResponseEntity<List<String>> response =
TEMPLATE.exchange( TEMPLATE.exchange(
@ -43,7 +44,7 @@ class TaskanaEngineControllerIntTest {
@Test @Test
void testClassificationTypes() { void testClassificationTypes() {
String url = restHelper.toUrl(RestEndpoints.URL_CLASSIFICATION_TYPES); String url = restHelper.toUrl(RestEndpoints.URL_CLASSIFICATION_TYPES);
HttpEntity<Object> auth = new HttpEntity<>(RestHelper.generateHeadersForUser("teamlead-1")); HttpEntity<?> auth = new HttpEntity<>(RestHelper.generateHeadersForUser("teamlead-1"));
ResponseEntity<List<String>> response = ResponseEntity<List<String>> response =
TEMPLATE.exchange( TEMPLATE.exchange(
@ -54,7 +55,7 @@ class TaskanaEngineControllerIntTest {
@Test @Test
void testClassificationCategories() { void testClassificationCategories() {
String url = restHelper.toUrl(RestEndpoints.URL_CLASSIFICATION_CATEGORIES); String url = restHelper.toUrl(RestEndpoints.URL_CLASSIFICATION_CATEGORIES);
HttpEntity<Object> auth = new HttpEntity<>(RestHelper.generateHeadersForUser("teamlead-1")); HttpEntity<?> auth = new HttpEntity<>(RestHelper.generateHeadersForUser("teamlead-1"));
ResponseEntity<List<String>> response = ResponseEntity<List<String>> response =
TEMPLATE.exchange( TEMPLATE.exchange(
@ -66,7 +67,7 @@ class TaskanaEngineControllerIntTest {
@Test @Test
void testGetCurrentUserInfo() { void testGetCurrentUserInfo() {
String url = restHelper.toUrl(RestEndpoints.URL_CURRENT_USER); String url = restHelper.toUrl(RestEndpoints.URL_CURRENT_USER);
HttpEntity<Object> auth = new HttpEntity<>(RestHelper.generateHeadersForUser("teamlead-1")); HttpEntity<?> auth = new HttpEntity<>(RestHelper.generateHeadersForUser("teamlead-1"));
ResponseEntity<TaskanaUserInfoRepresentationModel> response = ResponseEntity<TaskanaUserInfoRepresentationModel> response =
TEMPLATE.exchange( TEMPLATE.exchange(
@ -81,4 +82,19 @@ class TaskanaEngineControllerIntTest {
assertThat(response.getBody().getRoles()).contains(TaskanaRole.BUSINESS_ADMIN); assertThat(response.getBody().getRoles()).contains(TaskanaRole.BUSINESS_ADMIN);
assertThat(response.getBody().getRoles()).doesNotContain(TaskanaRole.ADMIN); assertThat(response.getBody().getRoles()).doesNotContain(TaskanaRole.ADMIN);
} }
@Test
void should_ReturnCustomAttributes() {
String url = restHelper.toUrl(RestEndpoints.URL_CUSTOM_ATTRIBUTES);
HttpEntity<?> auth = new HttpEntity<>(RestHelper.generateHeadersForUser("teamlead-1"));
ResponseEntity<CustomAttributesRepresentationModel> response =
TEMPLATE.exchange(
url,
HttpMethod.GET,
auth,
ParameterizedTypeReference.forType(CustomAttributesRepresentationModel.class));
assertThat(response.getBody()).isNotNull();
}
} }

View File

@ -38,8 +38,8 @@ public abstract class AbstractAccTest {
dbSchemaCreator.run(); dbSchemaCreator.run();
sampleDataGenerator.clearDb(); sampleDataGenerator.clearDb();
sampleDataGenerator.generateTestData(); sampleDataGenerator.generateTestData();
taskanaEngine = taskanaEngineConfiguration.buildTaskanaEngine(); taskanaEngine =
taskanaEngine.setConnectionManagementMode(ConnectionManagementMode.AUTOCOMMIT); taskanaEngineConfiguration.buildTaskanaEngine(ConnectionManagementMode.AUTOCOMMIT);
converter = taskanaEngine.getWorkingDaysToDaysConverter(); converter = taskanaEngine.getWorkingDaysToDaysConverter();
} }