Add support for pollingTime overrides (#2533)

* Add support for pollingTime overrides

* the current format HH:mm:ss is still supported
* add option for deviation percent (HH:mm:ss~\d{1,2}%) which allows the system to do some randomizing of the poll interval
* add support for overriding default polling time interval for devices matching some RSQL filters (in order), e.g. 01:00:00~10%, group == 'eu' -> 00:02:00~15%, status != in_sync -> 00:05:00
* IMPORTANT: overdue time is calculated according to the default polling time. So, the overdue status might be incorrect for targets with overridden poll interval

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>

* Remove min polling time from the tenant config - it is a system configuration

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>

* Add support for bigger poll intervals and overdue + duration format config support

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>

---------

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2025-07-07 16:33:55 +03:00
committed by GitHub
parent baab05f009
commit 7f97d6f441
31 changed files with 664 additions and 514 deletions

View File

@@ -161,6 +161,7 @@ import org.eclipse.hawkbit.security.SecurityTokenGenerator;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.tenancy.UserAuthoritiesResolver;
import org.eclipse.hawkbit.tenancy.configuration.ControllerPollProperties;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
import org.eclipse.hawkbit.utils.TenantConfigHelper;
import org.springframework.beans.BeansException;
@@ -850,16 +851,15 @@ public class RepositoryApplicationConfiguration {
final DeploymentManagement deploymentManagement, final ConfirmationManagement confirmationManagement,
final SoftwareModuleRepository softwareModuleRepository, final SoftwareModuleMetadataRepository softwareModuleMetadataRepository,
final DistributionSetManagement distributionSetManagement,
final TenantConfigurationManagement tenantConfigurationManagement,
final TenantConfigurationManagement tenantConfigurationManagement, final ControllerPollProperties controllerPollProperties,
final PlatformTransactionManager txManager, final EntityFactory entityFactory, final EntityManager entityManager,
final AfterTransactionCommitExecutor afterCommit,
final SystemSecurityContext systemSecurityContext, final TenantAware tenantAware,
final ScheduledExecutorService executorService) {
return new JpaControllerManagement(actionRepository, actionStatusRepository, quotaManagement, repositoryProperties,
targetRepository, targetTypeManagement, deploymentManagement, confirmationManagement, softwareModuleRepository,
softwareModuleMetadataRepository, distributionSetManagement, tenantConfigurationManagement, txManager,
entityFactory, entityManager, afterCommit, systemSecurityContext, tenantAware,
executorService);
softwareModuleMetadataRepository, distributionSetManagement, tenantConfigurationManagement, controllerPollProperties,
txManager,entityFactory, entityManager, afterCommit, systemSecurityContext, tenantAware, executorService);
}
@Bean

View File

