Merge remote-tracking branch 'origin/master' into fix_migration_to_new_spring_boot_version_merge_master

Signed-off-by: Ammar Bikic <ammar.bikic@bosch.io>
This commit is contained in:
Ammar Bikic
2021-01-15 16:44:31 +01:00
45 changed files with 709 additions and 167 deletions

View File

@@ -125,6 +125,20 @@ public interface TargetManagement {
+ SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
long countByInstalledDistributionSet(long distId);
/**
* Checks if there is already a {@link Target} that has the given distribution set Id assigned or installed.
*
* @param distId
* to search for
* @return <code>true</code> if a {@link Target} exists.
*
* @throws EntityNotFoundException
* if distribution set with given ID does not exist
*/
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR
+ SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
boolean existsByInstalledOrAssignedDistributionSet(long distId);
/**
* Count {@link TargetFilterQuery}s for given target filter query.
*

View File

@@ -123,6 +123,9 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED = new EnumMap<>(Database.class);
QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED.put(Database.SQL_SERVER, "DELETE TOP (" + ACTION_PAGE_LIMIT
+ ") FROM sp_action WHERE tenant=#tenant AND status IN (%s) AND last_modified_at<#last_modified_at ");
QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED.put(Database.POSTGRESQL,
"DELETE FROM sp_action WHERE id IN (SELECT id FROM sp_action WHERE tenant=#tenant AND status IN (%s) AND last_modified_at<#last_modified_at LIMIT "
+ ACTION_PAGE_LIMIT + ")");
}
private final EntityManager entityManager;
@@ -209,8 +212,8 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
final Map<Long, List<TargetWithActionType>> assignmentsByDsIds = convertRequest(validatedRequests);
final List<DistributionSetAssignmentResult> results = assignmentsByDsIds.entrySet().stream()
.map(entry -> assignDistributionSetToTargetsWithRetry(initiatedBy, entry.getKey(), entry.getValue(), actionMessage,
strategy))
.map(entry -> assignDistributionSetToTargetsWithRetry(initiatedBy, entry.getKey(), entry.getValue(),
actionMessage, strategy))
.collect(Collectors.toList());
strategy.sendDeploymentEvents(results);
return results;
@@ -239,8 +242,8 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
}
}
private DistributionSetAssignmentResult assignDistributionSetToTargetsWithRetry(final String initiatedBy, final Long dsID,
final Collection<TargetWithActionType> targetsWithActionType, final String actionMessage,
private DistributionSetAssignmentResult assignDistributionSetToTargetsWithRetry(final String initiatedBy,
final Long dsID, final Collection<TargetWithActionType> targetsWithActionType, final String actionMessage,
final AbstractDsAssignmentStrategy assignmentStrategy) {
final RetryCallback<DistributionSetAssignmentResult, ConcurrencyFailureException> retryCallback = retryContext -> assignDistributionSetToTargets(
initiatedBy, dsID, targetsWithActionType, actionMessage, assignmentStrategy);
@@ -427,9 +430,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
return targetsWithActionType.stream()
.map(twt -> assignmentStrategy.createTargetAction(initiatedBy, twt, targets, set))
.filter(Objects::nonNull)
.map(actionRepository::save)
.collect(Collectors.toList());
.filter(Objects::nonNull).map(actionRepository::save).collect(Collectors.toList());
}
private void createActionsStatus(final Collection<JpaAction> actions,

View File

@@ -38,6 +38,7 @@ import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.dao.ConcurrencyFailureException;
@@ -45,6 +46,7 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.transaction.PlatformTransactionManager;
@@ -59,6 +61,7 @@ import org.springframework.validation.annotation.Validated;
@Transactional(readOnly = true)
@Validated
public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, SystemManagement {
private static final Logger LOGGER = LoggerFactory.getLogger(JpaSystemManagement.class);
private static final int MAX_TENANTS_QUERY = 1000;
@@ -123,12 +126,28 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
@Autowired
private ArtifactRepository artifactRepository;
private final String countArtifactQuery;
private final String countSoftwareModulesQuery;
/**
* Constructor.
*
* @param properties
* properties to get the underlying database
*/
public JpaSystemManagement(final JpaProperties properties) {
final String isDeleted = isPostgreSql(properties) ? "false" : "0";
countArtifactQuery = "SELECT COUNT(a.id) FROM sp_artifact a INNER JOIN sp_base_software_module sm ON a.software_module = sm.id WHERE sm.deleted = "
+ isDeleted;
countSoftwareModulesQuery = "select SUM(file_size) from sp_artifact a INNER JOIN sp_base_software_module sm ON a.software_module = sm.id WHERE sm.deleted = "
+ isDeleted;
}
@Override
public SystemUsageReport getSystemUsageStatistics() {
final Number count = (Number) entityManager.createNativeQuery(
"select SUM(file_size) from sp_artifact a INNER JOIN sp_base_software_module sm ON a.software_module = sm.id WHERE sm.deleted = 0")
.getSingleResult();
final Number count = (Number) entityManager.createNativeQuery(countSoftwareModulesQuery).getSingleResult();
long sumOfArtifacts = 0;
if (count != null) {
@@ -140,9 +159,8 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
final long targets = ((Number) entityManager.createNativeQuery("SELECT COUNT(id) FROM sp_target")
.getSingleResult()).longValue();
final long artifacts = ((Number) entityManager.createNativeQuery(
"SELECT COUNT(a.id) FROM sp_artifact a INNER JOIN sp_base_software_module sm ON a.software_module = sm.id WHERE sm.deleted = 0")
.getSingleResult()).longValue();
final long artifacts = ((Number) entityManager.createNativeQuery(countArtifactQuery).getSingleResult())
.longValue();
final long actions = ((Number) entityManager.createNativeQuery("SELECT COUNT(id) FROM sp_action")
.getSingleResult()).longValue();
@@ -151,6 +169,10 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
tenantMetaDataRepository.count());
}
private static boolean isPostgreSql(final JpaProperties properties) {
return Database.POSTGRESQL == properties.getDatabase();
}
@Override
public SystemUsageReportWithTenants getSystemUsageStatisticsWithTenants() {
final SystemUsageReportWithTenants result = (SystemUsageReportWithTenants) getSystemUsageStatistics();

View File

@@ -646,6 +646,13 @@ public class JpaTargetManagement implements TargetManagement {
return targetRepository.countByInstalledDistributionSetId(distId);
}
@Override
public boolean existsByInstalledOrAssignedDistributionSet(final long distId) {
throwEntityNotFoundIfDsDoesNotExist(distId);
return targetRepository.existsByInstalledOrAssignedDistributionSet(distId);
}
@Override
public Page<Target> findByTargetFilterQueryAndNonDS(final Pageable pageRequest, final long distributionSetId,
final String targetFilterQuery) {

View File

@@ -15,6 +15,8 @@ import java.util.concurrent.ScheduledExecutorService;
import javax.persistence.EntityManager;
import javax.sql.DataSource;
import com.google.common.collect.Maps;
import org.eclipse.hawkbit.artifact.repository.ArtifactRepository;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.ControllerManagement;
@@ -124,8 +126,6 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.jta.JtaTransactionManager;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
import com.google.common.collect.Maps;
/**
* General configuration for hawkBit's Repository.
*
@@ -425,8 +425,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/
@Bean
@ConditionalOnMissingBean
SystemManagement systemManagement() {
return new JpaSystemManagement();
SystemManagement systemManagement(final JpaProperties properties) {
return new JpaSystemManagement(properties);
}
/**
@@ -759,8 +759,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
@Bean
@ConditionalOnMissingBean
AutoAssignExecutor autoAssignExecutor(final TargetFilterQueryManagement targetFilterQueryManagement,
final TargetManagement targetManagement, final DeploymentManagement deploymentManagement,
final PlatformTransactionManager transactionManager) {
final TargetManagement targetManagement, final DeploymentManagement deploymentManagement,
final PlatformTransactionManager transactionManager) {
return new AutoAssignChecker(targetFilterQueryManagement, targetManagement, deploymentManagement,
transactionManager);
}

View File

@@ -208,6 +208,16 @@ public interface TargetRepository extends BaseEntityRepository<JpaTarget, Long>,
*/
Long countByInstalledDistributionSetId(Long distId);
/**
* Checks if there is already a {@link Target} that has the given distribution set Id assigned or installed.
*
* @param distId
* to check
* @return <code>true</code> if a {@link Target} exists.
*/
@Query("SELECT CASE WHEN COUNT(t)>0 THEN 'true' ELSE 'false' END FROM JpaTarget t WHERE t.installedDistributionSet.id=:distId OR t.assignedDistributionSet.id=:distId")
boolean existsByInstalledOrAssignedDistributionSet(@Param("distId") Long distId);
/**
* Finds all {@link Target}s in the repository.
*

View File

@@ -35,6 +35,7 @@ import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Subquery;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.lang3.text.StrLookup;
import org.eclipse.hawkbit.repository.FieldNameProvider;
import org.eclipse.hawkbit.repository.FieldValueConverter;
@@ -375,7 +376,8 @@ public final class RSQLUtility {
*/
private Path<Object> getFieldPath(final A enumField, final String finalProperty) {
return (Path<Object>) getFieldPath(root, getSubAttributesFrom(finalProperty), enumField.isMap(),
this::getJoinFieldPath).orElseThrow(() -> new RSQLParameterUnsupportedFieldException("RSQL field path cannot be empty", null));
this::getJoinFieldPath).orElseThrow(
() -> new RSQLParameterUnsupportedFieldException("RSQL field path cannot be empty", null));
}
private Path<?> getJoinFieldPath(final Path<?> fieldPath, final String fieldNameSplit) {
@@ -396,8 +398,8 @@ public final class RSQLUtility {
return fieldPath;
}
private static Optional<Path<?>> getFieldPath(final Root<?> root, final String[] split, final boolean isMapKeyField,
final BiFunction<Path<?>, String, Path<?>> joinFieldPathProvider) {
private static Optional<Path<?>> getFieldPath(final Root<?> root, final String[] split,
final boolean isMapKeyField, final BiFunction<Path<?>, String, Path<?>> joinFieldPathProvider) {
Path<?> fieldPath = null;
for (int i = 0; i < split.length; i++) {
if (!(isMapKeyField && i == (split.length - 1))) {
@@ -685,7 +687,11 @@ public final class RSQLUtility {
}
private Predicate getEqualToPredicate(final Object transformedValue, final Path<Object> fieldPath) {
if (transformedValue instanceof String) {
if (transformedValue == null) {
return cb.isNull(pathOfString(fieldPath));
}
if ((transformedValue instanceof String) && !NumberUtils.isCreatable((String) transformedValue)) {
if (StringUtils.isEmpty(transformedValue)) {
return cb.or(cb.isNull(pathOfString(fieldPath)), cb.equal(pathOfString(fieldPath), ""));
}
@@ -694,10 +700,6 @@ public final class RSQLUtility {
return cb.like(cb.upper(pathOfString(fieldPath)), sqlValue, ESCAPE_CHAR);
}
if (transformedValue == null) {
return cb.isNull(pathOfString(fieldPath));
}
return cb.equal(fieldPath, transformedValue);
}
@@ -708,7 +710,7 @@ public final class RSQLUtility {
return toNotNullPredicate(fieldPath);
}
if (transformedValue instanceof String) {
if ((transformedValue instanceof String) && !NumberUtils.isCreatable((String) transformedValue)) {
if (StringUtils.isEmpty(transformedValue)) {
return toNotNullAndNotEmptyPredicate(fieldPath);
}
@@ -725,7 +727,7 @@ public final class RSQLUtility {
return toNotEqualWithSubQueryPredicate(enumField, sqlValue, fieldNames);
}
return toNotEqualPredicate(fieldPath, transformedValue);
return toNullOrNotEqualPredicate(fieldPath, transformedValue);
}
private void clearOuterJoinsIfNotNeeded() {
@@ -738,15 +740,15 @@ public final class RSQLUtility {
return cb.isNotNull(pathOfString(fieldPath));
}
private Predicate toNotEqualPredicate(final Path<Object> fieldPath, final Object transformedValue) {
return cb.notEqual(fieldPath, transformedValue);
}
private Predicate toNullOrNotLikePredicate(final Path<Object> fieldPath, final String sqlValue) {
return cb.or(cb.isNull(pathOfString(fieldPath)),
cb.notLike(cb.upper(pathOfString(fieldPath)), sqlValue, ESCAPE_CHAR));
}
private Predicate toNullOrNotEqualPredicate(final Path<Object> fieldPath, final Object transformedValue) {
return cb.or(cb.isNull(pathOfString(fieldPath)), cb.notEqual(fieldPath, transformedValue));
}
private Predicate toNotNullAndNotEmptyPredicate(final Path<Object> fieldPath) {
return cb.and(cb.isNotNull(pathOfString(fieldPath)), cb.notEqual(pathOfString(fieldPath), ""));
}
@@ -797,9 +799,11 @@ public final class RSQLUtility {
}
private static Path<?> getInnerFieldPath(final Root<?> subqueryRoot, final String[] split,
final boolean isMapKeyField) {
final boolean isMapKeyField) {
return getFieldPath(subqueryRoot, split, isMapKeyField,
(fieldPath, fieldNameSplit) -> getInnerJoinFieldPath(subqueryRoot, fieldPath, fieldNameSplit)).orElseThrow(() -> new RSQLParameterUnsupportedFieldException("RSQL field path cannot be empty", null));
(fieldPath, fieldNameSplit) -> getInnerJoinFieldPath(subqueryRoot, fieldPath, fieldNameSplit))
.orElseThrow(() -> new RSQLParameterUnsupportedFieldException(
"RSQL field path cannot be empty", null));
}
private static Path<?> getInnerJoinFieldPath(final Root<?> subqueryRoot, final Path<?> fieldPath,

View File

@@ -31,7 +31,9 @@ import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest;
import org.eclipse.hawkbit.repository.test.util.RolloutTestApprovalStrategy;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.cloud.stream.test.binder.TestSupportBinderAutoConfiguration;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.transaction.annotation.Transactional;
@@ -101,6 +103,13 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
@Autowired
protected RolloutTestApprovalStrategy approvalStrategy;
@Autowired
private JpaProperties jpaProperties;
protected Database getDatabase() {
return jpaProperties.getDatabase();
}
@Transactional(readOnly = true)
protected List<Action> findActionsByRolloutAndStatus(final Rollout rollout, final Action.Status actionStatus) {
return Lists.newArrayList(actionRepository.findByRolloutIdAndStatus(PAGE, rollout.getId(), actionStatus));

View File

@@ -39,9 +39,9 @@ import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedE
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetTagCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.InvalidTargetAddressException;
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.exception.TenantNotExistException;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetMetadata;
@@ -108,6 +108,8 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
"DistributionSet");
verifyThrownExceptionBy(() -> targetManagement.countByInstalledDistributionSet(NOT_EXIST_IDL),
"DistributionSet");
verifyThrownExceptionBy(() -> targetManagement.existsByInstalledOrAssignedDistributionSet(NOT_EXIST_IDL),
"DistributionSet");
verifyThrownExceptionBy(() -> targetManagement.countByTargetFilterQuery(NOT_EXIST_IDL), "TargetFilterQuery");
verifyThrownExceptionBy(() -> targetManagement.countByRsqlAndNonDS(NOT_EXIST_IDL, "name==*"),
@@ -465,10 +467,14 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
.isEqualTo(0);
assertThat(targetManagement.countByInstalledDistributionSet(set.getId())).as("Target count is wrong")
.isEqualTo(0);
assertThat(targetManagement.existsByInstalledOrAssignedDistributionSet(set.getId())).as("Target count is wrong")
.isFalse();
assertThat(targetManagement.countByAssignedDistributionSet(set2.getId())).as("Target count is wrong")
.isEqualTo(0);
assertThat(targetManagement.countByInstalledDistributionSet(set2.getId())).as("Target count is wrong")
.isEqualTo(0);
assertThat(targetManagement.existsByInstalledOrAssignedDistributionSet(set2.getId())).as("Target count is wrong")
.isFalse();
Target target = createTargetWithAttributes("4711");
@@ -488,10 +494,14 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
.isEqualTo(0);
assertThat(targetManagement.countByInstalledDistributionSet(set.getId())).as("Target count is wrong")
.isEqualTo(1);
assertThat(targetManagement.existsByInstalledOrAssignedDistributionSet(set.getId())).as("Target count is wrong")
.isTrue();
assertThat(targetManagement.countByAssignedDistributionSet(set2.getId())).as("Target count is wrong")
.isEqualTo(1);
assertThat(targetManagement.countByInstalledDistributionSet(set2.getId())).as("Target count is wrong")
.isEqualTo(0);
assertThat(targetManagement.existsByInstalledOrAssignedDistributionSet(set2.getId())).as("Target count is wrong")
.isTrue();
assertThat(target.getLastTargetQuery()).as("Target query is not work").isGreaterThanOrEqualTo(current);
assertThat(deploymentManagement.getAssignedDistributionSet("4711").get()).as("Assigned ds size is wrong")
.isEqualTo(set2);

View File

@@ -24,6 +24,7 @@ import org.junit.Before;
import org.junit.Test;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Slice;
import org.springframework.orm.jpa.vendor.Database;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
@@ -55,7 +56,7 @@ public class RSQLActionFieldsTest extends AbstractJpaIntegrationTest {
final JpaAction newAction = new JpaAction();
newAction.setActionType(ActionType.SOFT);
newAction.setDistributionSet(dsA);
newAction.setActive(i % 2 == 0);
newAction.setActive((i % 2) == 0);
newAction.setStatus(Status.RUNNING);
newAction.setTarget(target);
newAction.setWeight(45);
@@ -71,9 +72,18 @@ public class RSQLActionFieldsTest extends AbstractJpaIntegrationTest {
public void testFilterByParameterId() {
assertRSQLQuery(ActionFields.ID.name() + "==" + action.getId(), 1);
assertRSQLQuery(ActionFields.ID.name() + "!=" + action.getId(), 10);
assertRSQLQuery(ActionFields.ID.name() + "==noExist*", 0);
assertRSQLQuery(ActionFields.ID.name() + "=in=(" + action.getId() + ",1000000)", 1);
assertRSQLQuery(ActionFields.ID.name() + "=out=(" + action.getId() + ",1000000)", 10);
assertRSQLQuery(ActionFields.ID.name() + "==" + -1, 0);
assertRSQLQuery(ActionFields.ID.name() + "!=" + -1, 11);
// Not supported for numbers
if (Database.POSTGRESQL.equals(getDatabase())) {
return;
}
assertRSQLQuery(ActionFields.ID.name() + "==*", 11);
assertRSQLQuery(ActionFields.ID.name() + "==noexist*", 0);
assertRSQLQuery(ActionFields.ID.name() + "=in=(" + action.getId() + ",10000000)", 1);
assertRSQLQuery(ActionFields.ID.name() + "=out=(" + action.getId() + ",10000000)", 10);
}
@Test

View File

@@ -23,6 +23,7 @@ import org.junit.Before;
import org.junit.Test;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.orm.jpa.vendor.Database;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
@@ -32,10 +33,12 @@ import io.qameta.allure.Story;
@Story("RSQL filter distribution set")
public class RSQLDistributionSetFieldTest extends AbstractJpaIntegrationTest {
private DistributionSet ds;
@Before
public void seuptBeforeTest() {
DistributionSet ds = testdataFactory.createDistributionSet("DS");
ds = testdataFactory.createDistributionSet("DS");
ds = distributionSetManagement.update(entityFactory.distributionSet().update(ds.getId()).description("DS"));
createDistributionSetMetadata(ds.getId(), entityFactory.generateDsMetadata("metaKey", "metaValue"));
@@ -59,7 +62,20 @@ public class RSQLDistributionSetFieldTest extends AbstractJpaIntegrationTest {
@Test
@Description("Test filter distribution set by id")
public void testFilterByParameterId() {
assertRSQLQuery(DistributionSetFields.ID.name() + "==" + ds.getId(), 1);
assertRSQLQuery(DistributionSetFields.ID.name() + "!=" + ds.getId(), 4);
assertRSQLQuery(DistributionSetFields.ID.name() + "==" + -1, 0);
assertRSQLQuery(DistributionSetFields.ID.name() + "!=" + -1, 5);
// Not supported for numbers
if (Database.POSTGRESQL.equals(getDatabase())) {
return;
}
assertRSQLQuery(DistributionSetFields.ID.name() + "==*", 5);
assertRSQLQuery(DistributionSetFields.ID.name() + "==noexist*", 0);
assertRSQLQuery(DistributionSetFields.ID.name() + "=in=(" + ds.getId() + ",10000000)", 1);
assertRSQLQuery(DistributionSetFields.ID.name() + "=out=(" + ds.getId() + ",10000000)", 4);
}
@Test

View File

@@ -21,6 +21,7 @@ import org.junit.Before;
import org.junit.Test;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.orm.jpa.vendor.Database;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
@@ -47,11 +48,21 @@ public class RSQLRolloutGroupFields extends AbstractJpaIntegrationTest {
@Test
@Description("Test filter rollout group by id")
public void testFilterByParameterId() {
assertRSQLQuery(RolloutGroupFields.ID.name() + "==*", 3);
assertRSQLQuery(RolloutGroupFields.ID.name() + "==" + rolloutGroupId, 1);
assertRSQLQuery(RolloutGroupFields.ID.name() + "!=" + rolloutGroupId, 3);
assertRSQLQuery(RolloutGroupFields.ID.name() + "==noExist*", 0);
assertRSQLQuery(RolloutGroupFields.ID.name() + "=in=(" + rolloutGroupId + ")", 1);
assertRSQLQuery(RolloutGroupFields.ID.name() + "=out=(" + rolloutGroupId + ")", 3);
assertRSQLQuery(RolloutGroupFields.ID.name() + "==" + -1, 0);
assertRSQLQuery(RolloutGroupFields.ID.name() + "!=" + -1, 4);
// Not supported for numbers
if (Database.POSTGRESQL.equals(getDatabase())) {
return;
}
assertRSQLQuery(RolloutGroupFields.ID.name() + "==*", 4);
assertRSQLQuery(RolloutGroupFields.ID.name() + "==noexist*", 0);
assertRSQLQuery(RolloutGroupFields.ID.name() + "=in=(" + rolloutGroupId + ",10000000)", 1);
assertRSQLQuery(RolloutGroupFields.ID.name() + "=out=(" + rolloutGroupId + ",10000000)", 2);
}
@Test

View File

@@ -20,6 +20,7 @@ import org.junit.Before;
import org.junit.Test;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.orm.jpa.vendor.Database;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
@@ -29,10 +30,12 @@ import io.qameta.allure.Story;
@Story("RSQL filter software module")
public class RSQLSoftwareModuleFieldTest extends AbstractJpaIntegrationTest {
private SoftwareModule ah;
@Before
public void setupBeforeTest() {
final SoftwareModule ah = softwareModuleManagement.create(entityFactory.softwareModule().create().type(appType)
.name("agent-hub").version("1.0.1").description("agent-hub"));
ah = softwareModuleManagement.create(entityFactory.softwareModule().create().type(appType).name("agent-hub")
.version("1.0.1").description("agent-hub"));
softwareModuleManagement.create(entityFactory.softwareModule().create().type(runtimeType).name("oracle-jre")
.version("1.7.2").description("aa"));
softwareModuleManagement.create(
@@ -55,7 +58,20 @@ public class RSQLSoftwareModuleFieldTest extends AbstractJpaIntegrationTest {
@Test
@Description("Test filter software module by id")
public void testFilterByParameterId() {
assertRSQLQuery(SoftwareModuleFields.ID.name() + "==" + ah.getId(), 1);
assertRSQLQuery(SoftwareModuleFields.ID.name() + "!=" + ah.getId(), 4);
assertRSQLQuery(SoftwareModuleFields.ID.name() + "==" + -1, 0);
assertRSQLQuery(SoftwareModuleFields.ID.name() + "!=" + -1, 5);
// Not supported for numbers
if (Database.POSTGRESQL.equals(getDatabase())) {
return;
}
assertRSQLQuery(SoftwareModuleFields.ID.name() + "==*", 5);
assertRSQLQuery(SoftwareModuleFields.ID.name() + "==noexist*", 0);
assertRSQLQuery(SoftwareModuleFields.ID.name() + "=in=(" + ah.getId() + ",1000000)", 1);
assertRSQLQuery(SoftwareModuleFields.ID.name() + "=out=(" + ah.getId() + ",1000000)", 4);
}
@Test

View File

@@ -17,6 +17,7 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.junit.Test;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.orm.jpa.vendor.Database;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
@@ -29,7 +30,20 @@ public class RSQLSoftwareModuleTypeFieldsTest extends AbstractJpaIntegrationTest
@Test
@Description("Test filter software module test type by id")
public void testFilterByParameterId() {
assertRSQLQuery(SoftwareModuleTypeFields.ID.name() + "==" + osType.getId(), 1);
assertRSQLQuery(SoftwareModuleTypeFields.ID.name() + "!=" + osType.getId(), 2);
assertRSQLQuery(SoftwareModuleTypeFields.ID.name() + "==" + -1, 0);
assertRSQLQuery(SoftwareModuleTypeFields.ID.name() + "!=" + -1, 3);
// Not supported for numbers
if (Database.POSTGRESQL.equals(getDatabase())) {
return;
}
assertRSQLQuery(SoftwareModuleTypeFields.ID.name() + "==*", 3);
assertRSQLQuery(SoftwareModuleTypeFields.ID.name() + "==noexist*", 0);
assertRSQLQuery(SoftwareModuleTypeFields.ID.name() + "=in=(" + osType.getId() + ",1000000)", 1);
assertRSQLQuery(SoftwareModuleTypeFields.ID.name() + "=out=(" + osType.getId() + ",1000000)", 2);
}
@Test

View File

@@ -20,6 +20,7 @@ import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.domain.Page;
import org.springframework.orm.jpa.vendor.Database;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
@@ -54,7 +55,21 @@ public class RSQLTargetFilterQueryFieldsTest extends AbstractJpaIntegrationTest
@Test
@Description("Test filter target filter query by id")
public void testFilterByParameterId() {
assertRSQLQuery(TargetFilterQueryFields.ID.name() + "==" + filter1.getId(), 1);
assertRSQLQuery(TargetFilterQueryFields.ID.name() + "!=" + filter1.getId(), 2);
assertRSQLQuery(TargetFilterQueryFields.ID.name() + "==" + -1, 0);
assertRSQLQuery(TargetFilterQueryFields.ID.name() + "!=" + -1, 3);
// Not supported for numbers
if (Database.POSTGRESQL.equals(getDatabase())) {
return;
}
assertRSQLQuery(TargetFilterQueryFields.ID.name() + "==*", 3);
assertRSQLQuery(TargetFilterQueryFields.ID.name() + "==noexist*", 0);
assertRSQLQuery(TargetFilterQueryFields.ID.name() + "=in=(" + filter1.getId() + ",10000000)", 1);
assertRSQLQuery(TargetFilterQueryFields.ID.name() + "=out=(" + filter1.getId() + ",10000000)", 2);
}
@Test

View File

@@ -196,8 +196,7 @@ public class RSQLUtilityTest {
when(criteriaBuilderMock.upper(eq(pathOfString(baseSoftwareModuleRootMock))))
.thenReturn(pathOfString(baseSoftwareModuleRootMock));
when(criteriaBuilderMock.like(any(Expression.class), anyString(), eq('\\'))).thenReturn(mock(Predicate.class));
when(criteriaBuilderMock.<String> greaterThanOrEqualTo(any(Expression.class), any(String.class)))
.thenReturn(mock(Predicate.class));
when(criteriaBuilderMock.equal(any(Expression.class), any(String.class))).thenReturn(mock(Predicate.class));
// test
RSQLUtility.parse(correctRsql, SoftwareModuleFields.class, null, testDb).toPredicate(baseSoftwareModuleRootMock,

View File

@@ -51,6 +51,10 @@
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<!-- Add a JDBC DB2 compatabile driver if you want to run tests against DB2, e.g. -->
<!-- <dependency> -->
<!-- <groupId>com.ibm.db2.jcc</groupId> -->

View File

@@ -107,7 +107,8 @@ import com.google.common.io.Files;
// test execution. So, the order execution between EventVerifier and Cleanup is
// important!
@TestExecutionListeners(inheritListeners = true, listeners = { EventVerifier.class, CleanupTestExecutionListener.class,
MySqlTestDatabase.class, MsSqlTestDatabase.class }, mergeMode = MergeMode.MERGE_WITH_DEFAULTS)
MySqlTestDatabase.class, MsSqlTestDatabase.class,
PostgreSqlTestDatabase.class }, mergeMode = MergeMode.MERGE_WITH_DEFAULTS)
@TestPropertySource(properties = "spring.main.allow-bean-definition-overriding=true")
public abstract class AbstractIntegrationTest {
private static final Logger LOG = LoggerFactory.getLogger(AbstractIntegrationTest.class);

View File

@@ -0,0 +1,55 @@
/**
* Copyright (c) 2020 Microsoft and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.test.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestExecutionListener;
import org.springframework.test.context.support.AbstractTestExecutionListener;
/**
* A {@link TestExecutionListener} for creating and dropping MySql schemas if
* tests are setup with MySql.
*/
public abstract class AbstractSqlTestDatabase extends AbstractTestExecutionListener {
private static final Logger LOG = LoggerFactory.getLogger(AbstractSqlTestDatabase.class);
protected String schemaName;
protected String uri;
protected String username;
protected String password;
@Override
public void beforeTestClass(final TestContext testContext) throws Exception {
if (isRunningWithSql()) {
LOG.info("Setting up database for test class {}", testContext.getTestClass().getName());
this.username = System.getProperty("spring.datasource.username");
this.password = System.getProperty("spring.datasource.password");
this.uri = System.getProperty("spring.datasource.url");
createSchemaUri();
createSchema();
}
}
@Override
public void afterTestClass(final TestContext testContext) throws Exception {
if (isRunningWithSql()) {
dropSchema();
}
}
protected abstract void createSchemaUri();
protected abstract boolean isRunningWithSql();
protected abstract void createSchema();
protected abstract void dropSchema();
}

View File

@@ -16,53 +16,31 @@ import java.sql.SQLException;
import org.apache.commons.lang3.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestExecutionListener;
import org.springframework.test.context.support.AbstractTestExecutionListener;
/**
* A {@link TestExecutionListener} for creating and dropping MS SQL Server
* schemas if tests are setup with MS SQL Server.
*/
public class MsSqlTestDatabase extends AbstractTestExecutionListener {
public class MsSqlTestDatabase extends AbstractSqlTestDatabase {
private static final Logger LOG = LoggerFactory.getLogger(MsSqlTestDatabase.class);
private String schemaName;
private String uri;
private String username;
private String password;
@Override
public void beforeTestClass(final TestContext testContext) throws Exception {
if (isRunningWithMsSql()) {
LOG.info("Setting up mysql schema for test class {}", testContext.getTestClass().getName());
this.username = System.getProperty("spring.datasource.username");
this.password = System.getProperty("spring.datasource.password");
this.uri = System.getProperty("spring.datasource.url");
createSchemaUri();
createSchema();
}
}
@Override
public void afterTestClass(final TestContext testContext) throws Exception {
if (isRunningWithMsSql()) {
dropSchema();
}
}
private void createSchemaUri() {
protected void createSchemaUri() {
schemaName = "SP" + RandomStringUtils.randomAlphanumeric(10);
this.uri = this.uri.substring(0, uri.indexOf(';'));
System.setProperty("spring.datasource.url", uri + ";database=" + schemaName);
}
private static boolean isRunningWithMsSql() {
@Override
protected boolean isRunningWithSql() {
return "SQL_SERVER".equals(System.getProperty("spring.jpa.database"));
}
private void createSchema() {
@Override
protected void createSchema() {
try (Connection connection = DriverManager.getConnection(uri, username, password)) {
try (PreparedStatement statement = connection.prepareStatement("CREATE DATABASE " + schemaName + ";")) {
LOG.info("Creating schema {} on uri {}", schemaName, uri);
@@ -75,7 +53,8 @@ public class MsSqlTestDatabase extends AbstractTestExecutionListener {
}
private void dropSchema() {
@Override
protected void dropSchema() {
try (Connection connection = DriverManager.getConnection(uri, username, password)) {
// Needed to avoid the DROP is rejected with "database still in use"
try (PreparedStatement statement = connection

View File

@@ -16,53 +16,31 @@ import java.sql.SQLException;
import org.apache.commons.lang3.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestExecutionListener;
import org.springframework.test.context.support.AbstractTestExecutionListener;
/**
* A {@link TestExecutionListener} for creating and dropping MySql schemas if
* tests are setup with MySql.
*/
public class MySqlTestDatabase extends AbstractTestExecutionListener {
public class MySqlTestDatabase extends AbstractSqlTestDatabase {
private static final Logger LOG = LoggerFactory.getLogger(MySqlTestDatabase.class);
private String schemaName;
private String uri;
private String username;
private String password;
@Override
public void beforeTestClass(final TestContext testContext) throws Exception {
if (isRunningWithMySql()) {
LOG.info("Setting up mysql schema for test class {}", testContext.getTestClass().getName());
this.username = System.getProperty("spring.datasource.username");
this.password = System.getProperty("spring.datasource.password");
this.uri = System.getProperty("spring.datasource.url");
createSchemaUri();
createSchema();
}
}
@Override
public void afterTestClass(final TestContext testContext) throws Exception {
if (isRunningWithMySql()) {
dropSchema();
}
}
private void createSchemaUri() {
protected void createSchemaUri() {
schemaName = "SP" + RandomStringUtils.randomAlphanumeric(10);
this.uri = this.uri.substring(0, uri.lastIndexOf('/') + 1);
System.setProperty("spring.datasource.url", uri + schemaName);
}
private boolean isRunningWithMySql() {
@Override
protected boolean isRunningWithSql() {
return "MYSQL".equals(System.getProperty("spring.jpa.database"));
}
private void createSchema() {
@Override
protected void createSchema() {
try (Connection connection = DriverManager.getConnection(uri, username, password)) {
try (PreparedStatement statement = connection.prepareStatement("CREATE SCHEMA " + schemaName + ";")) {
LOG.info("Creating schema {} on uri {}", schemaName, uri);
@@ -75,7 +53,8 @@ public class MySqlTestDatabase extends AbstractTestExecutionListener {
}
private void dropSchema() {
@Override
protected void dropSchema() {
try (Connection connection = DriverManager.getConnection(uri, username, password)) {
try (PreparedStatement statement = connection.prepareStatement("DROP SCHEMA " + schemaName + ";")) {
LOG.info("Dropping schema {} on uri {}", schemaName, uri);

View File

@@ -0,0 +1,68 @@
/**
* Copyright (c) 2020 Microsoft and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.test.util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.apache.commons.lang3.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.test.context.TestExecutionListener;
/**
* A {@link TestExecutionListener} for creating and dropping MySql schemas if
* tests are setup with MySql.
*/
public class PostgreSqlTestDatabase extends AbstractSqlTestDatabase {
private static final Logger LOG = LoggerFactory.getLogger(PostgreSqlTestDatabase.class);
@Override
protected void createSchemaUri() {
schemaName = "sp" + RandomStringUtils.randomAlphanumeric(10).toLowerCase();
this.uri = this.uri.substring(0, uri.indexOf('?'));
System.setProperty("spring.datasource.url", uri + "?currentSchema=" + schemaName);
}
@Override
protected boolean isRunningWithSql() {
return "POSTGRESQL".equals(System.getProperty("spring.jpa.database"));
}
@Override
protected void createSchema() {
try (Connection connection = DriverManager.getConnection(uri, username, password)) {
try (PreparedStatement statement = connection.prepareStatement("CREATE schema " + schemaName + ";")) {
LOG.info("Creating schema {} on uri {}", schemaName, uri);
statement.execute();
LOG.info("Created schema {} on uri {}", schemaName, uri);
}
} catch (final SQLException e) {
LOG.error("Schema creation failed!", e);
}
}
@Override
protected void dropSchema() {
try (Connection connection = DriverManager.getConnection(uri, username, password)) {
try (PreparedStatement statement = connection.prepareStatement("DROP schema " + schemaName + " CASCADE;")) {
LOG.info("Dropping schema {} on uri {}", schemaName, uri);
statement.execute();
LOG.info("Dropped schema {} on uri {}", schemaName, uri);
}
} catch (final SQLException e) {
LOG.error("Schema drop failed!", e);
}
}
}