JpaDistributionSet#metadata made Map (#2411)

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2025-05-21 13:45:18 +03:00
committed by GitHub
parent ceba4f5cfb
commit 452d8618d7
26 changed files with 382 additions and 1216 deletions

View File

@@ -26,7 +26,6 @@ import org.eclipse.hawkbit.repository.jpa.builder.JpaActionStatusBuilder;
import org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutGroupBuilder;
import org.eclipse.hawkbit.repository.jpa.builder.JpaSoftwareModuleTypeBuilder;
import org.eclipse.hawkbit.repository.jpa.builder.JpaTagBuilder;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.MetaData;
import org.springframework.validation.annotation.Validated;
@@ -71,11 +70,6 @@ public class JpaEntityFactory implements EntityFactory {
return distributionSetBuilder;
}
@Override
public MetaData generateDsMetadata(final String key, final String value) {
return new JpaDistributionSetMetadata(key, value == null ? null : value.strip());
}
@Override
public SoftwareModuleMetadataBuilder softwareModuleMetadata() {
return softwareModuleMetadataBuilder;

View File

@@ -119,7 +119,6 @@ import org.eclipse.hawkbit.repository.jpa.model.helper.SecurityTokenGeneratorHol
import org.eclipse.hawkbit.repository.jpa.model.helper.TenantAwareHolder;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.repository.ActionStatusRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetMetadataRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTagRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTypeRepository;
@@ -536,7 +535,6 @@ public class RepositoryApplicationConfiguration {
final DistributionSetRepository distributionSetRepository,
final DistributionSetTagManagement distributionSetTagManagement, final SystemManagement systemManagement,
final DistributionSetTypeManagement distributionSetTypeManagement, final QuotaManagement quotaManagement,
final DistributionSetMetadataRepository distributionSetMetadataRepository,
final TargetRepository targetRepository,
final TargetFilterQueryRepository targetFilterQueryRepository, final ActionRepository actionRepository,
final SystemSecurityContext systemSecurityContext, final TenantConfigurationManagement tenantConfigurationManagement,
@@ -544,7 +542,7 @@ public class RepositoryApplicationConfiguration {
final DistributionSetTagRepository distributionSetTagRepository,
final JpaProperties properties, final RepositoryProperties repositoryProperties) {
return new JpaDistributionSetManagement(entityManager, distributionSetRepository, distributionSetTagManagement,
systemManagement, distributionSetTypeManagement, quotaManagement, distributionSetMetadataRepository,
systemManagement, distributionSetTypeManagement, quotaManagement,
targetRepository, targetFilterQueryRepository, actionRepository,
TenantConfigHelper.usingContext(systemSecurityContext, tenantConfigurationManagement),
virtualPropertyReplacer, softwareModuleRepository, distributionSetTagRepository,

View File

@@ -12,9 +12,9 @@ package org.eclipse.hawkbit.repository.jpa.management;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.IMPLICIT_LOCK_ENABLED;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -24,11 +24,15 @@ import java.util.function.Function;
import java.util.stream.Collectors;
import jakarta.persistence.EntityManager;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.MapJoin;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.metamodel.MapAttribute;
import jakarta.validation.constraints.NotNull;
import org.eclipse.hawkbit.repository.DistributionSetFields;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.DistributionSetMetadataFields;
import org.eclipse.hawkbit.repository.DistributionSetTagManagement;
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
import org.eclipse.hawkbit.repository.QuotaManagement;
@@ -49,14 +53,10 @@ import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.builder.JpaDistributionSetCreate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
import org.eclipse.hawkbit.repository.jpa.model.DsMetadataCompositeKey;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata_;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetMetadataRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTagRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
@@ -69,10 +69,8 @@ import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetFilter;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.MetaData;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Statistic;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
@@ -103,7 +101,6 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
private final SystemManagement systemManagement;
private final DistributionSetTypeManagement distributionSetTypeManagement;
private final QuotaManagement quotaManagement;
private final DistributionSetMetadataRepository distributionSetMetadataRepository;
private final TargetRepository targetRepository;
private final TargetFilterQueryRepository targetFilterQueryRepository;
private final ActionRepository actionRepository;
@@ -120,7 +117,6 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
final DistributionSetRepository distributionSetRepository,
final DistributionSetTagManagement distributionSetTagManagement, final SystemManagement systemManagement,
final DistributionSetTypeManagement distributionSetTypeManagement, final QuotaManagement quotaManagement,
final DistributionSetMetadataRepository distributionSetMetadataRepository,
final TargetRepository targetRepository,
final TargetFilterQueryRepository targetFilterQueryRepository, final ActionRepository actionRepository,
final TenantConfigHelper tenantConfigHelper,
@@ -135,7 +131,6 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
this.systemManagement = systemManagement;
this.distributionSetTypeManagement = distributionSetTypeManagement;
this.quotaManagement = quotaManagement;
this.distributionSetMetadataRepository = distributionSetMetadataRepository;
this.targetRepository = targetRepository;
this.targetFilterQueryRepository = targetFilterQueryRepository;
this.actionRepository = actionRepository;
@@ -360,21 +355,6 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
});
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetMetadata updateMetaData(final long id, final MetaData md) {
// check if exists otherwise throw entity not found exception
final JpaDistributionSetMetadata toUpdate = (JpaDistributionSetMetadata) findMetaDataByDistributionSetId(id, md.getKey())
.orElseThrow(() -> new EntityNotFoundException(DistributionSetMetadata.class, id, md.getKey()));
toUpdate.setValue(md.getValue());
// touch it to update the lock revision because we are modifying the DS indirectly, it will, also check UPDATE access
JpaManagementHelper.touch(entityManager, distributionSetRepository, (JpaDistributionSet) getValid(id));
return distributionSetMetadataRepository.save(toUpdate);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
@@ -394,34 +374,60 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<DistributionSetMetadata> putMetaData(final long id, final Collection<MetaData> md) {
public void createMetadata(final long id, final Map<String, String> md) {
final JpaDistributionSet distributionSet = (JpaDistributionSet) getValid(id);
assertMetaDataQuota(id, md.size());
md.forEach(meta -> checkAndThrowIfDistributionSetMetadataAlreadyExists(
new DsMetadataCompositeKey(id, meta.getKey())));
// get the modifiable metadata map
final Map<String, String> metadata = distributionSet.getMetadata();
md.keySet().forEach(key -> {
if (metadata.containsKey(key)) {
throw new EntityAlreadyExistsException("Metadata entry with key '" + key + "' already exists");
}
});
metadata.putAll(md);
JpaManagementHelper.touch(entityManager, distributionSetRepository, distributionSet);
assertMetaDataQuota(id, metadata.size());
return md.stream()
.map(meta -> distributionSetMetadataRepository
.save(new JpaDistributionSetMetadata(meta.getKey(), distributionSet, meta.getValue())))
.collect(Collectors.toUnmodifiableList());
distributionSetRepository.save(distributionSet);
}
@Override
public Map<String, String> getMetadata(final long id) {
assertDistributionSetExists(id);
return getMap(id, JpaDistributionSet_.metadata);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteMetaData(final long id, final String key) {
final JpaDistributionSetMetadata metadata = (JpaDistributionSetMetadata) findMetaDataByDistributionSetId(
id, key)
.orElseThrow(() -> new EntityNotFoundException(DistributionSetMetadata.class, id, key));
public void updateMetadata(final long id, final String key, final String value) {
final JpaDistributionSet distributionSet = (JpaDistributionSet) getValid(id);
// touch it to update the lock revision because we are modifying the
// DS indirectly, it will, also check UPDATE access
JpaManagementHelper.touch(entityManager, distributionSetRepository, metadata.getDistributionSet());
distributionSetMetadataRepository.deleteById(metadata.getId());
// get the modifiable metadata map
final Map<String, String> metadata = distributionSet.getMetadata();
if (!metadata.containsKey(key)) {
throw new EntityNotFoundException("DistributionSet metadata", id + ":" + key);
}
metadata.put(key, value);
distributionSetRepository.save(distributionSet);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteMetadata(final long id, final String key) {
final JpaDistributionSet distributionSet = (JpaDistributionSet) getValid(id);
// get the modifiable metadata map
final Map<String, String> metadata = distributionSet.getMetadata();
if (metadata.remove(key) == null) {
throw new EntityNotFoundException("DistributionSet metadata", id + ":" + key);
}
distributionSetRepository.save(distributionSet);
}
@Override
@@ -504,33 +510,6 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
return distributionSet;
}
@Override
public Page<DistributionSetMetadata> findMetaDataByDistributionSetId(final long id, final Pageable pageable) {
assertDistributionSetExists(id);
return JpaManagementHelper.findAllWithCountBySpec(distributionSetMetadataRepository, Collections.singletonList(byDsIdSpec(id)), pageable
);
}
@Override
public long countMetaDataByDistributionSetId(final long id) {
assertDistributionSetExists(id);
return distributionSetMetadataRepository.countByDistributionSetId(id);
}
@Override
public Page<DistributionSetMetadata> findMetaDataByDistributionSetIdAndRsql(final long id, final String rsqlParam,
final Pageable pageable) {
assertDistributionSetExists(id);
final List<Specification<JpaDistributionSetMetadata>> specList = Arrays
.asList(RSQLUtility.buildRsqlSpecification(rsqlParam, DistributionSetMetadataFields.class,
virtualPropertyReplacer, database), byDsIdSpec(id));
return JpaManagementHelper.findAllWithCountBySpec(distributionSetMetadataRepository, specList, pageable);
}
@Override
public Slice<DistributionSet> findByCompleted(final Pageable pageReq, final Boolean complete) {
final List<Specification<JpaDistributionSet>> specifications = buildSpecsByComplete(complete);
@@ -577,15 +556,6 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
DistributionSetSpecification.hasTag(tagId), DistributionSetSpecification.isNotDeleted()), pageable);
}
@Override
public Optional<DistributionSetMetadata> findMetaDataByDistributionSetId(final long id, final String key) {
assertDistributionSetExists(id);
return distributionSetMetadataRepository
.findById(new DsMetadataCompositeKey(id, key))
.map(DistributionSetMetadata.class::cast);
}
@Override
public long countByTypeId(final long typeId) {
if (!distributionSetTypeManagement.exists(typeId)) {
@@ -732,6 +702,24 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
}
}
private Map<String, String> getMap(final long id, final MapAttribute<JpaDistributionSet, String, String> mapAttribute) {
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
final Root<JpaDistributionSet> targetRoot = query.from(JpaDistributionSet.class);
query.where(cb.equal(targetRoot.get(AbstractJpaBaseEntity_.ID), id));
final MapJoin<JpaDistributionSet, String, String> mapJoin = targetRoot.join(mapAttribute);
query.multiselect(mapJoin.key(), mapJoin.value());
query.orderBy(cb.asc(mapJoin.key()));
return entityManager
.createQuery(query)
.getResultList()
.stream()
.collect(Collectors.toMap(entry -> (String) entry[0], entry -> (String) entry[1], (v1, v2) -> v1, LinkedHashMap::new));
}
private JpaSoftwareModule findSoftwareModuleAndThrowExceptionIfNotFound(final Long softwareModuleId) {
return softwareModuleRepository.findById(softwareModuleId)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, softwareModuleId));
@@ -754,9 +742,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
}
private void assertMetaDataQuota(final Long dsId, final int requested) {
QuotaHelper.assertAssignmentQuota(dsId, requested, quotaManagement.getMaxMetaDataEntriesPerDistributionSet(),
DistributionSetMetadata.class, DistributionSet.class,
distributionSetMetadataRepository::countByDistributionSetId);
final int limit = quotaManagement.getMaxMetaDataEntriesPerDistributionSet();
QuotaHelper.assertAssignmentQuota(dsId, requested, limit, "Metadata", DistributionSet.class.getSimpleName(), null);
}
private void assertSoftwareModuleQuota(final Long id, final int requested) {
@@ -764,11 +751,6 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
SoftwareModule.class, DistributionSet.class, softwareModuleRepository::countByAssignedToId);
}
private Specification<JpaDistributionSetMetadata> byDsIdSpec(final long dsId) {
return (root, query, cb) -> cb
.equal(root.get(JpaDistributionSetMetadata_.distributionSet).get(AbstractJpaBaseEntity_.id), dsId);
}
private void assertDistributionSetIsNotAssignedToTargets(final Long distributionSet) {
if (actionRepository.countByDistributionSetId(distributionSet) > 0) {
throw new EntityReadOnlyException(String.format(
@@ -776,13 +758,6 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
}
}
private void checkAndThrowIfDistributionSetMetadataAlreadyExists(final DsMetadataCompositeKey metadataId) {
if (distributionSetMetadataRepository.existsById(metadataId)) {
throw new EntityAlreadyExistsException(
"Metadata entry with key '" + metadataId.getKey() + "' already exists");
}
}
private List<JpaDistributionSet> getDistributionSets(final Collection<Long> ids) {
final List<JpaDistributionSet> foundDs = distributionSetRepository.findAllById(ids);
if (foundDs.size() != ids.size()) {

View File

@@ -1,76 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.model;
import java.io.Serial;
import java.io.Serializable;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* The DistributionSet Metadata composite key which contains the meta-data key and the ID of the DistributionSet itself.
*/
@NoArgsConstructor // Default constructor for JPA
@Setter
@Getter
public final class DsMetadataCompositeKey implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private String key;
private Long distributionSet;
/**
* @param distributionSet the distribution set for this meta data
* @param key the key of the meta data
*/
public DsMetadataCompositeKey(final Long distributionSet, final String key) {
this.distributionSet = distributionSet;
this.key = key;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (distributionSet == null ? 0 : distributionSet.hashCode());
result = prime * result + (key == null ? 0 : key.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final DsMetadataCompositeKey other = (DsMetadataCompositeKey) obj;
if (distributionSet == null) {
if (other.distributionSet != null) {
return false;
}
} else if (!distributionSet.equals(other.distributionSet)) {
return false;
}
if (key == null) {
return other.key == null;
} else {
return key.equals(other.key);
}
}
}

View File

@@ -13,13 +13,14 @@ import java.io.Serial;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import jakarta.persistence.CascadeType;
import jakarta.persistence.CollectionTable;
import jakarta.persistence.Column;
import jakarta.persistence.ConstraintMode;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.ForeignKey;
@@ -28,9 +29,9 @@ import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinTable;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.MapKeyColumn;
import jakarta.persistence.NamedAttributeNode;
import jakarta.persistence.NamedEntityGraph;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
import jakarta.validation.constraints.NotNull;
@@ -47,7 +48,6 @@ import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetExcepti
import org.eclipse.hawkbit.repository.exception.LockedException;
import org.eclipse.hawkbit.repository.exception.UnsupportedSoftwareModuleForThisDistributionSetException;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
@@ -109,11 +109,16 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_tag_tag")) })
private Set<DistributionSetTag> tags = new HashSet<>();
@ToString.Exclude
@OneToMany(mappedBy = "distributionSet", fetch = FetchType.LAZY,
cascade = { CascadeType.REMOVE },
targetEntity = JpaDistributionSetMetadata.class)
private List<DistributionSetMetadata> metadata;
// no cascade option on an ElementCollection, the target objects are always persisted, merged, removed with their parent
@Getter
@ElementCollection
@CollectionTable(
name = "sp_ds_metadata",
joinColumns = { @JoinColumn(name = "ds", nullable = false) },
foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_metadata_ds"))
@MapKeyColumn(name = "meta_key", length = DistributionSet.METADATA_KEY_SIZE)
@Column(name = "meta_value", length = DistributionSet.METADATA_VALUE_SIZE)
private Map<String, String> metadata;
@Column(name = "complete")
private boolean complete;
@@ -217,14 +222,6 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
return tags.remove(tag);
}
public List<DistributionSetMetadata> getMetadata() {
if (metadata == null) {
return Collections.emptyList();
}
return Collections.unmodifiableList(metadata);
}
public void lock() {
if (!isComplete()) {
throw new IncompleteDistributionSetException("Could not be locked while incomplete!");

View File

@@ -1,86 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.model;
import java.io.Serial;
import jakarta.persistence.ConstraintMode;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.ForeignKey;
import jakarta.persistence.Id;
import jakarta.persistence.IdClass;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
/**
* Meta data for {@link DistributionSet}.
*/
@NoArgsConstructor // Default constructor needed for JPA entities.
@Getter
@IdClass(DsMetadataCompositeKey.class)
@Entity
@Table(name = "sp_ds_metadata")
public class JpaDistributionSetMetadata extends AbstractJpaMetaData implements DistributionSetMetadata {
@Serial
private static final long serialVersionUID = 1L;
@Id
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "ds", nullable = false, updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_ds_metadata_ds"))
private JpaDistributionSet distributionSet;
public JpaDistributionSetMetadata(final String key, final String value) {
super(key, value);
}
public JpaDistributionSetMetadata(final String key, final DistributionSet distributionSet, final String value) {
super(key, value);
this.distributionSet = (JpaDistributionSet) distributionSet;
}
public DsMetadataCompositeKey getId() {
return new DsMetadataCompositeKey(distributionSet.getId(), getKey());
}
public void setDistributionSet(final DistributionSet distributionSet) {
this.distributionSet = (JpaDistributionSet) distributionSet;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((distributionSet == null) ? 0 : distributionSet.hashCode());
return result;
}
@Override
// exception squid:S2259 - obj is checked for null in super
@SuppressWarnings("squid:S2259")
public boolean equals(final Object obj) {
if (!super.equals(obj)) {
return false;
}
final JpaDistributionSetMetadata other = (JpaDistributionSetMetadata) obj;
if (distributionSet == null) {
return other.distributionSet == null;
} else {
return distributionSet.equals(other.distributionSet);
}
}
}

View File

@@ -1,39 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.repository;
import org.eclipse.hawkbit.repository.jpa.model.DsMetadataCompositeKey;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;
/**
* {@link DistributionSetMetadata} repository.
*/
@Transactional(readOnly = true)
public interface DistributionSetMetadataRepository
extends PagingAndSortingRepository<JpaDistributionSetMetadata, DsMetadataCompositeKey>,
CrudRepository<JpaDistributionSetMetadata, DsMetadataCompositeKey>,
JpaSpecificationExecutor<JpaDistributionSetMetadata> {
/**
* Counts the meta data entries that match the given distribution set ID.
* <p/>
* No access control applied
*
* @param id of the distribution set.
* @return The number of matching meta data entries.
*/
long countByDistributionSetId(@Param("id") Long id);
}

View File

@@ -16,6 +16,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import jakarta.persistence.criteria.Predicate;
@@ -28,14 +29,12 @@ import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetFilter;
import org.eclipse.hawkbit.repository.model.MetaData;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.junit.jupiter.api.Test;
@@ -121,12 +120,18 @@ class DistributionSetAccessControllerTest extends AbstractAccessControllerTest {
@Description("Verifies read access rules for distribution sets")
void verifyDistributionSetUpdates() {
// permit all operations first to prepare test setup
permitAllOperations(AccessController.Operation.READ);
permitAllOperations(AccessController.Operation.CREATE);
permitAllOperations(AccessController.Operation.UPDATE);
final DistributionSet permitted = testdataFactory.createDistributionSet();
final String mdPresetKey = "metadata.preset";
final String mdPresetValue = "presetValue";
distributionSetManagement.createMetadata(permitted.getId(), Map.of(mdPresetKey, mdPresetValue));
final DistributionSet readOnly = testdataFactory.createDistributionSet();
distributionSetManagement.createMetadata(readOnly.getId(), Map.of(mdPresetKey, mdPresetValue));
final DistributionSet hidden = testdataFactory.createDistributionSet();
distributionSetManagement.createMetadata(hidden.getId(), Map.of(mdPresetKey, mdPresetValue));
final SoftwareModule swModule = testdataFactory.createSoftwareModuleOs();
@@ -134,52 +139,49 @@ class DistributionSetAccessControllerTest extends AbstractAccessControllerTest {
testAccessControlManger.deleteAllRules();
// define access controlling rule
defineAccess(AccessController.Operation.READ, permitted, readOnly);
// allow updating the permitted distributionSet
defineAccess(AccessController.Operation.READ, permitted);
defineAccess(AccessController.Operation.UPDATE, permitted);
// verify distributionSetManagement#assignSoftwareModules
final var singleModuleIdList = Collections.singletonList(swModule.getId());
final List<Long> singleModuleIdList = Collections.singletonList(swModule.getId());
assertThat(distributionSetManagement.assignSoftwareModules(permitted.getId(), singleModuleIdList))
.satisfies(ds -> assertThat(ds.getModules().stream().map(Identifiable::getId).toList()).contains(swModule.getId()));
final Long readOnlyId = readOnly.getId();
assertThatThrownBy(() -> distributionSetManagement.assignSoftwareModules(readOnlyId, singleModuleIdList))
.as("Distribution set not allowed to me modified.")
.isInstanceOf(EntityNotFoundException.class);
.isInstanceOf(InsufficientPermissionException.class);
final Long hiddenId = hidden.getId();
assertThatThrownBy(() -> distributionSetManagement.assignSoftwareModules(hiddenId, singleModuleIdList))
.as("Distribution set should not be visible.")
.isInstanceOf(EntityNotFoundException.class);
final JpaDistributionSetMetadata metadata = new JpaDistributionSetMetadata("test", "test");
final Map<String, String> metadata = Map.of("test.create", mdPresetValue);
// verify distributionSetManagement#createMetaData
final List<MetaData> metadataList = Collections.singletonList(metadata);
distributionSetManagement.putMetaData(permitted.getId(), metadataList);
assertThatThrownBy(() -> distributionSetManagement.putMetaData(readOnlyId, metadataList))
.as("Distribution set not allowed to me modified.")
.isInstanceOf(EntityNotFoundException.class);
assertThatThrownBy(() -> distributionSetManagement.putMetaData(hiddenId, metadataList))
distributionSetManagement.createMetadata(permitted.getId(), metadata);
assertThatThrownBy(() -> distributionSetManagement.createMetadata(readOnlyId, metadata))
.as("Distribution set not allowed to be modified.")
.isInstanceOf(InsufficientPermissionException.class);
assertThatThrownBy(() -> distributionSetManagement.createMetadata(hiddenId, metadata))
.as("Distribution set should not be visible.")
.isInstanceOf(EntityNotFoundException.class);
// verify distributionSetManagement#updateMetaData
distributionSetManagement.updateMetaData(permitted.getId(), metadata);
assertThatThrownBy(() -> distributionSetManagement.updateMetaData(readOnlyId, metadata))
final String newValue = "newValue";
distributionSetManagement.updateMetadata(permitted.getId(), mdPresetKey, newValue);
assertThatThrownBy(() -> distributionSetManagement.updateMetadata(readOnlyId, mdPresetKey, newValue))
.as("Distribution set not allowed to me modified.")
.isInstanceOf(EntityNotFoundException.class);
assertThatThrownBy(() -> distributionSetManagement.updateMetaData(hiddenId, metadata))
.isInstanceOf(InsufficientPermissionException.class);
assertThatThrownBy(() -> distributionSetManagement.updateMetadata(hiddenId, mdPresetKey, newValue))
.as("Distribution set should not be visible.")
.isInstanceOf(EntityNotFoundException.class);
// verify distributionSetManagement#deleteMetaData
final String metadataKey = metadata.getKey();
distributionSetManagement.deleteMetaData(permitted.getId(), metadataKey);
assertThatThrownBy(() -> distributionSetManagement.deleteMetaData(readOnlyId, metadataKey))
final String metadataKey = metadata.entrySet().stream().findAny().get().getKey();
distributionSetManagement.deleteMetadata(permitted.getId(), metadataKey);
assertThatThrownBy(() -> distributionSetManagement.deleteMetadata(readOnlyId, mdPresetKey))
.as("Distribution set not allowed to me modified.")
.isInstanceOf(EntityNotFoundException.class);
assertThatThrownBy(() -> distributionSetManagement.deleteMetaData(hiddenId, metadataKey))
.isInstanceOf(InsufficientPermissionException.class);
assertThatThrownBy(() -> distributionSetManagement.deleteMetadata(hiddenId, mdPresetKey))
.as("Distribution set should not be visible.")
.isInstanceOf(EntityNotFoundException.class);
}

View File

@@ -220,13 +220,12 @@ class TargetAccessControllerTest extends AbstractAccessControllerTest {
final Target permittedTarget = targetManagement
.create(entityFactory.target().create().controllerId("device01").status(TargetUpdateStatus.REGISTERED));
final String hiddenTargetControllerId = targetManagement
.create(entityFactory.target().create().controllerId("device02").status(TargetUpdateStatus.REGISTERED))
.getControllerId();
// define access controlling rule
defineAccess(AccessController.Operation.READ, permittedTarget);
overwriteAccess(AccessController.Operation.READ, permittedTarget);
// verify targetManagement#findByUpdateStatus before assignment
assertThat(targetManagement.findByUpdateStatus(Pageable.unpaged(), TargetUpdateStatus.REGISTERED).get()
@@ -269,18 +268,15 @@ class TargetAccessControllerTest extends AbstractAccessControllerTest {
final Target manageableTarget = targetManagement
.create(entityFactory.target().create().controllerId("device01").status(TargetUpdateStatus.REGISTERED));
final Target readOnlyTarget = targetManagement
.create(entityFactory.target().create().controllerId("device02").status(TargetUpdateStatus.REGISTERED));
// define access controlling rule
defineAccess(AccessController.Operation.READ, manageableTarget, readOnlyTarget);
defineAccess(AccessController.Operation.UPDATE, manageableTarget);
// overwriting full access controlling rule
overwriteAccess(AccessController.Operation.READ, manageableTarget, readOnlyTarget);
overwriteAccess(AccessController.Operation.UPDATE, manageableTarget);
// assignment is permitted for manageableTarget
assertThat(assignDistributionSet(firstDsId, manageableTarget.getControllerId()).getAssigned())
.isEqualTo(1);
assertThat(assignDistributionSet(firstDsId, manageableTarget.getControllerId()).getAssigned()).isEqualTo(1);
// assignment is denied for readOnlyTarget (read, but no update permissions)
final var readOnlyTargetControllerId = readOnlyTarget.getControllerId();
@@ -309,19 +305,17 @@ class TargetAccessControllerTest extends AbstractAccessControllerTest {
final List<Target> updateTargets = testdataFactory.createTargets("update1", "update2", "update3");
final List<Target> readTargets = testdataFactory.createTargets("read1", "read2", "read3", "read4");
final List<Target> hiddenTargets = testdataFactory.createTargets(
"hidden1", "hidden2", "hidden3", "hidden4", "hidden5");
final List<Target> hiddenTargets = testdataFactory.createTargets("hidden1", "hidden2", "hidden3", "hidden4", "hidden5");
defineAccess(AccessController.Operation.UPDATE, updateTargets);
defineAccess(AccessController.Operation.READ, merge(readTargets, updateTargets));
overwriteAccess(AccessController.Operation.READ, merge(readTargets, updateTargets));
final Rollout rollout = testdataFactory.createRolloutByVariables("testRollout", "description",
updateTargets.size(), "id==*", ds, "50", "5");
final Rollout rollout = testdataFactory.createRolloutByVariables(
"testRollout", "description", updateTargets.size(), "id==*", ds, "50", "5");
assertThat(rollout.getTotalTargets()).isEqualTo(updateTargets.size());
final List<RolloutGroup> content = rolloutGroupManagement.findByRollout(rollout.getId(), Pageable.unpaged())
.getContent();
final List<RolloutGroup> content = rolloutGroupManagement.findByRollout(rollout.getId(), Pageable.unpaged()).getContent();
assertThat(content).hasSize(updateTargets.size());
final List<Target> rolloutTargets = content.stream().flatMap(
@@ -393,6 +387,18 @@ class TargetAccessControllerTest extends AbstractAccessControllerTest {
target -> ids.contains(target.getId()));
}
private void overwriteAccess(final AccessController.Operation operation, final Target... target) {
overwriteAccess(operation, List.of(target));
}
private void overwriteAccess(final AccessController.Operation operation, final List<Target> targets) {
final List<Long> ids = targets.stream().map(Target::getId).toList();
testAccessControlManger.overwriteAccessRule(
JpaTarget.class, operation,
TargetSpecifications.hasIdIn(ids),
target -> ids.contains(target.getId()));
}
private static Specification<JpaDistributionSet> dsById(final Long distid) {
return (dsRoot, query, cb) -> {
final Predicate predicate = cb.equal(dsRoot.get(JpaDistributionSet_.id), distid);

View File

@@ -14,6 +14,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
@@ -37,14 +38,9 @@ class TargetTypeAccessControllerTest extends AbstractAccessControllerTest {
@Test
@Description("Verifies read access rules for target types")
void verifyTargetTypeReadOperations() {
permitAllOperations(AccessController.Operation.READ);
permitAllOperations(AccessController.Operation.CREATE);
final TargetType permittedTargetType = targetTypeManagement
.create(entityFactory.targetType().create().name("type1"));
final TargetType hiddenTargetType = targetTypeManagement
.create(entityFactory.targetType().create().name("type2"));
final TargetType permittedTargetType = targetTypeManagement.create(entityFactory.targetType().create().name("type1"));
final TargetType hiddenTargetType = targetTypeManagement.create(entityFactory.targetType().create().name("type2"));
// define access controlling rule
defineAccess(AccessController.Operation.READ, permittedTargetType);
@@ -67,12 +63,12 @@ class TargetTypeAccessControllerTest extends AbstractAccessControllerTest {
assertThat(targetTypeManagement.count()).isEqualTo(1);
// verify targetTypeManagement#countByName
// assertThat(targetTypeManagement.countByName(permittedTargetType.getName())).isEqualTo(1);
// assertThat(targetTypeManagement.countByName(hiddenTargetType.getName())).isZero();
assertThat(targetTypeManagement.countByName(permittedTargetType.getName())).isEqualTo(1);
assertThat(targetTypeManagement.countByName(hiddenTargetType.getName())).isZero();
// verify targetTypeManagement#countByName
// assertThat(targetTypeManagement.countByName(permittedTargetType.getName())).isEqualTo(1);
// assertThat(targetTypeManagement.countByName(hiddenTargetType.getName())).isZero();
assertThat(targetTypeManagement.countByName(permittedTargetType.getName())).isEqualTo(1);
assertThat(targetTypeManagement.countByName(hiddenTargetType.getName())).isZero();
// verify targetTypeManagement#get by id
assertThat(targetTypeManagement.get(permittedTargetType.getId())).isPresent();
@@ -161,15 +157,11 @@ class TargetTypeAccessControllerTest extends AbstractAccessControllerTest {
.isInstanceOf(InsufficientPermissionException.class);
}
private void defineAccess(final AccessController.Operation operation, final TargetType... targetType) {
defineAccess(operation, List.of(targetType));
}
private void defineAccess(final AccessController.Operation operation, final List<TargetType> targetTypes) {
final List<Long> ids = targetTypes.stream().map(TargetType::getId).toList();
private void defineAccess(final AccessController.Operation operation, final TargetType... targetTypes) {
final List<Long> ids = Stream.of(targetTypes).map(TargetType::getId).toList();
testAccessControlManger.defineAccessRule(
JpaTargetType.class, operation,
TargetTypeSpecification.hasIdIn(ids),
targetType -> ids.contains(targetType.getId()));
}
}
}

View File

@@ -31,7 +31,23 @@ public class TestAccessControlManger {
public <T> void defineAccessRule(
final Class<T> ruleClass, final AccessController.Operation operation,
final Specification<T> specification, final Predicate<T> check) {
accessRules.put(new AccessRuleId<>(ruleClass, operation), new AccessRule<>(specification, check));
defineAccessRule(ruleClass, operation, specification, check, false);
}
public <T> void overwriteAccessRule(
final Class<T> ruleClass, final AccessController.Operation operation,
final Specification<T> specification, final Predicate<T> check) {
defineAccessRule(ruleClass, operation, specification, check, true);
}
private <T> void defineAccessRule(
final Class<T> ruleClass, final AccessController.Operation operation,
final Specification<T> specification, final Predicate<T> check, final boolean overwrite) {
final AccessRuleId<T> ruleId = new AccessRuleId<>(ruleClass, operation);
if (!overwrite && accessRules.containsKey(ruleId)) {
throw new IllegalStateException("Access rule already defined for " + ruleId + "! You should explicitly set overwrite to true.");
}
accessRules.put(ruleId, new AccessRule<>(specification, check));
}
public <T extends AbstractJpaBaseEntity> Specification<T> getAccessRule(final Class<T> ruleClass,
@@ -53,8 +69,7 @@ public class TestAccessControlManger {
} else {
for (final T entity : entities) {
if (!accessRule.checker.test(entity)) {
throw new InsufficientPermissionException(
"Access to " + ruleClass.getName() + "/" + entity + " not allowed by checker!");
throw new InsufficientPermissionException("Access to " + ruleClass.getName() + "/" + entity + " not allowed by checker!");
}
}
}
@@ -67,4 +82,4 @@ public class TestAccessControlManger {
private record AccessRuleId<T>(Class<T> ruleClass, AccessController.Operation operation) {}
private record AccessRule<T>(Specification<T> specification, Predicate<T> checker) {}
}
}

View File

@@ -10,6 +10,7 @@
package org.eclipse.hawkbit.repository.jpa.management;
import java.util.List;
import java.util.Map;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
@@ -66,17 +67,36 @@ class DistributionSetManagementSecurityTest
@Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void createMetaDataPermissionsCheck() {
void createMetadataPermissionsCheck() {
assertPermissions(
() -> distributionSetManagement.putMetaData(1L, List.of(entityFactory.generateDsMetadata("key", "value"))),
() -> {
distributionSetManagement.createMetadata(1L, Map.of("key", "value"));
return null;
},
List.of(SpPermission.UPDATE_REPOSITORY));
}
@Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void deleteMetaDataPermissionsCheck() {
void getMetadataPermissiosCheck() {
assertPermissions(() -> distributionSetManagement.getMetadata(1L), List.of(SpPermission.READ_REPOSITORY));
}
@Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void updateMetadataPermissionsCheck() {
assertPermissions(() -> {
distributionSetManagement.deleteMetaData(1L, "key");
distributionSetManagement.updateMetadata(1L,"key", "value");
return null;
},
List.of(SpPermission.UPDATE_REPOSITORY));
}
@Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void deleteMetadataPermissionsCheck() {
assertPermissions(() -> {
distributionSetManagement.deleteMetadata(1L, "key");
return null;
}, List.of(SpPermission.UPDATE_REPOSITORY));
}
@@ -135,25 +155,6 @@ class DistributionSetManagementSecurityTest
assertPermissions(() -> distributionSetManagement.getOrElseThrowException(1L), List.of(SpPermission.READ_REPOSITORY));
}
@Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void findMetaDataByDistributionSetIdPermissionsCheck() {
assertPermissions(() -> distributionSetManagement.findMetaDataByDistributionSetId(1L, PAGE), List.of(SpPermission.READ_REPOSITORY));
}
@Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void countMetaDataByDistributionSetIdPermissionsCheck() {
assertPermissions(() -> distributionSetManagement.countMetaDataByDistributionSetId(1L), List.of(SpPermission.READ_REPOSITORY));
}
@Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void findMetaDataByDistributionSetIdAndRsqlPermissionsCheck() {
assertPermissions(() -> distributionSetManagement.findMetaDataByDistributionSetIdAndRsql(1L, "rsql", PAGE),
List.of(SpPermission.READ_REPOSITORY));
}
@Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void findByCompletedPermissionsCheck() {
@@ -192,12 +193,6 @@ class DistributionSetManagementSecurityTest
assertPermissions(() -> distributionSetManagement.findByRsqlAndTag("rsql", 1L, PAGE), List.of(SpPermission.READ_REPOSITORY));
}
@Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void getMetaDataByDistributionSetIdPermissionsCheck() {
assertPermissions(() -> distributionSetManagement.findMetaDataByDistributionSetId(1L, "key"), List.of(SpPermission.READ_REPOSITORY));
}
@Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void isInUsePermissionsCheck() {
@@ -210,13 +205,6 @@ class DistributionSetManagementSecurityTest
assertPermissions(() -> distributionSetManagement.unassignSoftwareModule(1L, 1L), List.of(SpPermission.UPDATE_REPOSITORY));
}
@Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void updateMetaDataPermissionsCheck() {
assertPermissions(() -> distributionSetManagement.updateMetaData(1L, entityFactory.generateDsMetadata("key", "value")),
List.of(SpPermission.UPDATE_REPOSITORY));
}
@Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void countByTypeIdPermissionsCheck() {

View File

@@ -18,7 +18,9 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.Set;
@@ -49,17 +51,14 @@ import org.eclipse.hawkbit.repository.exception.LockedException;
import org.eclipse.hawkbit.repository.exception.UnsupportedSoftwareModuleForThisDistributionSetException;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetFilter;
import org.eclipse.hawkbit.repository.model.DistributionSetFilter.DistributionSetFilterBuilder;
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation;
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation.CancelationType;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.MetaData;
import org.eclipse.hawkbit.repository.model.NamedEntity;
import org.eclipse.hawkbit.repository.model.NamedVersionedEntity;
import org.eclipse.hawkbit.repository.model.Rollout;
@@ -71,8 +70,6 @@ import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
/**
* {@link DistributionSetManagement} tests.
@@ -96,7 +93,7 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
assertThat(distributionSetManagement.get(NOT_EXIST_IDL)).isNotPresent();
assertThat(distributionSetManagement.getWithDetails(NOT_EXIST_IDL)).isNotPresent();
assertThat(distributionSetManagement.findByNameAndVersion(NOT_EXIST_ID, NOT_EXIST_ID)).isNotPresent();
assertThat(distributionSetManagement.findMetaDataByDistributionSetId(set.getId(), NOT_EXIST_ID)).isNotPresent();
assertThat(distributionSetManagement.getMetadata(set.getId()).get(NOT_EXIST_ID)).isNull();
}
@Test
@@ -138,44 +135,30 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
verifyThrownExceptionBy(() -> distributionSetManagement.create(
entityFactory.distributionSet().create().name("xxx").type(NOT_EXIST_ID)), "DistributionSetType");
verifyThrownExceptionBy(() -> distributionSetManagement.putMetaData(
NOT_EXIST_IDL, singletonList(entityFactory.generateDsMetadata("123", "123"))), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.createMetadata(NOT_EXIST_IDL, Map.of("123", "123")), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.delete(singletonList(NOT_EXIST_IDL)), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.delete(NOT_EXIST_IDL), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.deleteMetaData(NOT_EXIST_IDL, "xxx"),
"DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.deleteMetaData(set.getId(), NOT_EXIST_ID),
"DistributionSetMetadata");
verifyThrownExceptionBy(() -> distributionSetManagement.deleteMetadata(NOT_EXIST_IDL, "xxx"), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.deleteMetadata(set.getId(), NOT_EXIST_ID), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.findByAction(NOT_EXIST_IDL), "Action");
verifyThrownExceptionBy(() -> distributionSetManagement.findMetaDataByDistributionSetId(NOT_EXIST_IDL, "xxx"),
"DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.getMetadata(NOT_EXIST_IDL).get("xxx"), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.findMetaDataByDistributionSetId(NOT_EXIST_IDL, PAGE),
"DistributionSet");
verifyThrownExceptionBy(
() -> distributionSetManagement.findMetaDataByDistributionSetIdAndRsql(NOT_EXIST_IDL, "name==*", PAGE),
"DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.getMetadata(NOT_EXIST_IDL).get(PAGE), "DistributionSet");
assertThatThrownBy(() -> distributionSetManagement.isInUse(NOT_EXIST_IDL))
.isInstanceOf(EntityNotFoundException.class).hasMessageContaining(NOT_EXIST_ID)
.hasMessageContaining("DistributionSet");
verifyThrownExceptionBy(
() -> distributionSetManagement.update(entityFactory.distributionSet().update(NOT_EXIST_IDL)),
"DistributionSet");
() -> distributionSetManagement.update(entityFactory.distributionSet().update(NOT_EXIST_IDL)), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.updateMetaData(NOT_EXIST_IDL,
entityFactory.generateDsMetadata("xxx", "xxx")), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.updateMetadata(NOT_EXIST_IDL,"xxx", "xxx"), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.updateMetadata(set.getId(),NOT_EXIST_ID, "xxx"), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.updateMetaData(set.getId(),
entityFactory.generateDsMetadata(NOT_EXIST_ID, "xxx")), "DistributionSetMetadata");
verifyThrownExceptionBy(() -> distributionSetManagement.getOrElseThrowException(NOT_EXIST_IDL),
"DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.getOrElseThrowException(NOT_EXIST_IDL), "DistributionSet");
verifyThrownExceptionBy(() -> distributionSetManagement.getValidAndComplete(NOT_EXIST_IDL), "DistributionSet");
@@ -247,68 +230,57 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Checks that metadata for a distribution set can be created.")
void createDistributionSetMetadata() {
void createMetadata() {
final String knownKey = "dsMetaKnownKey";
final String knownValue = "dsMetaKnownValue";
final DistributionSet ds = testdataFactory.createDistributionSet("testDs");
final DistributionSetMetadata metadata = new JpaDistributionSetMetadata(knownKey, ds, knownValue);
final JpaDistributionSetMetadata createdMetadata = (JpaDistributionSetMetadata) createDistributionSetMetadata(
ds.getId(), metadata);
assertThat(createdMetadata).isNotNull();
assertThat(createdMetadata.getId().getKey()).isEqualTo(knownKey);
assertThat(createdMetadata.getDistributionSet().getId()).isEqualTo(ds.getId());
assertThat(createdMetadata.getValue()).isEqualTo(knownValue);
insertMetadata(knownKey, knownValue, ds); // and validate
}
@Test
@Description("Verifies the enforcement of the metadata quota per distribution set.")
void createDistributionSetMetadataUntilQuotaIsExceeded() {
void createMetadataUntilQuotaIsExceeded() {
// add meta data one by one
final DistributionSet ds1 = testdataFactory.createDistributionSet("ds1");
final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerDistributionSet();
for (int i = 0; i < maxMetaData; ++i) {
assertThat((JpaDistributionSetMetadata) createDistributionSetMetadata(ds1.getId(),
new JpaDistributionSetMetadata("k" + i, ds1, "v" + i))).isNotNull();
insertMetadata("k" + i, "v" + i, ds1);
}
// quota exceeded
final Long ds1Id = ds1.getId();
final JpaDistributionSetMetadata jpaMaxMetaData = new JpaDistributionSetMetadata("k" + maxMetaData, ds1, "v" + maxMetaData);
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
.isThrownBy(() -> createDistributionSetMetadata(ds1Id, jpaMaxMetaData));
.isThrownBy(() -> insertMetadata("k" + maxMetaData, "v" + maxMetaData, ds1));
// add multiple meta data entries at once
final DistributionSet ds2 = testdataFactory.createDistributionSet("ds2");
final List<MetaData> metaData2 = new ArrayList<>();
final Map<String, String> metaData2 = new HashMap<>();
for (int i = 0; i < maxMetaData + 1; ++i) {
metaData2.add(new JpaDistributionSetMetadata("k" + i, ds2, "v" + i));
metaData2.put("k" + i, "v" + i);
}
// verify quota is exceeded
final Long ds2Id = ds2.getId();
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
.isThrownBy(() -> createDistributionSetMetadata(ds2Id, metaData2));
.isThrownBy(() -> distributionSetManagement.createMetadata(ds2Id, metaData2));
// add some meta data entries
final DistributionSet ds3 = testdataFactory.createDistributionSet("ds3");
final int firstHalf = Math.round((maxMetaData) / 2.f);
final Long ds3Id = ds3.getId();
for (int i = 0; i < firstHalf; ++i) {
createDistributionSetMetadata(ds3Id, new JpaDistributionSetMetadata("k" + i, ds3, "v" + i));
insertMetadata("k" + i, "v" + i, ds3);
}
// add too many data entries
final int secondHalf = maxMetaData - firstHalf;
final List<MetaData> metaData3 = new ArrayList<>();
final Map<String, String> metaData3 = new HashMap<>();
for (int i = 0; i < secondHalf + 1; ++i) {
metaData3.add(new JpaDistributionSetMetadata("kk" + i, ds3, "vv" + i));
metaData3.put("kk" + i, "vv" + i);
}
// verify quota is exceeded
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
.isThrownBy(() -> createDistributionSetMetadata(ds3Id, metaData3));
.isThrownBy(() -> distributionSetManagement.createMetadata(ds3Id, metaData3));
}
@Test
@@ -507,7 +479,7 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
@Test
@WithUser(allSpPermissions = true)
@Description("Checks that metadata for a distribution set can be updated.")
void updateDistributionSetMetadata() {
void updateMetadata() {
final String knownKey = "myKnownKey";
final String knownValue = "myKnownValue";
final String knownUpdateValue = "myNewUpdatedValue";
@@ -519,26 +491,22 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
waitNextMillis();
// create an DS meta data entry
createDistributionSetMetadata(ds.getId(), new JpaDistributionSetMetadata(knownKey, ds, knownValue));
insertMetadata(knownKey, knownValue, ds);
final DistributionSet changedLockRevisionDS = getOrThrow(distributionSetManagement.get(ds.getId()));
assertThat(changedLockRevisionDS.getOptLockRevision()).isEqualTo(2);
waitNextMillis();
// update the DS metadata
final JpaDistributionSetMetadata updated = (JpaDistributionSetMetadata) distributionSetManagement
.updateMetaData(ds.getId(), entityFactory.generateDsMetadata(knownKey, knownUpdateValue));
distributionSetManagement.updateMetadata(ds.getId(), knownKey, knownUpdateValue);
// we are updating the sw metadata so also modifying the base software
// module so opt lock revision must be three
final DistributionSet reloadedDS = getOrThrow(distributionSetManagement.get(ds.getId()));
assertThat(reloadedDS.getOptLockRevision()).isEqualTo(3);
assertThat(reloadedDS.getLastModifiedAt()).isPositive();
// verify updated meta data contains the updated value
assertThat(updated).isNotNull();
assertThat(updated.getValue()).isEqualTo(knownUpdateValue);
assertThat(updated.getId().getKey()).isEqualTo(knownKey);
assertThat(updated.getDistributionSet().getId()).isEqualTo(ds.getId());
// verify updated meta data is the updated value
assertThat(distributionSetManagement.getMetadata(ds.getId()).get(knownKey)).isEqualTo(knownUpdateValue);
}
@Test
@@ -783,37 +751,22 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
}
@Test
@Description("Queries and loads the metadata related to a given software module.")
void findAllDistributionSetMetadataByDsId() {
@Description("Queries and loads the metadata related to a given distribution set.")
void getMetadata() {
// create a DS
final DistributionSet ds1 = testdataFactory.createDistributionSet("testDs1");
final DistributionSet ds2 = testdataFactory.createDistributionSet("testDs2");
for (int index = 0; index < quotaManagement.getMaxMetaDataEntriesPerDistributionSet(); index++) {
createDistributionSetMetadata(ds1.getId(),
new JpaDistributionSetMetadata("key" + index, ds1, "value" + index));
insertMetadata("key" + index, "value" + index, ds1);
}
for (int index = 0; index <= quotaManagement.getMaxMetaDataEntriesPerDistributionSet() - 2; index++) {
createDistributionSetMetadata(ds2.getId(),
new JpaDistributionSetMetadata("key" + index, ds2, "value" + index));
insertMetadata("key" + index, "value" + index, ds2);
}
final Page<DistributionSetMetadata> metadataOfDs1 = distributionSetManagement
.findMetaDataByDistributionSetId(ds1.getId(), PageRequest.of(0, 100));
final Page<DistributionSetMetadata> metadataOfDs2 = distributionSetManagement
.findMetaDataByDistributionSetId(ds2.getId(), PageRequest.of(0, 100));
assertThat(metadataOfDs1.getNumberOfElements())
.isEqualTo(quotaManagement.getMaxMetaDataEntriesPerDistributionSet());
assertThat(metadataOfDs1.getTotalElements())
.isEqualTo(quotaManagement.getMaxMetaDataEntriesPerDistributionSet());
assertThat(metadataOfDs2.getNumberOfElements())
.isEqualTo(quotaManagement.getMaxMetaDataEntriesPerDistributionSet() - 1);
assertThat(metadataOfDs2.getTotalElements())
.isEqualTo(quotaManagement.getMaxMetaDataEntriesPerDistributionSet() - 1);
assertThat(distributionSetManagement.getMetadata(ds1.getId())).hasSize(quotaManagement.getMaxMetaDataEntriesPerDistributionSet());
assertThat(distributionSetManagement.getMetadata(ds2.getId())).hasSize(quotaManagement.getMaxMetaDataEntriesPerDistributionSet() - 1);
}
@Test
@@ -897,23 +850,20 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
final String knownUpdateValue = "knownUpdateValue";
final Long dsId = testdataFactory.createDistributionSet().getId();
distributionSetManagement.putMetaData(dsId,
singletonList(entityFactory.generateDsMetadata(knownKey1, knownValue)));
distributionSetManagement.createMetadata(dsId, Map.of(knownKey1, knownValue));
distributionSetInvalidationManagement.invalidateDistributionSet(
new DistributionSetInvalidation(singletonList(dsId), CancelationType.NONE, false));
// assert that no new metadata can be created
final List<MetaData> metadata = singletonList(entityFactory.generateDsMetadata(knownKey2, knownValue));
assertThatExceptionOfType(InvalidDistributionSetException.class)
.as("Invalid distributionSet should throw an exception")
.isThrownBy(() -> distributionSetManagement.putMetaData(dsId, metadata));
.isThrownBy(() -> distributionSetManagement.createMetadata(dsId, Map.of(knownKey2, knownValue)));
// assert that an existing metadata can not be updated
final MetaData metadata2 = entityFactory.generateDsMetadata(knownKey1, knownUpdateValue);
assertThatExceptionOfType(InvalidDistributionSetException.class)
.as("Invalid distributionSet should throw an exception")
.isThrownBy(() -> distributionSetManagement.updateMetaData(dsId, metadata2));
.isThrownBy(() -> distributionSetManagement.updateMetadata(dsId, knownKey1, knownUpdateValue));
}
@Test
@@ -1265,6 +1215,11 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
.isComplete(Boolean.FALSE).isDeleted(Boolean.FALSE));
}
private void insertMetadata(final String knownKey, final String knownValue, final DistributionSet distributionSet) {
distributionSetManagement.createMetadata(distributionSet.getId(), Map.of(knownKey, knownValue));
assertThat(distributionSetManagement.getMetadata(distributionSet.getId()).get(knownKey)).isEqualTo(knownValue);
}
private void assertThatFilterContainsOnlyGivenDistributionSets(final DistributionSetFilterBuilder filterBuilder,
final List<DistributionSet> distributionSets) {
final int expectedDsSize = distributionSets.size();

View File

@@ -1,234 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.rsql;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import java.util.Arrays;
import java.util.Collections;
import jakarta.persistence.EntityManager;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Story;
import org.eclipse.hawkbit.repository.DistributionSetFields;
import org.eclipse.hawkbit.repository.SoftwareModuleFields;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.orm.jpa.vendor.Database;
@Feature("Component Tests - Repository")
@Story("RSQL filter distribution set")
@SuppressWarnings("java:S6813") // constructor injects are not possible for test classes
class RSQLDistributionSetFieldTest extends AbstractJpaIntegrationTest {
@Autowired
protected VirtualPropertyReplacer virtualPropertyReplacer;
@Autowired
protected EntityManager entityManager;
private DistributionSet ds;
private SoftwareModule sm;
@BeforeEach
void setupBeforeTest() {
sm = testdataFactory.createSoftwareModuleApp("SM");
ds = testdataFactory.createDistributionSet(Collections.singletonList(sm), "DS");
ds = distributionSetManagement.update(entityFactory.distributionSet().update(ds.getId()).description("DS"));
createDistributionSetMetadata(ds.getId(), entityFactory.generateDsMetadata("metaKey", "metaValue"));
DistributionSet ds2 = testdataFactory.createDistributionSets("NewDS", 3).get(0);
ds2 = distributionSetManagement.update(entityFactory.distributionSet().update(ds2.getId()).description("DS%"));
createDistributionSetMetadata(ds2.getId(), entityFactory.generateDsMetadata("metaKey", "value"));
final DistributionSetTag distSetTag = distributionSetTagManagement
.create(entityFactory.tag().create().name("Tag1"));
distributionSetTagManagement.create(entityFactory.tag().create().name("Tag2"));
distributionSetTagManagement.create(entityFactory.tag().create().name("Tag3"));
distributionSetTagManagement.create(entityFactory.tag().create().name("Tag4"));
distributionSetManagement.assignTag(Arrays.asList(ds.getId(), ds2.getId()), distSetTag.getId());
final DistributionSet ds3 = distributionSetManagement
.create(entityFactory.distributionSet().create().name("test123").version("noDescription"));
distributionSetManagement.invalidate(ds3);
}
@Test
@Description("Test filter distribution set by id")
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() + "=in=(" + ds.getId() + ",10000000)", 1);
assertRSQLQuery(DistributionSetFields.ID.name() + "=out=(" + ds.getId() + ",10000000)", 4);
}
@Test
@Description("Test filter distribution set by name")
void testFilterByParameterName() {
assertRSQLQuery(DistributionSetFields.NAME.name() + "==DS", 1);
assertRSQLQuery(DistributionSetFields.NAME.name() + "!=DS", 4);
assertRSQLQuery(DistributionSetFields.NAME.name() + "==*DS", 4);
assertRSQLQuery(DistributionSetFields.NAME.name() + "==noExist*", 0);
assertRSQLQuery(DistributionSetFields.NAME.name() + "=in=(DS,notexist)", 1);
assertRSQLQuery(DistributionSetFields.NAME.name() + "=out=(DS,notexist)", 4);
}
@Test
@Description("Test filter distribution set by assigned software module")
void testFilterBySoftwareModule() {
assertRSQLQuery(DistributionSetFields.MODULE.name() + "." + SoftwareModuleFields.NAME.name() + "==" + sm.getName(), 1);
assertRSQLQuery(DistributionSetFields.MODULE.name() + "." + SoftwareModuleFields.ID.name() + "==" + sm.getId(), 1);
assertRSQLQuery(DistributionSetFields.MODULE.name() + "." + SoftwareModuleFields.NAME.name() + "==noExist", 0);
assertRSQLQuery(DistributionSetFields.MODULE.name() + "." + SoftwareModuleFields.ID.name() + "=in=(" + sm.getId() + ", -1)", 1);
assertRSQLQuery(DistributionSetFields.MODULE.name() + "." + SoftwareModuleFields.ID.name() + "=out=(" + sm.getId() + ", -1)", 4);
}
@Test
@Description("Test filter distribution set by description")
void testFilterByParameterDescription() {
assertRSQLQuery(DistributionSetFields.DESCRIPTION.name() + "==''", 1);
assertRSQLQuery(DistributionSetFields.DESCRIPTION.name() + "!=''", 4);
assertRSQLQuery(DistributionSetFields.DESCRIPTION.name() + "==DS", 1);
assertRSQLQuery(DistributionSetFields.DESCRIPTION.name() + "!=DS*", 3);
assertRSQLQuery(DistributionSetFields.DESCRIPTION.name() + "!=DS", 4);
assertRSQLQuery(DistributionSetFields.DESCRIPTION.name() + "==DS*", 2);
assertRSQLQuery(DistributionSetFields.DESCRIPTION.name() + "==DS%", 1);
assertRSQLQuery(DistributionSetFields.DESCRIPTION.name() + "==noExist*", 0);
assertRSQLQuery(DistributionSetFields.DESCRIPTION.name() + "=in=(DS,notexist)", 1);
assertRSQLQuery(DistributionSetFields.DESCRIPTION.name() + "=out=(DS,notexist)", 4);
}
@Test
@Description("Test filter distribution set by version")
void testFilterByParameterVersion() {
assertRSQLQuery(DistributionSetFields.VERSION.name() + "==" + TestdataFactory.DEFAULT_VERSION, 1);
assertRSQLQuery(DistributionSetFields.VERSION.name() + "!=" + TestdataFactory.DEFAULT_VERSION, 4);
assertRSQLQuery(DistributionSetFields.VERSION.name() + "=in=(" + TestdataFactory.DEFAULT_VERSION + ",1.0.0,1.0.1)", 3);
assertRSQLQuery(DistributionSetFields.VERSION.name() + "=out=(" + TestdataFactory.DEFAULT_VERSION + ",error)", 4);
}
@Test
@Description("Test filter distribution set by complete property")
void testFilterByAttributeComplete() {
assertRSQLQuery(DistributionSetFields.COMPLETE.name() + "==true", 3);
final String noExistStar = DistributionSetFields.COMPLETE.name() + "==noExist*";
assertThatExceptionOfType(RSQLParameterSyntaxException.class)
.isThrownBy(() -> assertRSQLQuery(noExistStar, 0));
assertRSQLQuery(DistributionSetFields.COMPLETE.name() + "=in=(true)", 3);
assertRSQLQuery(DistributionSetFields.COMPLETE.name() + "=out=(true)", 2);
}
@Test
@Description("Test filter distribution set by valid property")
void testFilterByAttributeValid() {
assertRSQLQuery(DistributionSetFields.VALID.name() + "==true", 4);
assertRSQLQuery(DistributionSetFields.VALID.name() + "==false", 1);
final String rsqlNoExistStar = DistributionSetFields.VALID.name() + "==noExist*";
assertThatExceptionOfType(RSQLParameterSyntaxException.class)
.isThrownBy(() -> assertRSQLQuery(rsqlNoExistStar, 0));
assertRSQLQuery(DistributionSetFields.VALID.name() + "=in=(true)", 4);
assertRSQLQuery(DistributionSetFields.VALID.name() + "=out=(true)", 1);
}
@Test
@Description("Test filter distribution set by tag name")
void testFilterByTag() {
assertRSQLQuery(DistributionSetFields.TAG.name() + "==Tag1", 2);
assertRSQLQuery(DistributionSetFields.TAG.name() + "!=Tag1", 3);
assertRSQLQuery(DistributionSetFields.TAG.name() + "==T*", 2);
assertRSQLQuery(DistributionSetFields.TAG.name() + "==noExist*", 0);
assertRSQLQuery(DistributionSetFields.TAG.name() + "=in=(Tag1,notexist)", 2);
assertRSQLQuery(DistributionSetFields.TAG.name() + "=out=(null)", 5);
}
@Test
@Description("Test filter distribution set by type key")
void testFilterByType() {
assertRSQLQuery(DistributionSetFields.TYPE.name() + "==" + TestdataFactory.DS_TYPE_DEFAULT, 4);
assertRSQLQuery(DistributionSetFields.TYPE.name() + "==noExist*", 0);
assertRSQLQuery(DistributionSetFields.TYPE.name() + "=in=(" + TestdataFactory.DS_TYPE_DEFAULT + ",ecl)", 4);
assertRSQLQuery(DistributionSetFields.TYPE.name() + "=out=(" + TestdataFactory.DS_TYPE_DEFAULT + ")", 1);
}
@Test
@Description("Test filter distribution set by metadata")
void testFilterByMetadata() {
createDistributionSetWithMetadata("key.dot", "value.dot");
assertRSQLQuery(DistributionSetFields.METADATA.name() + ".metaKey==metaValue", 1);
assertRSQLQuery(DistributionSetFields.METADATA.name() + ".metaKey==*v*", 2);
assertRSQLQuery(DistributionSetFields.METADATA.name() + ".metaKey==noExist*", 0);
assertRSQLQuery(DistributionSetFields.METADATA.name() + ".metaKey=in=(metaValue,notexist)", 1);
assertRSQLQuery(DistributionSetFields.METADATA.name() + ".metaKey=out=(metaValue,notexist)", 1);
assertRSQLQuery(DistributionSetFields.METADATA.name() + ".notExist==metaValue", 0);
assertRSQLQuery(DistributionSetFields.METADATA.name() + ".key.dot==value.dot", 1);
assertRSQLQuery(DistributionSetFields.METADATA.name() + ".key.dot*==value.dot", 0);
assertRSQLQuery(DistributionSetFields.METADATA.name() + ".key.*==value.dot", 0);
assertRSQLQuery(DistributionSetFields.METADATA.name() + ".key.==value.dot", 0);
assertRSQLQuery(DistributionSetFields.METADATA.name() + ".key*==value.dot", 0);
assertRSQLQuery(DistributionSetFields.METADATA.name() + ".*==value.dot", 0);
assertRSQLQuery(DistributionSetFields.METADATA.name() + "..==value.dot", 0);
assertRSQLQueryThrowsException(
DistributionSetFields.METADATA.name() + ".==value.dot",
RSQLParameterUnsupportedFieldException.class);
assertRSQLQueryThrowsException(
DistributionSetFields.METADATA.name() + "*==value.dot",
RSQLParameterUnsupportedFieldException.class);
assertRSQLQueryThrowsException(
DistributionSetFields.METADATA.name() + "==value.dot",
RSQLParameterUnsupportedFieldException.class);
}
private void assertRSQLQuery(final String rsqlParam, final long expectedEntity) {
final Page<DistributionSet> find = distributionSetManagement.findByRsql(rsqlParam, PageRequest.of(0, 100));
final long countAll = find.getTotalElements();
assertThat(find).as("Found entity is should not be null").isNotNull();
assertThat(countAll).as("Found entity size is wrong").isEqualTo(expectedEntity);
}
private <T extends Throwable> void assertRSQLQueryThrowsException(final String rsqlParam, final Class<T> expectedException) {
assertThatExceptionOfType(expectedException)
.isThrownBy(() -> RSQLUtility.validateRsqlFor(
rsqlParam, DistributionSetFields.class, JpaDistributionSet.class, virtualPropertyReplacer, entityManager));
}
private DistributionSet createDistributionSetWithMetadata(final String metadataKeyName, final String metadataValue) {
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
createDistributionSetMetadata(distributionSet.getId(), entityFactory.generateDsMetadata(metadataKeyName, metadataValue));
return distributionSet;
}
}

View File

@@ -1,81 +0,0 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.rsql;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Story;
import org.eclipse.hawkbit.repository.DistributionSetMetadataFields;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
import org.eclipse.hawkbit.repository.model.MetaData;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
@Feature("Component Tests - Repository")
@Story("RSQL filter distribution set metadata")
class RSQLDistributionSetMetadataFieldsTest extends AbstractJpaIntegrationTest {
private Long distributionSetId;
@BeforeEach
void setupBeforeTest() {
final DistributionSet distributionSet = testdataFactory.createDistributionSet("DS");
distributionSetId = distributionSet.getId();
final List<MetaData> metadata = new ArrayList<>(5);
for (int i = 0; i < 5; i++) {
metadata.add(entityFactory.generateDsMetadata("" + i, "" + i));
}
distributionSetManagement.putMetaData(distributionSetId, metadata);
distributionSetManagement.putMetaData(distributionSetId,
Arrays.asList(entityFactory.generateDsMetadata("emptyValueTest", null)));
}
@Test
@Description("Test filter distribution set metadata by key")
void testFilterByParameterKey() {
assertRSQLQuery(DistributionSetMetadataFields.KEY.name() + "==1", 1);
assertRSQLQuery(DistributionSetMetadataFields.KEY.name() + "!=1", 5);
assertRSQLQuery(DistributionSetMetadataFields.KEY.name() + "=in=(1,2)", 2);
assertRSQLQuery(DistributionSetMetadataFields.KEY.name() + "=out=(1,2)", 4);
}
@Test
@Description("Test filter distribution set metadata by value")
void testFilterByParameterValue() {
assertRSQLQuery(DistributionSetMetadataFields.VALUE.name() + "==''", 1);
assertRSQLQuery(DistributionSetMetadataFields.VALUE.name() + "!=''", 5);
assertRSQLQuery(DistributionSetMetadataFields.VALUE.name() + "==1", 1);
assertRSQLQuery(DistributionSetMetadataFields.VALUE.name() + "!=1", 5);
assertRSQLQuery(DistributionSetMetadataFields.VALUE.name() + "=in=(1,2)", 2);
assertRSQLQuery(DistributionSetMetadataFields.VALUE.name() + "=out=(1,2)", 4);
}
private void assertRSQLQuery(final String rsqlParam, final long expectedEntities) {
final Page<DistributionSetMetadata> findEnitity = distributionSetManagement
.findMetaDataByDistributionSetIdAndRsql(distributionSetId, rsqlParam, PageRequest.of(0, 100));
final long countAllEntities = findEnitity.getTotalElements();
assertThat(findEnitity).isNotNull();
assertThat(countAllEntities).isEqualTo(expectedEntities);
}
}