@@ -79,6 +79,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaAction_;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.jpa.ql.EntityMatcher;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.repository.ActionStatusRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleMetadataRepository;
@@ -101,6 +102,9 @@ import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.tenancy.configuration.ControllerPollProperties;
import org.eclipse.hawkbit.tenancy.configuration.DurationHelper;
import org.eclipse.hawkbit.tenancy.configuration.PollingTime;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
@@ -116,6 +120,7 @@ import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
@@ -140,6 +145,8 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
private final SoftwareModuleMetadataRepository softwareModuleMetadataRepository;
private final DistributionSetManagement distributionSetManagement;
private final TenantConfigurationManagement tenantConfigurationManagement;
private final ControllerPollProperties controllerPollProperties;
private final Duration minPollingTime, maxPollingTime;
private final PlatformTransactionManager txManager;
private final EntityFactory entityFactory;
private final EntityManager entityManager;
@@ -155,7 +162,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
final DeploymentManagement deploymentManagement, final ConfirmationManagement confirmationManagement,
final SoftwareModuleRepository softwareModuleRepository, final SoftwareModuleMetadataRepository softwareModuleMetadataRepository,
final DistributionSetManagement distributionSetManagement,
final TenantConfigurationManagement tenantConfigurationManagement,
final TenantConfigurationManagement tenantConfigurationManagement, final ControllerPollProperties controllerPollProperties,
final PlatformTransactionManager txManager, final EntityFactory entityFactory, final EntityManager entityManager,
final AfterTransactionCommitExecutor afterCommit,
final SystemSecurityContext systemSecurityContext, final TenantAware tenantAware,
@@ -170,6 +177,13 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
this.softwareModuleMetadataRepository = softwareModuleMetadataRepository;
this.distributionSetManagement = distributionSetManagement;
this.tenantConfigurationManagement = tenantConfigurationManagement;
this.controllerPollProperties = controllerPollProperties;
minPollingTime = controllerPollProperties.getMinPollingTime() == null
? Duration.of(0, ChronoUnit.SECONDS)
: DurationHelper.fromString(controllerPollProperties.getMinPollingTime());
maxPollingTime = controllerPollProperties.getMaxPollingTime() == null
? Duration.of(100, ChronoUnit.YEARS)
: DurationHelper.fromString(controllerPollProperties.getMaxPollingTime());
this.txManager = txManager;
this.entityFactory = entityFactory;
this.entityManager = entityManager;
@@ -345,6 +359,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
public Target findOrRegisterTargetIfItDoesNotExist(final String controllerId, final URI address, final String name, final String type) {
return findOrRegisterTargetIfItDoesNotExist0(controllerId, address, name, type);
}
private Target findOrRegisterTargetIfItDoesNotExist0(final String controllerId, final URI address, final String name, final String type) {
final Specification<JpaTarget> spec = (targetRoot, query, cb) -> cb.equal(targetRoot.get(JpaTarget_.controllerId), controllerId);
return targetRepository.findOne(spec)
@@ -373,42 +388,39 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
}
@Override
public String getPollingTime() {
return systemSecurityContext.runAsSystem(() -> tenantConfigurationManagement
.getConfigurationValue(TenantConfigurationKey.POLLING_TIME_INTERVAL, String.class).getValue());
}
/**
* Returns the configured minimum polling interval.
*
* @return current {@link TenantConfigurationKey#MIN_POLLING_TIME_INTERVAL}.
*/
@Override
public String getMinPollingTime() {
return systemSecurityContext.runAsSystem(() -> tenantConfigurationManagement
.getConfigurationValue(TenantConfigurationKey.MIN_POLLING_TIME_INTERVAL, String.class).getValue());
}
/**
* Returns the count to be used for reducing polling interval while calling {@link ControllerManagement#getPollingTimeForAction(Action)}.
*
* @return configured value of
* {@link TenantConfigurationKey#MAINTENANCE_WINDOW_POLL_COUNT}.
*/
@Override
public int getMaintenanceWindowPollCount() {
return systemSecurityContext.runAsSystem(() -> tenantConfigurationManagement
.getConfigurationValue(TenantConfigurationKey.MAINTENANCE_WINDOW_POLL_COUNT, Integer.class).getValue());
public String getPollingTime(final Target target) {
return systemSecurityContext.runAsSystem(() -> {
final PollingTime pollingTime = new PollingTime(
tenantConfigurationManagement.getConfigurationValue(TenantConfigurationKey.POLLING_TIME, String.class).getValue());
if (!ObjectUtils.isEmpty(pollingTime.getOverrides()) && target instanceof JpaTarget jpaTarget) {
for (final PollingTime.Override override : pollingTime.getOverrides()) {
try {
if (EntityMatcher.forRsql(override.qlStr()).match(jpaTarget)) {
return override.pollingInterval().getFormattedIntervalWithDeviation(minPollingTime, maxPollingTime);
}
} catch (final Exception e) {
log.warn("Error while evaluating polling override for target {}: {}", jpaTarget.getId(), e.getMessage());
}
}
}
// returns default - no overrides or not applicable for the target
return pollingTime.getPollingInterval().getFormattedIntervalWithDeviation(minPollingTime, maxPollingTime);
});
}
@Override
public String getPollingTimeForAction(final Action action) {
public String getPollingTimeForAction(final Target target, final Action action) {
final String pollingTime = getPollingTime(target);
if (!action.hasMaintenanceSchedule() || action.isMaintenanceScheduleLapsed()) {
return getPollingTime();
return pollingTime;
} else {
// the count to be used for reducing polling interval -> the configured value of {@link TenantConfigurationKey#MAINTENANCE_WINDOW_POLL_COUNT}
final int maintenanceWindowPollCount = systemSecurityContext.runAsSystem(
() -> tenantConfigurationManagement.getConfigurationValue(
TenantConfigurationKey.MAINTENANCE_WINDOW_POLL_COUNT, Integer.class).getValue());
return new EventTimer(pollingTime, controllerPollProperties.getMinPollingTime(), ChronoUnit.SECONDS)
.timeToNextEvent(maintenanceWindowPollCount, action.getMaintenanceWindowStartTime().orElse(null));
}
return new EventTimer(getPollingTime(), getMinPollingTime(), ChronoUnit.SECONDS)
.timeToNextEvent(getMaintenanceWindowPollCount(), action.getMaintenanceWindowStartTime().orElse(null));
}
@Override

View File

@@ -9,8 +9,10 @@
*/
package org.eclipse.hawkbit.repository.jpa.management;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.BATCH_ASSIGNMENTS_ENABLED;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.POLLING_TIME;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED;
import java.io.Serializable;
@@ -18,8 +20,8 @@ import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
@@ -33,7 +35,9 @@ import org.eclipse.hawkbit.repository.exception.TenantConfigurationValidatorExce
import org.eclipse.hawkbit.repository.exception.TenantConfigurationValueChangeNotAllowedException;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTenantConfiguration;
import org.eclipse.hawkbit.repository.jpa.ql.EntityMatcher;
import org.eclipse.hawkbit.repository.jpa.repository.TenantConfigurationRepository;
import org.eclipse.hawkbit.repository.model.PollStatus;
import org.eclipse.hawkbit.repository.model.Target;
@@ -42,6 +46,7 @@ import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
import org.eclipse.hawkbit.repository.model.helper.SystemSecurityContextHolder;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.configuration.DurationHelper;
import org.eclipse.hawkbit.tenancy.configuration.PollingTime;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.springframework.cache.Cache;
@@ -55,6 +60,7 @@ import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated;
/**
@@ -90,9 +96,8 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public <T extends Serializable> TenantConfigurationValue<T> addOrUpdateConfiguration(
final String configurationKeyName, final T value) {
return addOrUpdateConfiguration0(Collections.singletonMap(configurationKeyName, value)).values().iterator().next();
public <T extends Serializable> TenantConfigurationValue<T> addOrUpdateConfiguration(final String configurationKeyName, final T value) {
return addOrUpdateConfiguration0(Map.of(configurationKeyName, value)).values().iterator().next();
}
@Override
@@ -138,11 +143,9 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
checkAccess(configurationKeyName);
final TenantConfigurationKey configurationKey = tenantConfigurationProperties.fromKeyName(configurationKeyName);
validateTenantConfigurationDataType(configurationKey, propertyType);
final TenantConfiguration tenantConfiguration = tenantConfigurationRepository.findByKey(configurationKey.getKeyName());
return buildTenantConfigurationValueByKey(configurationKey, propertyType, tenantConfiguration);
}
@@ -151,7 +154,6 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
checkAccess(configurationKeyName);
final TenantConfigurationKey key = tenantConfigurationProperties.fromKeyName(configurationKeyName);
if (!key.getDataType().isAssignableFrom(propertyType)) {
throw new TenantConfigurationValidatorException(String.format(
"Cannot parse the database value of type %s into the type %s.", key.getDataType(), propertyType));
@@ -162,47 +164,62 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
@Override
public Function<Target, PollStatus> pollStatusResolver() {
final Duration pollTime = DurationHelper.formattedStringToDuration(
getConfigurationValue(TenantConfigurationKey.POLLING_TIME_INTERVAL, String.class).getValue());
final Duration overdueTime = DurationHelper.formattedStringToDuration(
getConfigurationValue(TenantConfigurationKey.POLLING_OVERDUE_TIME_INTERVAL, String.class)
.getValue());
final PollingTime pollingTime = new PollingTime(
getConfigurationValue(TenantConfigurationKey.POLLING_TIME, String.class).getValue());
final Duration pollingOverdueTime = DurationHelper.fromString(
getConfigurationValue(TenantConfigurationKey.POLLING_OVERDUE_TIME, String.class).getValue());
return target -> {
final Long lastTargetQuery = target.getLastTargetQuery();
if (lastTargetQuery == null) {
return null;
}
final LocalDateTime currentDate = LocalDateTime.now();
final LocalDateTime lastPollDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(lastTargetQuery),
ZoneId.systemDefault());
final LocalDateTime nextPollDate = lastPollDate.plus(pollTime);
final LocalDateTime overdueDate = nextPollDate.plus(overdueTime);
return new PollStatus(lastPollDate, nextPollDate, overdueDate, currentDate);
if (!ObjectUtils.isEmpty(pollingTime.getOverrides()) && target instanceof JpaTarget jpaTarget) {
for (final PollingTime.Override override : pollingTime.getOverrides()) {
try {
if (EntityMatcher.forRsql(override.qlStr()).match(jpaTarget)) {
return pollStatus(lastTargetQuery, override.pollingInterval(), pollingOverdueTime);
}
} catch (final Exception e) {
log.warn("Error while evaluating polling override for target {}: {}", jpaTarget.getId(), e.getMessage());
}
}
}
// returns default - no overrides or not applicable for the target
return pollStatus(lastTargetQuery, pollingTime.getPollingInterval(), pollingOverdueTime);
};
}
/**
* Validates the data type of the tenant configuration. If it is possible to
* cast to the given data type.
*
* @param configurationKey the key
* @param propertyType the class
*/
private static <T> void validateTenantConfigurationDataType(final TenantConfigurationKey configurationKey,
final Class<T> propertyType) {
private static PollStatus pollStatus(
final long lastTargetQuery,
final PollingTime.PollingInterval pollingInterval, final Duration pollingOverdueTime) {
final LocalDateTime currentDate = LocalDateTime.now();
final LocalDateTime lastPollDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(lastTargetQuery), ZoneId.systemDefault());
LocalDateTime nextPollDate = lastPollDate.plus(pollingInterval.getInterval());
if (pollingInterval.getDeviationPercent() > 0) {
nextPollDate = nextPollDate.plus(
pollingInterval.getInterval().toMillis() * pollingInterval.getDeviationPercent() / 100,
ChronoUnit.MILLIS);
}
final LocalDateTime overdueDate = nextPollDate.plus(pollingOverdueTime);
return new PollStatus(lastPollDate, nextPollDate, overdueDate, currentDate);
}
/**
* Validates the data type of the tenant configuration. If it is possible to cast to the given data type.
*/
private static <T> void validateTenantConfigurationDataType(final TenantConfigurationKey configurationKey, final Class<T> propertyType) {
if (!configurationKey.getDataType().isAssignableFrom(propertyType)) {
throw new TenantConfigurationValidatorException(
String.format("Cannot parse the database value of type %s into the type %s.",
String.format(
"Cannot parse the database value of type %s into the type %s.",
configurationKey.getDataType(), propertyType));
}
}
private void checkAccess(final String configurationKeyName) {
if (TenantConfigurationProperties.TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY
.equalsIgnoreCase(configurationKeyName)) {
final SystemSecurityContext systemSecurityContext =
SystemSecurityContextHolder.getInstance().getSystemSecurityContext();
if (AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY.equalsIgnoreCase(configurationKeyName)) {
final SystemSecurityContext systemSecurityContext = SystemSecurityContextHolder.getInstance().getSystemSecurityContext();
if (!systemSecurityContext.isCurrentThreadSystemCode() &&
!systemSecurityContext.hasPermission(SpPermission.READ_GATEWAY_SEC_TOKEN)) {
throw new InsufficientPermissionException(
@@ -211,22 +228,28 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
}
}
private <T extends Serializable> Map<String, TenantConfigurationValue<T>> addOrUpdateConfiguration0(Map<String, T> configurations) {
List<JpaTenantConfiguration> configurationList = new ArrayList<>();
private <T extends Serializable> Map<String, TenantConfigurationValue<T>> addOrUpdateConfiguration0(final Map<String, T> configurations) {
final List<JpaTenantConfiguration> configurationList = new ArrayList<>();
configurations.forEach((configurationKeyName, value) -> {
final TenantConfigurationKey configurationKey = tenantConfigurationProperties.fromKeyName(configurationKeyName);
if (!configurationKey.getDataType().isAssignableFrom(value.getClass())) {
throw new TenantConfigurationValidatorException(String.format(
"Cannot parse the value %s of type %s into the type %s defined by the configuration key.", value,
value.getClass(), configurationKey.getDataType()));
}
configurationKey.validate(value, applicationContext);
// additional validation for specific configuration keys
if (POLLING_TIME.equals(configurationKey.getKeyName())) {
final PollingTime pollingTime = new PollingTime(value.toString());
if (!ObjectUtils.isEmpty(pollingTime.getOverrides())) {
// validate that the QL strings are valid RSQL queries,
// nevertheless always when parse them we shall be prepared to catch exceptions if the parsers
// has been changed in non backward compatible way
pollingTime.getOverrides().forEach(override -> EntityMatcher.forRsql(override.qlStr()));
}
}
configurationKey.validate(applicationContext, value);
JpaTenantConfiguration tenantConfiguration = tenantConfigurationRepository
.findByKey(configurationKey.getKeyName());
JpaTenantConfiguration tenantConfiguration = tenantConfigurationRepository.findByKey(configurationKey.getKeyName());
if (tenantConfiguration == null) {
tenantConfiguration = new JpaTenantConfiguration(configurationKey.getKeyName(), value.toString());
} else {
@@ -237,15 +260,12 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
configurationList.add(tenantConfiguration);
});
List<JpaTenantConfiguration> jpaTenantConfigurations = tenantConfigurationRepository
.saveAll(configurationList);
final List<JpaTenantConfiguration> jpaTenantConfigurations = tenantConfigurationRepository.saveAll(configurationList);
return jpaTenantConfigurations.stream().collect(Collectors.toMap(
JpaTenantConfiguration::getKey,
updatedTenantConfiguration -> {
@SuppressWarnings("unchecked") final Class<T> clazzT = (Class<T>) configurations.get(updatedTenantConfiguration.getKey())
.getClass();
@SuppressWarnings("unchecked")
final Class<T> clazzT = (Class<T>) configurations.get(updatedTenantConfiguration.getKey()).getClass();
return TenantConfigurationValue.<T> builder().global(false)
.createdBy(updatedTenantConfiguration.getCreatedBy())
.createdAt(updatedTenantConfiguration.getCreatedAt())
@@ -257,27 +277,25 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
}
private <T extends Serializable> TenantConfigurationValue<T> buildTenantConfigurationValueByKey(
final TenantConfigurationKey configurationKey, final Class<T> propertyType,
final TenantConfiguration tenantConfiguration) {
final TenantConfigurationKey configurationKey, final Class<T> propertyType, final TenantConfiguration tenantConfiguration) {
if (tenantConfiguration != null) {
return TenantConfigurationValue.<T> builder().global(false).createdBy(tenantConfiguration.getCreatedBy())
.createdAt(tenantConfiguration.getCreatedAt())
.lastModifiedAt(tenantConfiguration.getLastModifiedAt())
.lastModifiedBy(tenantConfiguration.getLastModifiedBy())
.value(CONVERSION_SERVICE.convert(tenantConfiguration.getValue(), propertyType)).build();
} else if (configurationKey.getDefaultValue() != null) {
return TenantConfigurationValue.<T> builder().global(true).createdBy(null).createdAt(null)
.lastModifiedAt(null).lastModifiedBy(null)
.value(getGlobalConfigurationValue(configurationKey.getKeyName(), propertyType)).build();
} else {
return null;
}
return null;
}
/**
* Asserts that the requested configuration value change is allowed. Throws
* a {@link TenantConfigurationValueChangeNotAllowedException} otherwise.
* Asserts that the requested configuration value change is allowed. Throws a {@link TenantConfigurationValueChangeNotAllowedException}
* otherwise.
*
* @param key The configuration key.
* @param valueChange The configuration to be validated.
@@ -293,9 +311,7 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
private void assertAutoCloseValueChange(final String key, final JpaTenantConfiguration valueChange) {
if (REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED.equals(key)
&& Boolean.TRUE.equals(getConfigurationValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class).getValue())) {
log.debug(
"The property '{}' must not be changed because the Multi-Assignments feature is currently enabled.",
key);
log.debug("The property '{}' must not be changed because the Multi-Assignments feature is currently enabled.", key);
throw new TenantConfigurationValueChangeNotAllowedException();
}
}
@@ -308,8 +324,7 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
if (MULTI_ASSIGNMENTS_ENABLED.equals(key) && Boolean.parseBoolean(valueChange.getValue())) {
JpaTenantConfiguration batchConfig = tenantConfigurationRepository.findByKey(BATCH_ASSIGNMENTS_ENABLED);
if (batchConfig != null && Boolean.parseBoolean(batchConfig.getValue())) {
log.debug("The Multi-Assignments '{}' feature cannot be enabled as it contradicts with " +
"The Batch-Assignments feature, which is already enabled .", key);
log.debug("The Multi-Assignments '{}' feature cannot be enabled as it contradicts with the Batch-Assignments feature, which is already enabled .", key);
throw new TenantConfigurationValueChangeNotAllowedException();
}
}
@@ -319,10 +334,9 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
if (BATCH_ASSIGNMENTS_ENABLED.equals(key) && Boolean.parseBoolean(valueChange.getValue())) {
JpaTenantConfiguration multiConfig = tenantConfigurationRepository.findByKey(MULTI_ASSIGNMENTS_ENABLED);
if (multiConfig != null && Boolean.parseBoolean(multiConfig.getValue())) {
log.debug("The Batch-Assignments '{}' feature cannot be enabled as it contradicts with " +
"The Multi-Assignments feature, which is already enabled .", key);
log.debug("The Batch-Assignments '{}' feature cannot be enabled as it contradicts with the Multi-Assignments feature, which is already enabled .", key);
throw new TenantConfigurationValueChangeNotAllowedException();
}
}
}
}
}

View File

@@ -139,24 +139,7 @@ class ControllerManagementSecurityTest extends AbstractJpaIntegrationTest {
*/
@Test
void getPollingTimePermissionsCheck() {
assertPermissions(() -> controllerManagement.getPollingTime(), List.of(SpPermission.SpringEvalExpressions.CONTROLLER_ROLE));
}
/**
* Tests ControllerManagement#getMinPollingTime() method
*/
@Test
void getMinPollingTimePermissionsCheck() {
assertPermissions(() -> controllerManagement.getMinPollingTime(), List.of(SpPermission.SpringEvalExpressions.CONTROLLER_ROLE));
}
/**
* Tests ControllerManagement#getMaxPollingTime() method
*/
@Test
void getMaintenanceWindowPollCountPermissionsCheck() {
assertPermissions(() -> controllerManagement.getMaintenanceWindowPollCount(),
List.of(SpPermission.SpringEvalExpressions.CONTROLLER_ROLE));
assertPermissions(() -> controllerManagement.getPollingTime(null), List.of(SpPermission.SpringEvalExpressions.CONTROLLER_ROLE));
}
/**
@@ -168,7 +151,7 @@ class ControllerManagementSecurityTest extends AbstractJpaIntegrationTest {
action.setId(1L);
assertPermissions(() -> {
try {
controllerManagement.getPollingTimeForAction(action);
controllerManagement.getPollingTimeForAction(action.getTarget(), action);
} catch (final CancelActionNotAllowedException e) {
// expected since action is not found
}

View File

@@ -51,7 +51,7 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
// get the configuration from the system management
final TenantConfigurationValue<String> defaultConfigValue = tenantConfigurationManagement.getConfigurationValue(
TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY, String.class);
TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, String.class);
assertThat(defaultConfigValue.isGlobal()).isTrue();
assertThat(defaultConfigValue.getValue()).isEqualTo(envPropertyDefault);
@@ -60,11 +60,11 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
final String newConfigurationValue = "thisIsAnotherTokenName";
assertThat(newConfigurationValue).isNotEqualTo(defaultConfigValue.getValue());
tenantConfigurationManagement.addOrUpdateConfiguration(
TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY, newConfigurationValue);
TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, newConfigurationValue);
// verify that new configuration value is used
final TenantConfigurationValue<String> updatedConfigurationValue = tenantConfigurationManagement
.getConfigurationValue(TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY, String.class);
.getConfigurationValue(TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, String.class);
assertThat(updatedConfigurationValue.isGlobal()).isFalse();
assertThat(updatedConfigurationValue.getValue()).isEqualTo(newConfigurationValue);
@@ -76,7 +76,7 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
*/
@Test
void updateTenantSpecificConfiguration() {
final String configKey = TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY;
final String configKey = TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY;
final String value1 = "firstValue";
final String value2 = "secondValue";
@@ -95,14 +95,14 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
@Test
void batchUpdateTenantSpecificConfiguration() {
Map<String, Serializable> configuration = new HashMap<>() {{
put(TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY, "token_123");
put(TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, "token_123");
put(TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED, true);
}};
// add value first
tenantConfigurationManagement.addOrUpdateConfiguration(configuration);
assertThat(tenantConfigurationManagement.getConfigurationValue(
TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY, String.class).getValue())
TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, String.class).getValue())
.isEqualTo("token_123");
assertThat(
tenantConfigurationManagement.getConfigurationValue(TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED, Boolean.class).getValue())
@@ -114,7 +114,7 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
*/
@Test
void storeAndUpdateTenantSpecificConfigurationAsBoolean() {
final String configKey = TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_ENABLED;
final String configKey = TenantConfigurationKey.AUTHENTICATION_HEADER_ENABLED;
final Boolean value1 = true;
tenantConfigurationManagement.addOrUpdateConfiguration(configKey, value1);
assertThat(tenantConfigurationManagement.getConfigurationValue(configKey, Boolean.class).getValue()).isEqualTo(value1);
@@ -128,7 +128,7 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
*/
@Test
void wrongTenantConfigurationValueTypeThrowsException() {
final String configKey = TenantConfigurationKey.AUTHENTICATION_MODE_HEADER_ENABLED;
final String configKey = TenantConfigurationKey.AUTHENTICATION_HEADER_ENABLED;
final String value1 = "thisIsNotABoolean";
// add value as String
@@ -143,9 +143,9 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
@Test
void batchWrongTenantConfigurationValueTypeThrowsException() {
final Map<String, Serializable> configuration = new HashMap<>() {{
put(TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY, "token_123");
put(TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, "token_123");
put(TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED, true);
put(TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_ENABLED, "wrong");
put(TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_ENABLED, "wrong");
}};
try {
@@ -154,7 +154,7 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
} catch (final TenantConfigurationValidatorException e) {
assertThat(
tenantConfigurationManagement.getConfigurationValue(
TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY, String.class).getValue())
TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, String.class).getValue())
.isNotEqualTo("token_123");
assertThat(
tenantConfigurationManagement.getConfigurationValue(
@@ -168,7 +168,7 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
*/
@Test
void deleteConfigurationReturnNullConfiguration() {
final String configKey = TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY;
final String configKey = TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY;
// gateway token does not have default value so no configuration value should be available
final String defaultConfigValue = tenantConfigurationManagement.getConfigurationValue(configKey, String.class).getValue();
@@ -195,7 +195,7 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
*/
@Test
void storesIntegerWhenStringIsExpected() {
final String configKey = TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY;
final String configKey = TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY;
final Integer wrongDatType = 123;
assertThatThrownBy(() -> tenantConfigurationManagement.addOrUpdateConfiguration(configKey, wrongDatType))
.as("Should not have worked as integer is not a string")
@@ -207,7 +207,7 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
*/
@Test
void storesIntegerWhenBooleanIsExpected() {
final String configKey = TenantConfigurationKey.AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_ENABLED;
final String configKey = TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_ENABLED;
final Integer wrongDataType = 123;
assertThatThrownBy(() -> tenantConfigurationManagement.addOrUpdateConfiguration(configKey, wrongDataType))
.as("Should not have worked as integer is not a boolean")
@@ -219,7 +219,7 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
*/
@Test
void storesIntegerWhenPollingIntervalIsExpected() {
final String configKey = TenantConfigurationKey.POLLING_TIME_INTERVAL;
final String configKey = TenantConfigurationKey.POLLING_TIME;
final Integer wrongDataType = 123;
assertThatThrownBy(() -> tenantConfigurationManagement.addOrUpdateConfiguration(configKey, wrongDataType))
.as("Should not have worked as integer is not a time field")
@@ -231,7 +231,7 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
*/
@Test
void storesWrongFormattedStringAsPollingInterval() {
final String configKey = TenantConfigurationKey.POLLING_TIME_INTERVAL;
final String configKey = TenantConfigurationKey.POLLING_TIME;
final String wrongFormatted = "wrongFormatted";
assertThatThrownBy(() -> tenantConfigurationManagement.addOrUpdateConfiguration(configKey, wrongFormatted))
.as("should not have worked as string is not a time field")
@@ -243,10 +243,9 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
*/
@Test
void storesTooSmallDurationAsPollingInterval() {
final String configKey = TenantConfigurationKey.POLLING_TIME_INTERVAL;
final String configKey = TenantConfigurationKey.POLLING_TIME;
final String tooSmallDuration = DurationHelper
.durationToFormattedString(DurationHelper.getDurationByTimeValues(0, 0, 1));
final String tooSmallDuration = DurationHelper.toString(getDurationByTimeValues(0, 0, 1));
assertThatThrownBy(() -> tenantConfigurationManagement.addOrUpdateConfiguration(configKey, tooSmallDuration))
.as("Should not have worked as string has an invalid format")
.isInstanceOf(TenantConfigurationValidatorException.class);
@@ -257,15 +256,15 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
*/
@Test
void storesCorrectDurationAsPollingInterval() {
final String configKey = TenantConfigurationKey.POLLING_TIME_INTERVAL;
final String configKey = TenantConfigurationKey.POLLING_TIME;
final Duration duration = DurationHelper.getDurationByTimeValues(1, 2, 0);
final Duration duration = getDurationByTimeValues(1, 2, 0);
assertThat(duration).isEqualTo(Duration.ofHours(1).plusMinutes(2));
tenantConfigurationManagement.addOrUpdateConfiguration(configKey, DurationHelper.durationToFormattedString(duration));
tenantConfigurationManagement.addOrUpdateConfiguration(configKey, DurationHelper.toString(duration));
final String storedDurationString = tenantConfigurationManagement.getConfigurationValue(configKey, String.class).getValue();
assertThat(duration).isEqualTo(DurationHelper.formattedStringToDuration(storedDurationString));
assertThat(duration).isEqualTo(DurationHelper.fromString(storedDurationString));
}
/**
@@ -274,7 +273,7 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
@Test
void requestConfigValueWithWrongType() {
assertThatThrownBy(() -> tenantConfigurationManagement.getConfigurationValue(
TenantConfigurationKey.POLLING_TIME_INTERVAL, Serializable.class))
TenantConfigurationKey.POLLING_TIME, Serializable.class))
.isInstanceOf(TenantConfigurationValidatorException.class);
}
@@ -297,7 +296,7 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
*/
@Test
void getTenantConfigurationKeyByName() {
final String configKey = TenantConfigurationKey.POLLING_TIME_INTERVAL;
final String configKey = TenantConfigurationKey.POLLING_TIME;
assertThat(tenantConfigurationProperties.fromKeyName(configKey).getKeyName()).isEqualTo(configKey);
}
@@ -311,4 +310,8 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
.as("Expected InvalidTenantConfigurationKeyException for tenant configuration key which is not declared")
.isInstanceOf(InvalidTenantConfigurationKeyException.class);
}
private static Duration getDurationByTimeValues(final long hours, final long minutes, final long seconds) {
return Duration.ofHours(hours).plusMinutes(minutes).plusSeconds(seconds);
}
}

View File

@@ -411,9 +411,9 @@ class RsqlUtilityTest {
VirtualPropertyReplacer setupMacroLookup() {
when(securityContext.runAsSystem(Mockito.any())).thenAnswer(a -> ((Callable<?>) a.getArgument(0)).call());
when(confMgmt.getConfigurationValue(TenantConfigurationKey.POLLING_TIME_INTERVAL, String.class))
when(confMgmt.getConfigurationValue(TenantConfigurationKey.POLLING_TIME, String.class))
.thenReturn(TEST_POLLING_TIME_INTERVAL);
when(confMgmt.getConfigurationValue(TenantConfigurationKey.POLLING_OVERDUE_TIME_INTERVAL, String.class))
when(confMgmt.getConfigurationValue(TenantConfigurationKey.POLLING_OVERDUE_TIME, String.class))
.thenReturn(TEST_POLLING_OVERDUE_TIME_INTERVAL);
return macroResolver;

View File

@@ -54,9 +54,9 @@ class VirtualPropertyResolverTest {
@BeforeEach
void before() {
when(confMgmt.getConfigurationValue(TenantConfigurationKey.POLLING_TIME_INTERVAL, String.class))
when(confMgmt.getConfigurationValue(TenantConfigurationKey.POLLING_TIME, String.class))
.thenReturn(TEST_POLLING_TIME_INTERVAL);
when(confMgmt.getConfigurationValue(TenantConfigurationKey.POLLING_OVERDUE_TIME_INTERVAL, String.class))
when(confMgmt.getConfigurationValue(TenantConfigurationKey.POLLING_OVERDUE_TIME, String.class))
.thenReturn(TEST_POLLING_OVERDUE_TIME_INTERVAL);
}