Unify target attributes and metadata (#2408)
* Unify target attributes and metadata Currently, the target attributes are Map while the metadata, which has the same concept is List. This PR unifies them making the metadata also a Map Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -27,7 +27,6 @@ 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.jpa.model.JpaTargetMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.MetaData;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
@@ -77,11 +76,6 @@ public class JpaEntityFactory implements EntityFactory {
|
||||
return new JpaDistributionSetMetadata(key, value == null ? null : value.strip());
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetaData generateTargetMetadata(final String key, final String value) {
|
||||
return new JpaTargetMetadata(key, value == null ? null : value.strip());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SoftwareModuleMetadataBuilder softwareModuleMetadata() {
|
||||
return softwareModuleMetadataBuilder;
|
||||
|
||||
@@ -132,7 +132,6 @@ import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleMetadataRepos
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleTypeRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetFilterQueryRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetMetadataRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetTagRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetTypeRepository;
|
||||
@@ -622,7 +621,7 @@ public class RepositoryApplicationConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
TargetManagement targetManagement(final EntityManager entityManager, final QuotaManagement quotaManagement,
|
||||
final TargetRepository targetRepository, final TargetMetadataRepository targetMetadataRepository,
|
||||
final TargetRepository targetRepository,
|
||||
final RolloutGroupRepository rolloutGroupRepository,
|
||||
final TargetFilterQueryRepository targetFilterQueryRepository,
|
||||
final TargetTypeRepository targetTypeRepository, final TargetTagRepository targetTagRepository,
|
||||
@@ -630,7 +629,7 @@ public class RepositoryApplicationConfiguration {
|
||||
final VirtualPropertyReplacer virtualPropertyReplacer,
|
||||
final JpaProperties properties, final DistributionSetManagement distributionSetManagement) {
|
||||
return new JpaTargetManagement(entityManager, distributionSetManagement, quotaManagement, targetRepository,
|
||||
targetTypeRepository, targetMetadataRepository, rolloutGroupRepository, targetFilterQueryRepository,
|
||||
targetTypeRepository, rolloutGroupRepository, targetFilterQueryRepository,
|
||||
targetTagRepository, eventPublisherHolder, tenantAware, virtualPropertyReplacer,
|
||||
properties.getDatabase());
|
||||
}
|
||||
|
||||
@@ -37,12 +37,7 @@ public class JpaTargetCreate extends AbstractTargetUpdateCreate<TargetCreate> im
|
||||
|
||||
@Override
|
||||
public JpaTarget build() {
|
||||
final JpaTarget target;
|
||||
if (ObjectUtils.isEmpty(securityToken)) {
|
||||
target = new JpaTarget(controllerId);
|
||||
} else {
|
||||
target = new JpaTarget(controllerId, securityToken);
|
||||
}
|
||||
final JpaTarget target = new JpaTarget(controllerId, securityToken);
|
||||
|
||||
if (!ObjectUtils.isEmpty(name)) {
|
||||
target.setName(name);
|
||||
|
||||
@@ -31,6 +31,7 @@ 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.NotEmpty;
|
||||
|
||||
import org.apache.commons.collections4.ListUtils;
|
||||
@@ -39,12 +40,10 @@ import org.eclipse.hawkbit.repository.FilterParams;
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetFields;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetMetadataFields;
|
||||
import org.eclipse.hawkbit.repository.TimestampCalculator;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetCreate;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetUpdate;
|
||||
import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
|
||||
@@ -52,18 +51,13 @@ import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetCreate;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetUpdate;
|
||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetMetadata;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetMetadata_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.TargetMetadataCompositeKey;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetFilterQueryRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetMetadataRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetTagRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetTypeRepository;
|
||||
@@ -73,11 +67,9 @@ import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
|
||||
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.MetaData;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
import org.eclipse.hawkbit.repository.model.TargetMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTypeAssignmentResult;
|
||||
@@ -109,7 +101,6 @@ public class JpaTargetManagement implements TargetManagement {
|
||||
private final QuotaManagement quotaManagement;
|
||||
private final TargetRepository targetRepository;
|
||||
private final TargetTypeRepository targetTypeRepository;
|
||||
private final TargetMetadataRepository targetMetadataRepository;
|
||||
private final RolloutGroupRepository rolloutGroupRepository;
|
||||
private final TargetFilterQueryRepository targetFilterQueryRepository;
|
||||
private final TargetTagRepository targetTagRepository;
|
||||
@@ -122,7 +113,6 @@ public class JpaTargetManagement implements TargetManagement {
|
||||
public JpaTargetManagement(final EntityManager entityManager,
|
||||
final DistributionSetManagement distributionSetManagement, final QuotaManagement quotaManagement,
|
||||
final TargetRepository targetRepository, final TargetTypeRepository targetTypeRepository,
|
||||
final TargetMetadataRepository targetMetadataRepository,
|
||||
final RolloutGroupRepository rolloutGroupRepository,
|
||||
final TargetFilterQueryRepository targetFilterQueryRepository,
|
||||
final TargetTagRepository targetTagRepository, final EventPublisherHolder eventPublisherHolder,
|
||||
@@ -133,7 +123,6 @@ public class JpaTargetManagement implements TargetManagement {
|
||||
this.quotaManagement = quotaManagement;
|
||||
this.targetRepository = targetRepository;
|
||||
this.targetTypeRepository = targetTypeRepository;
|
||||
this.targetMetadataRepository = targetMetadataRepository;
|
||||
this.rolloutGroupRepository = rolloutGroupRepository;
|
||||
this.targetFilterQueryRepository = targetFilterQueryRepository;
|
||||
this.targetTagRepository = targetTagRepository;
|
||||
@@ -597,7 +586,7 @@ public class JpaTargetManagement implements TargetManagement {
|
||||
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
|
||||
|
||||
targetRepository.getAccessController().ifPresent(acm ->
|
||||
acm.assertOperationAllowed(AccessController.Operation.UPDATE, target));
|
||||
acm.assertOperationAllowed(AccessController.Operation.UPDATE, target));
|
||||
|
||||
final JpaTargetType targetType = getTargetTypeByIdAndThrowIfNotFound(targetTypeId);
|
||||
target.setTargetType(targetType);
|
||||
@@ -635,57 +624,6 @@ public class JpaTargetManagement implements TargetManagement {
|
||||
return Collections.unmodifiableList(targetRepository.findAll(TargetSpecifications.hasIdIn(ids)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getControllerAttributes(final String controllerId) {
|
||||
getByControllerIdAndThrowIfNotFound(controllerId);
|
||||
|
||||
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
final CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
|
||||
|
||||
final Root<JpaTarget> targetRoot = query.from(JpaTarget.class);
|
||||
query.where(cb.equal(targetRoot.get(JpaTarget_.controllerId), controllerId));
|
||||
|
||||
final MapJoin<JpaTarget, String, String> attributes = targetRoot.join(JpaTarget_.controllerAttributes);
|
||||
query.multiselect(attributes.key(), attributes.value());
|
||||
query.orderBy(cb.asc(attributes.key()));
|
||||
|
||||
final List<Object[]> attr = entityManager.createQuery(query).getResultList();
|
||||
|
||||
return attr.stream().collect(Collectors.toMap(entry -> (String) entry[0], entry -> (String) entry[1],
|
||||
(v1, v2) -> v1, LinkedHashMap::new));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void requestControllerAttributes(final String controllerId) {
|
||||
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
|
||||
targetRepository.getAccessController()
|
||||
.ifPresent(acm -> acm.assertOperationAllowed(AccessController.Operation.UPDATE, target));
|
||||
target.setRequestControllerAttributes(true);
|
||||
AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit().afterCommit(() ->
|
||||
eventPublisherHolder.getEventPublisher()
|
||||
.publishEvent(new TargetAttributesRequestedEvent(
|
||||
tenantAware.getCurrentTenant(), target.getId(), target.getControllerId(),
|
||||
target.getAddress() != null ? target.getAddress().toString() : null,
|
||||
JpaTarget.class, eventPublisherHolder.getApplicationId())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isControllerAttributesRequested(final String controllerId) {
|
||||
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
|
||||
|
||||
return target.isRequestControllerAttributes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Target> findByControllerAttributesRequested(final Pageable pageReq) {
|
||||
return JpaManagementHelper.findAllWithCountBySpec(targetRepository, List.of(TargetSpecifications.hasRequestControllerAttributesTrue()),
|
||||
pageReq
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean existsByControllerId(final String controllerId) {
|
||||
return targetRepository.exists(TargetSpecifications.hasControllerId(controllerId));
|
||||
@@ -710,115 +648,120 @@ public class JpaTargetManagement implements TargetManagement {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<TargetTag> getTagsByControllerId(@NotEmpty String controllerId) {
|
||||
public Set<TargetTag> getTags(@NotEmpty String controllerId) {
|
||||
// the method has PreAuthorized by itself
|
||||
return ((JpaTarget)getWithTags(controllerId)).getTags();
|
||||
return ((JpaTarget) getWithTags(controllerId)).getTags();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getControllerAttributes(final String controllerId) {
|
||||
return getMap(controllerId, JpaTarget_.controllerAttributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public List<TargetMetadata> createMetaData(final String controllerId, final Collection<MetaData> md) {
|
||||
public void requestControllerAttributes(final String controllerId) {
|
||||
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
|
||||
|
||||
md.forEach(meta -> checkAndThrowIfTargetMetadataAlreadyExists(
|
||||
new TargetMetadataCompositeKey(target.getId(), meta.getKey())));
|
||||
|
||||
assertMetaDataQuota(target.getId(), md.size());
|
||||
|
||||
final JpaTarget updatedTarget = JpaManagementHelper.touch(entityManager, targetRepository, target);
|
||||
|
||||
final List<TargetMetadata> createdMetadata = md.stream()
|
||||
.map(meta -> targetMetadataRepository.save(new JpaTargetMetadata(meta.getKey(), meta.getValue(), updatedTarget)))
|
||||
.collect(Collectors.toUnmodifiableList());
|
||||
|
||||
// TargetUpdatedEvent is not sent within the touch() method due to the
|
||||
// "lastModifiedAt" field being ignored in JpaTarget
|
||||
eventPublisherHolder.getEventPublisher().publishEvent(new TargetUpdatedEvent(updatedTarget, eventPublisherHolder.getApplicationId()));
|
||||
|
||||
return createdMetadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void deleteMetaData(final String controllerId, final String key) {
|
||||
final JpaTargetMetadata metadata = (JpaTargetMetadata) getMetaDataByControllerId(controllerId, key)
|
||||
.orElseThrow(() -> new EntityNotFoundException(TargetMetadata.class, controllerId, key));
|
||||
|
||||
final JpaTarget target = JpaManagementHelper.touch(
|
||||
entityManager, targetRepository, getByControllerIdAndThrowIfNotFound(controllerId));
|
||||
|
||||
targetRepository.getAccessController()
|
||||
.ifPresent(acm -> acm.assertOperationAllowed(AccessController.Operation.UPDATE, target));
|
||||
|
||||
targetMetadataRepository.deleteById(metadata.getId());
|
||||
// target update event is set to ignore "lastModifiedAt" field, so it is
|
||||
// not send automatically within the touch() method
|
||||
eventPublisherHolder.getEventPublisher()
|
||||
.publishEvent(new TargetUpdatedEvent(target, eventPublisherHolder.getApplicationId()));
|
||||
target.setRequestControllerAttributes(true);
|
||||
AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit().afterCommit(() ->
|
||||
eventPublisherHolder.getEventPublisher()
|
||||
.publishEvent(new TargetAttributesRequestedEvent(
|
||||
tenantAware.getCurrentTenant(), target.getId(), target.getControllerId(),
|
||||
target.getAddress() != null ? target.getAddress().toString() : null,
|
||||
JpaTarget.class, eventPublisherHolder.getApplicationId())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<TargetMetadata> findMetaDataByControllerId(final Pageable pageable, final String controllerId) {
|
||||
final Long id = getByControllerIdAndThrowIfNotFound(controllerId).getId();
|
||||
|
||||
return JpaManagementHelper.findAllWithCountBySpec(targetMetadataRepository, Collections.singletonList(metadataByTargetIdSpec(id)),
|
||||
pageable
|
||||
);
|
||||
public boolean isControllerAttributesRequested(final String controllerId) {
|
||||
return getByControllerIdAndThrowIfNotFound(controllerId).isRequestControllerAttributes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countMetaDataByControllerId(@NotEmpty final String controllerId) {
|
||||
final Long targetId = getByControllerIdAndThrowIfNotFound(controllerId).getId();
|
||||
|
||||
return JpaManagementHelper.countBySpec(targetMetadataRepository,
|
||||
Collections.singletonList(metadataByTargetIdSpec(targetId)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<TargetMetadata> findMetaDataByControllerIdAndRsql(final Pageable pageable, final String controllerId,
|
||||
final String rsqlParam) {
|
||||
final Long targetId = getByControllerIdAndThrowIfNotFound(controllerId).getId();
|
||||
|
||||
final List<Specification<JpaTargetMetadata>> specList = Arrays.asList(RSQLUtility
|
||||
.buildRsqlSpecification(rsqlParam, TargetMetadataFields.class, virtualPropertyReplacer, database),
|
||||
metadataByTargetIdSpec(targetId));
|
||||
|
||||
return JpaManagementHelper.findAllWithCountBySpec(targetMetadataRepository, specList, pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<TargetMetadata> getMetaDataByControllerId(final String controllerId, final String key) {
|
||||
final Long targetId = getByControllerIdAndThrowIfNotFound(controllerId).getId();
|
||||
|
||||
return targetMetadataRepository.findById(new TargetMetadataCompositeKey(targetId, key)).map(t -> t);
|
||||
public Page<Target> findByControllerAttributesRequested(final Pageable pageReq) {
|
||||
return JpaManagementHelper.findAllWithCountBySpec(
|
||||
targetRepository, List.of(TargetSpecifications.hasRequestControllerAttributesTrue()), pageReq);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public TargetMetadata updateMetadata(final String controllerId, final MetaData md) {
|
||||
public void createMetadata(final String controllerId, final Map<String, String> md) {
|
||||
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
|
||||
|
||||
// check if exists otherwise throw entity not found exception
|
||||
final JpaTargetMetadata updatedMetadata = (JpaTargetMetadata) getMetaDataByControllerId(controllerId,
|
||||
md.getKey())
|
||||
.orElseThrow(() -> new EntityNotFoundException(TargetMetadata.class, controllerId, md.getKey()));
|
||||
updatedMetadata.setValue(md.getValue());
|
||||
// touch it to update the lock revision because we are modifying the
|
||||
// target indirectly
|
||||
final JpaTarget target = JpaManagementHelper.touch(entityManager, targetRepository,
|
||||
getByControllerIdAndThrowIfNotFound(controllerId));
|
||||
// get the modifiable metadata map
|
||||
final Map<String, String> metadata = target.getMetadata();
|
||||
md.keySet().forEach(key -> {
|
||||
if (metadata.containsKey(key)) {
|
||||
throw new EntityAlreadyExistsException("Metadata entry with key '" + key + "' already exists");
|
||||
}
|
||||
});
|
||||
metadata.putAll(md);
|
||||
|
||||
final JpaTargetMetadata metadata = targetMetadataRepository.save(updatedMetadata);
|
||||
// target update event is set to ignore "lastModifiedAt" field, so it is
|
||||
// not send automatically within the touch() method
|
||||
eventPublisherHolder.getEventPublisher()
|
||||
.publishEvent(new TargetUpdatedEvent(target, eventPublisherHolder.getApplicationId()));
|
||||
return metadata;
|
||||
assertMetadataQuota(target.getId(), metadata.size());
|
||||
targetRepository.save(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getMetadata(final String controllerId) {
|
||||
return getMap(controllerId, JpaTarget_.metadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void updateMetadata(final String controllerId, final String key, final String value) {
|
||||
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
|
||||
|
||||
// get the modifiable metadata map
|
||||
final Map<String, String> metadata = target.getMetadata();
|
||||
if (!metadata.containsKey(key)) {
|
||||
throw new EntityNotFoundException("Target metadata", controllerId + ":" + key);
|
||||
}
|
||||
metadata.put(key, value);
|
||||
|
||||
targetRepository.save(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void deleteMetadata(final String controllerId, final String key) {
|
||||
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
|
||||
|
||||
// get the modifiable metadata map
|
||||
final Map<String, String> metadata = target.getMetadata();
|
||||
if (metadata.remove(key) == null) {
|
||||
throw new EntityNotFoundException("Target metadata", controllerId + ":" + key);
|
||||
}
|
||||
|
||||
targetRepository.save(target);
|
||||
}
|
||||
|
||||
private Map<String, String> getMap(final String controllerId, final MapAttribute<JpaTarget, String, String> mapAttribute) {
|
||||
getByControllerIdAndThrowIfNotFound(controllerId);
|
||||
|
||||
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
final CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
|
||||
|
||||
final Root<JpaTarget> targetRoot = query.from(JpaTarget.class);
|
||||
query.where(cb.equal(targetRoot.get(JpaTarget_.controllerId), controllerId));
|
||||
|
||||
final MapJoin<JpaTarget, 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 static boolean hasTagsFilterActive(final FilterParams filterParams) {
|
||||
@@ -851,20 +794,9 @@ public class JpaTargetManagement implements TargetManagement {
|
||||
return targetTypeRepository.findById(id).orElseThrow(() -> new EntityNotFoundException(TargetType.class, id));
|
||||
}
|
||||
|
||||
private void checkAndThrowIfTargetMetadataAlreadyExists(final TargetMetadataCompositeKey metadataId) {
|
||||
if (targetMetadataRepository.existsById(metadataId)) {
|
||||
throw new EntityAlreadyExistsException(
|
||||
"Metadata entry with key '" + metadataId.getKey() + "' already exists");
|
||||
}
|
||||
}
|
||||
|
||||
private void assertMetaDataQuota(final Long targetId, final int requested) {
|
||||
QuotaHelper.assertAssignmentQuota(targetId, requested, quotaManagement.getMaxMetaDataEntriesPerTarget(),
|
||||
TargetMetadata.class, Target.class, targetMetadataRepository::countByTargetId);
|
||||
}
|
||||
|
||||
private Specification<JpaTargetMetadata> metadataByTargetIdSpec(final Long targetId) {
|
||||
return (root, query, cb) -> cb.equal(root.get(JpaTargetMetadata_.target).get(AbstractJpaBaseEntity_.id), targetId);
|
||||
private void assertMetadataQuota(final Long targetId, final int requested) {
|
||||
final int limit = quotaManagement.getMaxMetaDataEntriesPerTarget();
|
||||
QuotaHelper.assertAssignmentQuota(targetId, requested, limit, "Metadata", Target.class.getSimpleName(), null);
|
||||
}
|
||||
|
||||
private List<Specification<JpaTarget>> buildSpecificationList(final FilterParams filterParams) {
|
||||
|
||||
@@ -59,7 +59,6 @@ import org.eclipse.hawkbit.repository.jpa.utils.MapAttributeConverter;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.PollStatus;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
@@ -181,7 +180,7 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
||||
})
|
||||
private Set<TargetTag> tags;
|
||||
|
||||
// no cascade option on an ElementCollection, the target objects are always persisted, merged, removed with their parent.
|
||||
// no cascade option on an ElementCollection, the target objects are always persisted, merged, removed with their parent
|
||||
@Getter
|
||||
@ElementCollection
|
||||
@CollectionTable(
|
||||
@@ -192,8 +191,16 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
||||
@Column(name = "attribute_value", length = Target.CONTROLLER_ATTRIBUTE_VALUE_SIZE)
|
||||
private Map<String, String> controllerAttributes;
|
||||
|
||||
@OneToMany(mappedBy = "target", fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE }, targetEntity = JpaTargetMetadata.class)
|
||||
private List<TargetMetadata> metadata;
|
||||
// no cascade option on an ElementCollection, the target objects are always persisted, merged, removed with their parent
|
||||
@Getter
|
||||
@ElementCollection
|
||||
@CollectionTable(
|
||||
name = "sp_target_metadata",
|
||||
joinColumns = { @JoinColumn(name = "target", nullable = false) },
|
||||
foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_metadata_target"))
|
||||
@MapKeyColumn(name = "meta_key", length = Target.CONTROLLER_METADATA_KEY_SIZE)
|
||||
@Column(name = "meta_value", length = Target.CONTROLLER_METADATA_VALUE_SIZE)
|
||||
private Map<String, String> metadata;
|
||||
|
||||
@OneToMany(mappedBy = "target", fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE }, targetEntity = JpaAction.class)
|
||||
private List<JpaAction> actions;
|
||||
@@ -201,15 +208,11 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
||||
@OneToMany(mappedBy = "target", fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.REMOVE })
|
||||
private List<RolloutTargetGroup> rolloutTargetGroup;
|
||||
|
||||
public JpaTarget(final String controllerId) {
|
||||
this(controllerId, SecurityTokenGeneratorHolder.getInstance().generateToken());
|
||||
}
|
||||
|
||||
public JpaTarget(final String controllerId, final String securityToken) {
|
||||
this.controllerId = controllerId;
|
||||
// truncate controller ID to max name length (if needed)
|
||||
// truncate controller ID to max name length (if too big)
|
||||
setName(controllerId != null && controllerId.length() > NAME_MAX_SIZE ? controllerId.substring(0, NAME_MAX_SIZE) : controllerId);
|
||||
this.securityToken = securityToken;
|
||||
this.securityToken = ObjectUtils.isEmpty(securityToken) ? SecurityTokenGeneratorHolder.getInstance().generateToken() : securityToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -287,10 +290,6 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
||||
return tags == null ? Collections.emptySet() : Collections.unmodifiableSet(tags);
|
||||
}
|
||||
|
||||
public List<TargetMetadata> getMetadata() {
|
||||
return metadata == null ? Collections.emptyList() : Collections.unmodifiableList(metadata);
|
||||
}
|
||||
|
||||
public void addAction(final Action action) {
|
||||
if (actions == null) {
|
||||
actions = new ArrayList<>();
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2018 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 lombok.Setter;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetMetadata;
|
||||
|
||||
/**
|
||||
* Meta data for {@link Target}.
|
||||
*/
|
||||
@NoArgsConstructor // Default constructor for JPA
|
||||
@Setter
|
||||
@Getter
|
||||
@IdClass(TargetMetadataCompositeKey.class)
|
||||
@Entity
|
||||
@Table(name = "sp_target_metadata")
|
||||
public class JpaTargetMetadata extends AbstractJpaMetaData implements TargetMetadata {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||
@JoinColumn(
|
||||
name = "target", nullable = false, updatable = false,
|
||||
foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_metadata_target"))
|
||||
private JpaTarget target;
|
||||
|
||||
/**
|
||||
* Creates a single metadata entry with the given key and value.
|
||||
*
|
||||
* @param key of the meta-data entry
|
||||
* @param value of the meta-data entry
|
||||
*/
|
||||
public JpaTargetMetadata(final String key, final String value) {
|
||||
super(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a single metadata entry with the given key and value for the
|
||||
* given {@link Target}.
|
||||
*
|
||||
* @param key of the meta-data entry
|
||||
* @param value of the meta-data entry
|
||||
* @param target the meta-data entry is associated with
|
||||
*/
|
||||
public JpaTargetMetadata(final String key, final String value, final Target target) {
|
||||
super(key, value);
|
||||
this.target = (JpaTarget) target;
|
||||
}
|
||||
|
||||
public TargetMetadataCompositeKey getId() {
|
||||
return new TargetMetadataCompositeKey(target.getId(), getKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + ((target == null) ? 0 : target.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 JpaTargetMetadata other = (JpaTargetMetadata) obj;
|
||||
if (target == null) {
|
||||
return other.target == null;
|
||||
} else {
|
||||
return target.equals(other.target);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2018 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.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* The Target Metadata composite key which contains the meta-data key and the ID of the Target itself.
|
||||
*/
|
||||
@NoArgsConstructor // Default constructor for JPA
|
||||
@Data
|
||||
public final class TargetMetadataCompositeKey implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String key;
|
||||
private Long target;
|
||||
|
||||
/**
|
||||
* @param target the target Id for this meta-data
|
||||
* @param key the key of the meta-data
|
||||
*/
|
||||
public TargetMetadataCompositeKey(final Long target, final String key) {
|
||||
this.target = target;
|
||||
this.key = key;
|
||||
}
|
||||
}
|
||||
@@ -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.JpaTargetMetadata;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.TargetMetadataCompositeKey;
|
||||
import org.eclipse.hawkbit.repository.model.TargetMetadata;
|
||||
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 TargetMetadata} repository.
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public interface TargetMetadataRepository
|
||||
extends PagingAndSortingRepository<JpaTargetMetadata, TargetMetadataCompositeKey>,
|
||||
CrudRepository<JpaTargetMetadata, TargetMetadataCompositeKey>,
|
||||
JpaSpecificationExecutor<JpaTargetMetadata> {
|
||||
|
||||
/**
|
||||
* Counts the meta data entries that match the given target ID.
|
||||
* <p/>
|
||||
* No access control applied
|
||||
*
|
||||
* @param id of the target.
|
||||
* @return The number of matching meta data entries.
|
||||
*/
|
||||
long countByTargetId(@Param("id") Long id);
|
||||
}
|
||||
@@ -13,35 +13,31 @@ import java.util.function.ToLongFunction;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||
|
||||
/**
|
||||
* Helper class to check quotas.
|
||||
*/
|
||||
@NoArgsConstructor
|
||||
@Slf4j
|
||||
public final class QuotaHelper {
|
||||
|
||||
private static final String MAX_ASSIGNMENT_QUOTA_EXCEEDED = "Quota exceeded: Cannot assign %s entities at once. The maximum is %s.";
|
||||
|
||||
private QuotaHelper() {
|
||||
// no need to instantiate this class
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts the specified assignment quota.
|
||||
*
|
||||
* @param requested The number of entities that shall be assigned to the parent
|
||||
* entity.
|
||||
* @param limit The maximum number of entities that may be assigned to the
|
||||
* parent entity.
|
||||
* @param requested The number of entities that shall be assigned to the parent entity.
|
||||
* @param limit The maximum number of entities that may be assigned to the parent entity.
|
||||
* @param type The type of the entities that shall be assigned.
|
||||
* @param parentType The type of the parent entity.
|
||||
* @throws AssignmentQuotaExceededException if the assignment operation would cause the quota to be
|
||||
* exceeded
|
||||
* @throws AssignmentQuotaExceededException if the assignment operation would cause the quota to be exceeded
|
||||
*/
|
||||
public static void assertAssignmentQuota(final long requested, final long limit, @NotNull final Class<?> type,
|
||||
@NotNull final Class<?> parentType) {
|
||||
public static void assertAssignmentQuota(
|
||||
final long requested, final long limit,
|
||||
@NotNull final Class<?> type, @NotNull final Class<?> parentType) {
|
||||
assertAssignmentQuota(null, requested, limit, type.getSimpleName(), parentType.getSimpleName(), null);
|
||||
}
|
||||
|
||||
@@ -49,19 +45,18 @@ public final class QuotaHelper {
|
||||
* Asserts the specified assignment quota.
|
||||
*
|
||||
* @param parentId The ID of the parent entity.
|
||||
* @param requested The number of entities that shall be assigned to the parent
|
||||
* entity.
|
||||
* @param limit The maximum number of entities that may be assigned to the
|
||||
* parent entity.
|
||||
* @param requested The number of entities that shall be assigned to the parent entity.
|
||||
* @param limit The maximum number of entities that may be assigned to the parent entity.
|
||||
* @param type The type of the entities that shall be assigned.
|
||||
* @param parentType The type of the parent entity.
|
||||
* @param countFct Function to count the entities that are currently assigned to
|
||||
* the parent entity.
|
||||
* @throws AssignmentQuotaExceededException if the assignment operation would cause the quota to be
|
||||
* exceeded
|
||||
* @param countFct Function to count the entities that are currently assigned to the parent entity.
|
||||
* @throws AssignmentQuotaExceededException if the assignment operation would cause the quota to be exceeded
|
||||
*/
|
||||
public static <T> void assertAssignmentQuota(final T parentId, final long requested, final long limit,
|
||||
@NotNull final Class<?> type, @NotNull final Class<?> parentType, final ToLongFunction<T> countFct) {
|
||||
public static <T> void assertAssignmentQuota(
|
||||
final T parentId,
|
||||
final long requested, final long limit,
|
||||
@NotNull final Class<?> type, @NotNull final Class<?> parentType,
|
||||
final ToLongFunction<T> countFct) {
|
||||
assertAssignmentQuota(parentId, requested, limit, type.getSimpleName(), parentType.getSimpleName(), countFct);
|
||||
}
|
||||
|
||||
@@ -69,20 +64,18 @@ public final class QuotaHelper {
|
||||
* Asserts the specified assignment quota.
|
||||
*
|
||||
* @param parentId The ID of the parent entity.
|
||||
* @param requested The number of entities that shall be assigned to the parent
|
||||
* entity.
|
||||
* @param limit The maximum number of entities that may be assigned to the
|
||||
* parent entity.
|
||||
* @param requested The number of entities that shall be assigned to the parent entity.
|
||||
* @param limit The maximum number of entities that may be assigned to the parent entity.
|
||||
* @param type The type of the entities that shall be assigned.
|
||||
* @param parentType The type of the parent entity.
|
||||
* @param countFct Function to count the entities that are currently assigned to
|
||||
* the parent entity.
|
||||
* @throws AssignmentQuotaExceededException if the assignment operation would cause the quota to be
|
||||
* exceeded
|
||||
* @param countFct Function to count the entities that are currently assigned to the parent entity.
|
||||
* @throws AssignmentQuotaExceededException if the assignment operation would cause the quota to be exceeded
|
||||
*/
|
||||
public static <T> void assertAssignmentQuota(final T parentId, final long requested, final long limit,
|
||||
@NotNull final String type, @NotNull final String parentType, final ToLongFunction<T> countFct) {
|
||||
|
||||
public static <T> void assertAssignmentQuota(
|
||||
final T parentId,
|
||||
final long requested, final long limit,
|
||||
@NotNull final String type, @NotNull final String parentType,
|
||||
final ToLongFunction<T> countFct) {
|
||||
// check if the quota is unlimited
|
||||
if (limit <= 0) {
|
||||
log.debug("Quota 'Max {} entities per {}' is unlimited.", type, parentType);
|
||||
@@ -91,8 +84,9 @@ public final class QuotaHelper {
|
||||
|
||||
if (requested > limit) {
|
||||
final String parentIdStr = parentId != null ? String.valueOf(parentId) : "<new>";
|
||||
log.warn("Cannot assign {} {} entities to {} '{}' because of the configured quota limit {}.", requested,
|
||||
type, parentType, parentIdStr, limit);
|
||||
log.warn(
|
||||
"Cannot assign {} {} entities to {} '{}' because of the configured quota limit {}.",
|
||||
requested, type, parentType, parentIdStr, limit);
|
||||
throw new AssignmentQuotaExceededException(type, parentType, parentId, requested, limit);
|
||||
}
|
||||
|
||||
@@ -100,7 +94,8 @@ public final class QuotaHelper {
|
||||
final long currentCount = countFct.applyAsLong(parentId);
|
||||
if (currentCount + requested > limit) {
|
||||
log.warn(
|
||||
"Cannot assign {} {} entities to {} '{}' because of the configured quota limit {}. Currently, there are {} {} entities assigned.",
|
||||
"Cannot assign {} {} entities to {} '{}' because of the configured quota limit {}. " +
|
||||
"Currently, there are {} {} entities assigned.",
|
||||
requested, type, parentType, parentId, limit, currentCount, type);
|
||||
throw new AssignmentQuotaExceededException(type, parentType, parentId, requested, limit);
|
||||
}
|
||||
@@ -108,8 +103,7 @@ public final class QuotaHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that the number of assignments in a request does not exceed the
|
||||
* limit.
|
||||
* Assert that the number of assignments in a request does not exceed the limit.
|
||||
*
|
||||
* @param requested the number of assignments that are to be made
|
||||
* @param limit the maximum number of assignments per request
|
||||
@@ -121,4 +115,4 @@ public final class QuotaHelper {
|
||||
throw new AssignmentQuotaExceededException(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -303,7 +303,7 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
|
||||
}
|
||||
|
||||
protected Set<TargetTag> getTargetTags(final String controllerId) {
|
||||
return targetManagement.getTagsByControllerId(controllerId);
|
||||
return targetManagement.getTags(controllerId);
|
||||
}
|
||||
|
||||
protected JpaRolloutGroup refresh(final RolloutGroup group) {
|
||||
|
||||
@@ -68,7 +68,7 @@ class DistributionSetManagementSecurityTest
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
void createMetaDataPermissionsCheck() {
|
||||
assertPermissions(
|
||||
() -> distributionSetManagement.putMetaData(1L, List.of(entityFactory.generateTargetMetadata("key", "value"))),
|
||||
() -> distributionSetManagement.putMetaData(1L, List.of(entityFactory.generateDsMetadata("key", "value"))),
|
||||
List.of(SpPermission.UPDATE_REPOSITORY));
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -339,6 +340,26 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
|
||||
assertPermissions(() -> targetManagement.get(List.of(1L)), List.of(SpPermission.READ_TARGET));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
void existsByControllerIdPermissionsCheck() {
|
||||
assertPermissions(() -> targetManagement.existsByControllerId("controllerId"), List.of(SpPermission.READ_TARGET));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
void isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatablePermissionsCheck() {
|
||||
assertPermissions(
|
||||
() -> targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable("controllerId", 1L, "controllerId==id"),
|
||||
List.of(SpPermission.READ_TARGET, SpPermission.READ_REPOSITORY));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
void getTagsPermissionsCheck() {
|
||||
assertPermissions(() -> targetManagement.getTags("controllerId"), List.of(SpPermission.READ_TARGET));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
void getControllerAttributesPermissionsCheck() {
|
||||
@@ -368,72 +389,38 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
void existsByControllerIdPermissionsCheck() {
|
||||
assertPermissions(() -> targetManagement.existsByControllerId("controllerId"), List.of(SpPermission.READ_TARGET));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
void isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatablePermissionsCheck() {
|
||||
void createMetadataPermissionsCheck() {
|
||||
assertPermissions(
|
||||
() -> targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable("controllerId", 1L, "controllerId==id"),
|
||||
List.of(SpPermission.READ_TARGET, SpPermission.READ_REPOSITORY));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
void getTagsByControllerIdPermissionsCheck() {
|
||||
assertPermissions(() -> targetManagement.getTagsByControllerId("controllerId"), List.of(SpPermission.READ_TARGET));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
void createMetaDataPermissionsCheck() {
|
||||
assertPermissions(
|
||||
() -> targetManagement.createMetaData("controllerId", List.of(entityFactory.generateTargetMetadata("key", "value"))),
|
||||
() -> {
|
||||
targetManagement.createMetadata("controllerId", Map.of("key", "value"));
|
||||
return null;
|
||||
},
|
||||
List.of(SpPermission.UPDATE_REPOSITORY));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
void deleteMetaDataPermissionsCheck() {
|
||||
assertPermissions(() -> {
|
||||
targetManagement.deleteMetaData("controllerId", "key");
|
||||
return null;
|
||||
}, List.of(SpPermission.UPDATE_REPOSITORY));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
void countMetaDataByControllerIdPermissionsCheck() {
|
||||
assertPermissions(() -> targetManagement.countMetaDataByControllerId("controllerId"), List.of(SpPermission.READ_REPOSITORY));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
void findMetaDataByControllerIdAndRsqlPermissionsCheck() {
|
||||
assertPermissions(() -> targetManagement.findMetaDataByControllerIdAndRsql(PAGE, "controllerId", "controllerId==id"),
|
||||
List.of(SpPermission.READ_REPOSITORY));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
void getMetaDataByControllerIdPermissionsCheck() {
|
||||
assertPermissions(() -> targetManagement.getMetaDataByControllerId("controllerId", "key"), List.of(SpPermission.READ_REPOSITORY));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
void findMetaDataByControllerIdPermissionsCheck() {
|
||||
assertPermissions(() -> targetManagement.findMetaDataByControllerId(PAGE, "controllerId"), List.of(SpPermission.READ_REPOSITORY));
|
||||
void getMetadataPermissionsCheck() {
|
||||
assertPermissions(() -> targetManagement.getMetadata("controllerId"), List.of(SpPermission.READ_REPOSITORY));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
@WithUser(principal = "user", authorities = { SpPermission.UPDATE_REPOSITORY })
|
||||
void updateMetadataPermissionsCheck() {
|
||||
assertPermissions(() -> targetManagement.updateMetadata("controllerId", entityFactory.generateTargetMetadata("key", "value")),
|
||||
assertPermissions(() -> {
|
||||
targetManagement.updateMetadata("controllerId", "key", "value");
|
||||
return null;
|
||||
},
|
||||
List.of(SpPermission.UPDATE_REPOSITORY));
|
||||
}
|
||||
|
||||
}
|
||||
@Test
|
||||
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
|
||||
void deleteMetadataPermissionsCheck() {
|
||||
assertPermissions(() -> {
|
||||
targetManagement.deleteMetadata("controllerId", "key");
|
||||
return null;
|
||||
}, List.of(SpPermission.UPDATE_REPOSITORY));
|
||||
}
|
||||
}
|
||||
@@ -60,17 +60,14 @@ import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldExc
|
||||
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.model.MetaData;
|
||||
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.Tag;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTypeAssignmentResult;
|
||||
@@ -81,7 +78,6 @@ import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
|
||||
import org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch;
|
||||
import org.eclipse.hawkbit.repository.test.util.WithUser;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Slice;
|
||||
|
||||
@@ -99,7 +95,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Target target = testdataFactory.createTarget();
|
||||
assertThat(targetManagement.getByControllerID(NOT_EXIST_ID)).isNotPresent();
|
||||
assertThat(targetManagement.get(NOT_EXIST_IDL)).isNotPresent();
|
||||
assertThat(targetManagement.getMetaDataByControllerId(target.getControllerId(), NOT_EXIST_ID)).isNotPresent();
|
||||
assertThat(targetManagement.getMetadata(target.getControllerId()).get(NOT_EXIST_ID)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -113,20 +109,15 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Target target = testdataFactory.createTarget();
|
||||
|
||||
verifyThrownExceptionBy(
|
||||
() -> targetManagement.assignTag(Collections.singletonList(target.getControllerId()), NOT_EXIST_IDL),
|
||||
"TargetTag");
|
||||
verifyThrownExceptionBy(() -> targetManagement.assignTag(Collections.singletonList(NOT_EXIST_ID), tag.getId()),
|
||||
"Target");
|
||||
() -> targetManagement.assignTag(Collections.singletonList(target.getControllerId()), NOT_EXIST_IDL), "TargetTag");
|
||||
verifyThrownExceptionBy(() -> targetManagement.assignTag(Collections.singletonList(NOT_EXIST_ID), tag.getId()), "Target");
|
||||
|
||||
verifyThrownExceptionBy(() -> targetManagement.findByTag(PAGE, NOT_EXIST_IDL), "TargetTag");
|
||||
verifyThrownExceptionBy(() -> targetManagement.findByRsqlAndTag(PAGE, "name==*", NOT_EXIST_IDL), "TargetTag");
|
||||
|
||||
verifyThrownExceptionBy(() -> targetManagement.countByAssignedDistributionSet(NOT_EXIST_IDL),
|
||||
"DistributionSet");
|
||||
verifyThrownExceptionBy(() -> targetManagement.countByInstalledDistributionSet(NOT_EXIST_IDL),
|
||||
"DistributionSet");
|
||||
verifyThrownExceptionBy(() -> targetManagement.existsByInstalledOrAssignedDistributionSet(NOT_EXIST_IDL),
|
||||
"DistributionSet");
|
||||
verifyThrownExceptionBy(() -> targetManagement.countByAssignedDistributionSet(NOT_EXIST_IDL), "DistributionSet");
|
||||
verifyThrownExceptionBy(() -> targetManagement.countByInstalledDistributionSet(NOT_EXIST_IDL), "DistributionSet");
|
||||
verifyThrownExceptionBy(() -> targetManagement.existsByInstalledOrAssignedDistributionSet(NOT_EXIST_IDL), "DistributionSet");
|
||||
|
||||
verifyThrownExceptionBy(() -> targetManagement.countByTargetFilterQuery(NOT_EXIST_IDL), "TargetFilterQuery");
|
||||
verifyThrownExceptionBy(() -> targetManagement.countByRsqlAndNonDSAndCompatibleAndUpdatable(NOT_EXIST_IDL, "name==*"),
|
||||
@@ -138,45 +129,29 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
|
||||
verifyThrownExceptionBy(
|
||||
() -> targetManagement.findByTargetFilterQueryAndNonDSAndCompatibleAndUpdatable(PAGE, NOT_EXIST_IDL, "name==*"),
|
||||
"DistributionSet");
|
||||
verifyThrownExceptionBy(() -> targetManagement.findByInRolloutGroupWithoutAction(PAGE, NOT_EXIST_IDL),
|
||||
"RolloutGroup");
|
||||
verifyThrownExceptionBy(() -> targetManagement.findByAssignedDistributionSet(PAGE, NOT_EXIST_IDL),
|
||||
"DistributionSet");
|
||||
verifyThrownExceptionBy(() -> targetManagement.findByInRolloutGroupWithoutAction(PAGE, NOT_EXIST_IDL), "RolloutGroup");
|
||||
verifyThrownExceptionBy(() -> targetManagement.findByAssignedDistributionSet(PAGE, NOT_EXIST_IDL), "DistributionSet");
|
||||
verifyThrownExceptionBy(
|
||||
() -> targetManagement.findByAssignedDistributionSetAndRsql(PAGE, NOT_EXIST_IDL, "name==*"),
|
||||
"DistributionSet");
|
||||
() -> targetManagement.findByAssignedDistributionSetAndRsql(PAGE, NOT_EXIST_IDL, "name==*"), "DistributionSet");
|
||||
|
||||
verifyThrownExceptionBy(() -> targetManagement.findByInstalledDistributionSet(PAGE, NOT_EXIST_IDL),
|
||||
"DistributionSet");
|
||||
verifyThrownExceptionBy(() -> targetManagement.findByInstalledDistributionSet(PAGE, NOT_EXIST_IDL), "DistributionSet");
|
||||
verifyThrownExceptionBy(
|
||||
() -> targetManagement.findByInstalledDistributionSetAndRsql(PAGE, NOT_EXIST_IDL, "name==*"),
|
||||
"DistributionSet");
|
||||
() -> targetManagement.findByInstalledDistributionSetAndRsql(PAGE, NOT_EXIST_IDL, "name==*"), "DistributionSet");
|
||||
|
||||
verifyThrownExceptionBy(() -> targetManagement
|
||||
.assignTag(Collections.singletonList(target.getControllerId()), Long.parseLong(NOT_EXIST_ID)), "TargetTag");
|
||||
verifyThrownExceptionBy(
|
||||
() -> targetManagement.assignTag(Collections.singletonList(NOT_EXIST_ID), tag.getId()),
|
||||
"Target");
|
||||
() -> targetManagement.assignTag(Collections.singletonList(NOT_EXIST_ID), tag.getId()), "Target");
|
||||
|
||||
verifyThrownExceptionBy(() -> targetManagement.unassignTag(List.of(NOT_EXIST_ID), tag.getId()), "Target");
|
||||
verifyThrownExceptionBy(() -> targetManagement.unassignTag(List.of(target.getControllerId()), NOT_EXIST_IDL),
|
||||
"TargetTag");
|
||||
verifyThrownExceptionBy(() -> targetManagement.unassignTag(List.of(target.getControllerId()), NOT_EXIST_IDL), "TargetTag");
|
||||
verifyThrownExceptionBy(() -> targetManagement.update(entityFactory.target().update(NOT_EXIST_ID)), "Target");
|
||||
|
||||
verifyThrownExceptionBy(() -> targetManagement.createMetaData(NOT_EXIST_ID,
|
||||
Collections.singletonList(entityFactory.generateTargetMetadata("123", "123"))), "Target");
|
||||
verifyThrownExceptionBy(() -> targetManagement.deleteMetaData(NOT_EXIST_ID, "xxx"), "Target");
|
||||
verifyThrownExceptionBy(() -> targetManagement.deleteMetaData(target.getControllerId(), NOT_EXIST_ID),
|
||||
"TargetMetadata");
|
||||
verifyThrownExceptionBy(() -> targetManagement.getMetaDataByControllerId(NOT_EXIST_ID, "xxx"), "Target");
|
||||
verifyThrownExceptionBy(() -> targetManagement.findMetaDataByControllerId(PAGE, NOT_EXIST_ID), "Target");
|
||||
verifyThrownExceptionBy(() -> targetManagement.findMetaDataByControllerIdAndRsql(PAGE, NOT_EXIST_ID, "key==*"),
|
||||
"Target");
|
||||
verifyThrownExceptionBy(
|
||||
() -> targetManagement.updateMetadata(NOT_EXIST_ID, entityFactory.generateTargetMetadata("xxx", "xxx")),
|
||||
"Target");
|
||||
verifyThrownExceptionBy(() -> targetManagement.updateMetadata(target.getControllerId(),
|
||||
entityFactory.generateTargetMetadata(NOT_EXIST_ID, "xxx")), "TargetMetadata");
|
||||
verifyThrownExceptionBy(() -> targetManagement.createMetadata(NOT_EXIST_ID, Map.of("123", "123")), "Target");
|
||||
verifyThrownExceptionBy(() -> targetManagement.deleteMetadata(NOT_EXIST_ID, "xxx"), "Target");
|
||||
verifyThrownExceptionBy(() -> targetManagement.deleteMetadata(target.getControllerId(), NOT_EXIST_ID), "Target");
|
||||
verifyThrownExceptionBy(() -> targetManagement.getMetadata(NOT_EXIST_ID).get("xxx"), "Target");
|
||||
verifyThrownExceptionBy(() -> targetManagement.updateMetadata(NOT_EXIST_ID, "xxx", "xxx"), "Target");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -764,68 +739,75 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Checks that metadata for a target can be created.")
|
||||
void createTargetMetadata() {
|
||||
final String knownKey = "targetMetaKnownKey";
|
||||
final String knownValue = "targetMetaKnownValue";
|
||||
|
||||
final Target target = testdataFactory.createTarget("targetIdWithMetadata");
|
||||
final JpaTargetMetadata createdMetadata = insertTargetMetadata(knownKey, knownValue, target);
|
||||
|
||||
assertThat(createdMetadata).isNotNull();
|
||||
assertThat(createdMetadata.getId().getKey()).isEqualTo(knownKey);
|
||||
assertThat(createdMetadata.getTarget().getControllerId()).isEqualTo(target.getControllerId());
|
||||
assertThat(createdMetadata.getTarget().getId()).isEqualTo(target.getId());
|
||||
assertThat(createdMetadata.getValue()).isEqualTo(knownValue);
|
||||
void createMetadata() {
|
||||
insertMetadata(
|
||||
"targetMetaKnownKey", "targetMetaKnownValue",
|
||||
testdataFactory.createTarget("targetIdWithMetadata")); // and validate
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verifies the enforcement of the metadata quota per target.")
|
||||
void createTargetMetadataUntilQuotaIsExceeded() {
|
||||
void createMetadataUntilQuotaIsExceeded() {
|
||||
// add meta-data one by one
|
||||
final Target target1 = testdataFactory.createTarget("target1");
|
||||
final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerTarget();
|
||||
for (int i = 0; i < maxMetaData; ++i) {
|
||||
assertThat(insertTargetMetadata("k" + i, "v" + i, target1)).isNotNull();
|
||||
insertMetadata("k" + i, "v" + i, target1);
|
||||
}
|
||||
|
||||
// quota exceeded
|
||||
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
|
||||
.isThrownBy(() -> insertTargetMetadata("k" + maxMetaData, "v" + maxMetaData, target1));
|
||||
.isThrownBy(() -> insertMetadata("k" + maxMetaData, "v" + maxMetaData, target1));
|
||||
|
||||
// add multiple meta data entries at once
|
||||
final Target target2 = testdataFactory.createTarget("target2");
|
||||
final List<MetaData> metaData2 = new ArrayList<>();
|
||||
for (int i = 0; i < maxMetaData + 1; ++i) {
|
||||
metaData2.add(new JpaTargetMetadata("k" + i, "v" + i, target2));
|
||||
final Map<String, String> metaData2 = new HashMap<>();
|
||||
for (int i = 0; i < maxMetaData + 1; i++) {
|
||||
metaData2.put("k" + i, "v" + i);
|
||||
}
|
||||
// verify quota is exceeded
|
||||
final String target2ControllerId = target2.getControllerId();
|
||||
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
|
||||
.isThrownBy(() -> targetManagement.createMetaData(target2ControllerId, metaData2));
|
||||
.isThrownBy(() -> targetManagement.createMetadata(target2ControllerId, metaData2));
|
||||
|
||||
// add some meta data entries
|
||||
final Target target3 = testdataFactory.createTarget("target3");
|
||||
final int firstHalf = maxMetaData / 2;
|
||||
for (int i = 0; i < firstHalf; ++i) {
|
||||
insertTargetMetadata("k" + i, "v" + i, target3);
|
||||
for (int i = 0; i < firstHalf; i++) {
|
||||
insertMetadata("k" + i, "v" + i, target3);
|
||||
}
|
||||
// add too many data entries
|
||||
final int secondHalf = maxMetaData - firstHalf;
|
||||
final List<MetaData> metaData3 = new ArrayList<>();
|
||||
for (int i = 0; i < secondHalf + 1; ++i) {
|
||||
metaData3.add(new JpaTargetMetadata("kk" + i, "vv" + i, target3));
|
||||
final Map<String, String> metaData3 = new HashMap<>();
|
||||
for (int i = 0; i < secondHalf + 1; i++) {
|
||||
metaData3.put("kk" + i, "vv" + i);
|
||||
}
|
||||
// verify quota is exceeded
|
||||
final String target3ControllerId = target3.getControllerId();
|
||||
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
|
||||
.isThrownBy(() -> targetManagement.createMetaData(target3ControllerId, metaData3));
|
||||
.isThrownBy(() -> targetManagement.createMetadata(target3ControllerId, metaData3));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
@Description("Queries and loads the metadata related to a given target.")
|
||||
void getMetadata() {
|
||||
// create targets
|
||||
final Target target1 = createTargetWithMetadata("target1", 10);
|
||||
final Target target2 = createTargetWithMetadata("target2", 8);
|
||||
|
||||
final Map<String, String> metadataOfTarget1 = targetManagement.getMetadata(target1.getControllerId());
|
||||
final Map<String, String> metadataOfTarget2 = targetManagement.getMetadata(target2.getControllerId());
|
||||
|
||||
assertThat(metadataOfTarget1).hasSize(10);
|
||||
assertThat(metadataOfTarget2).hasSize(8);
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithUser(allSpPermissions = true)
|
||||
@Description("Checks that metadata for a target can be updated.")
|
||||
void updateTargetMetadata() {
|
||||
void updateMetadata() {
|
||||
final String knownKey = "myKnownKey";
|
||||
final String knownValue = "myKnownValue";
|
||||
final String knownUpdateValue = "myNewUpdatedValue";
|
||||
@@ -836,26 +818,36 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
|
||||
assertThat(target.getOptLockRevision()).isEqualTo(1);
|
||||
|
||||
// create target meta data entry
|
||||
insertTargetMetadata(knownKey, knownValue, target);
|
||||
insertMetadata(knownKey, knownValue, target);
|
||||
|
||||
Target changedLockRevisionTarget = targetManagement.get(target.getId()).orElseThrow(NoSuchElementException::new);
|
||||
final Target changedLockRevisionTarget = targetManagement.get(target.getId()).orElseThrow(NoSuchElementException::new);
|
||||
assertThat(changedLockRevisionTarget.getOptLockRevision()).isEqualTo(2);
|
||||
|
||||
// update the target metadata
|
||||
final JpaTargetMetadata updated = (JpaTargetMetadata) targetManagement.updateMetadata(
|
||||
target.getControllerId(), entityFactory.generateTargetMetadata(knownKey, knownUpdateValue));
|
||||
// we are updating the target meta-data so also modifying the base
|
||||
// software module so opt lock revision must be three
|
||||
changedLockRevisionTarget = targetManagement.get(target.getId()).orElseThrow(NoSuchElementException::new);
|
||||
assertThat(changedLockRevisionTarget.getOptLockRevision()).isEqualTo(3);
|
||||
assertThat(changedLockRevisionTarget.getLastModifiedAt()).isPositive();
|
||||
targetManagement.updateMetadata(target.getControllerId(), knownKey, knownUpdateValue);
|
||||
// we are updating the target meta-data so also modifying the target so opt lock revision must be three
|
||||
final Target changedLockRevisionTarget2 = targetManagement.get(target.getId()).orElseThrow(NoSuchElementException::new);
|
||||
assertThat(changedLockRevisionTarget2.getOptLockRevision()).isEqualTo(3);
|
||||
assertThat(changedLockRevisionTarget2.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.getTarget().getControllerId()).isEqualTo(target.getControllerId());
|
||||
assertThat(updated.getTarget().getId()).isEqualTo(target.getId());
|
||||
assertThat(targetManagement.getMetadata(target.getControllerId()).get(knownKey)).isEqualTo(knownUpdateValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Queries and loads the metadata related to a given target.")
|
||||
void deleteMetadata() {
|
||||
final String knownKey = "myKnownKey";
|
||||
final String knownValue = "myKnownValue";
|
||||
|
||||
// create target
|
||||
final String controllerId = createTargetWithMetadata("target1", 10).getControllerId();
|
||||
targetManagement.createMetadata(controllerId, Map.of(knownKey, knownValue));
|
||||
|
||||
targetManagement.deleteMetadata(controllerId, knownKey);
|
||||
// already removed
|
||||
assertThatExceptionOfType(EntityNotFoundException.class)
|
||||
.isThrownBy(() -> targetManagement.deleteMetadata(controllerId, knownKey));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -963,26 +955,6 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
|
||||
checkTargetsHaveType(typeATargets, typeB);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Queries and loads the metadata related to a given target.")
|
||||
void findAllTargetMetadataByControllerId() {
|
||||
// create targets
|
||||
final Target target1 = createTargetWithMetadata("target1", 10);
|
||||
final Target target2 = createTargetWithMetadata("target2", 8);
|
||||
|
||||
final Page<TargetMetadata> metadataOfTarget1 = targetManagement
|
||||
.findMetaDataByControllerId(PageRequest.of(0, 100), target1.getControllerId());
|
||||
|
||||
final Page<TargetMetadata> metadataOfTarget2 = targetManagement
|
||||
.findMetaDataByControllerId(PageRequest.of(0, 100), target2.getControllerId());
|
||||
|
||||
assertThat(metadataOfTarget1.getNumberOfElements()).isEqualTo(10);
|
||||
assertThat(metadataOfTarget1.getTotalElements()).isEqualTo(10);
|
||||
|
||||
assertThat(metadataOfTarget2.getNumberOfElements()).isEqualTo(8);
|
||||
assertThat(metadataOfTarget2.getTotalElements()).isEqualTo(8);
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithUser(allSpPermissions = true)
|
||||
@Description("Checks that target type is not assigned to target if invalid.")
|
||||
@@ -1419,11 +1391,9 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
|
||||
}
|
||||
}
|
||||
|
||||
private JpaTargetMetadata insertTargetMetadata(final String knownKey, final String knownValue,
|
||||
final Target target) {
|
||||
final JpaTargetMetadata metadata = new JpaTargetMetadata(knownKey, knownValue, target);
|
||||
return (JpaTargetMetadata) targetManagement
|
||||
.createMetaData(target.getControllerId(), Collections.singletonList(metadata)).get(0);
|
||||
private void insertMetadata(final String knownKey, final String knownValue, final Target target) {
|
||||
targetManagement.createMetadata(target.getControllerId(), Map.of(knownKey, knownValue));
|
||||
assertThat(targetManagement.getMetadata(target.getControllerId()).get(knownKey)).isEqualTo(knownValue);
|
||||
}
|
||||
|
||||
private void checkTargetsHaveType(final List<Target> targets, final TargetType type) {
|
||||
@@ -1440,7 +1410,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Target target = testdataFactory.createTarget(controllerId);
|
||||
|
||||
for (int index = 1; index <= count; index++) {
|
||||
insertTargetMetadata("key" + index, controllerId + "-value" + index, target);
|
||||
insertMetadata("key" + index, controllerId + "-value" + index, target);
|
||||
}
|
||||
|
||||
return target;
|
||||
@@ -1450,7 +1420,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Target target = testdataFactory.createTarget(controllerId, controllerId, targetTypeId);
|
||||
|
||||
for (int index = 1; index <= count; index++) {
|
||||
insertTargetMetadata("key" + index, controllerId + "-value" + index, target);
|
||||
insertMetadata("key" + index, controllerId + "-value" + index, target);
|
||||
}
|
||||
|
||||
return target;
|
||||
|
||||
@@ -79,7 +79,7 @@ class RSQLTargetFieldTest extends AbstractJpaIntegrationTest {
|
||||
.description("targetDesc123"));
|
||||
target = controllerManagement.updateControllerAttributes(target.getControllerId(), Map.of("revision", "1.1"), null);
|
||||
target = controllerManagement.findOrRegisterTargetIfItDoesNotExist(target.getControllerId(), LOCALHOST);
|
||||
createTargetMetadata(target.getControllerId(), entityFactory.generateTargetMetadata("metaKey", "metaValue"));
|
||||
targetManagement.createMetadata(target.getControllerId(), Map.of("metaKey", "metaValue"));
|
||||
assignDistributionSet(ds.getId(), target.getControllerId());
|
||||
targetManagement.assignType(target.getControllerId(), targetType1.getId());
|
||||
|
||||
@@ -89,7 +89,7 @@ class RSQLTargetFieldTest extends AbstractJpaIntegrationTest {
|
||||
.description("targetId1234"));
|
||||
target2 = controllerManagement.updateControllerAttributes(target2.getControllerId(), Map.of("revision", "1.2"), null);
|
||||
targetManagement.assignType(target2.getControllerId(), targetType2.getId());
|
||||
createTargetMetadata(target2.getControllerId(), entityFactory.generateTargetMetadata("metaKey", "value"));
|
||||
targetManagement.createMetadata(target2.getControllerId(), Map.of("metaKey", "value"));
|
||||
target2 = controllerManagement.findOrRegisterTargetIfItDoesNotExist(target2.getControllerId(), LOCALHOST);
|
||||
targetManagement.assignTag(Arrays.asList(target.getControllerId(), target2.getControllerId()), targetTag.getId());
|
||||
|
||||
@@ -270,7 +270,7 @@ class RSQLTargetFieldTest extends AbstractJpaIntegrationTest {
|
||||
@Test
|
||||
@Description("Test filter target by metadata")
|
||||
void testFilterByMetadata() {
|
||||
createTargetMetadata(testdataFactory.createTarget().getControllerId(), entityFactory.generateTargetMetadata("key.dot", "value.dot"));
|
||||
targetManagement.createMetadata(testdataFactory.createTarget().getControllerId(), Map.of("key.dot", "value.dot"));
|
||||
|
||||
assertRSQLQuery(TargetFields.METADATA.name() + ".metaKey==metaValue", 1);
|
||||
assertRSQLQuery(TargetFields.METADATA.name() + ".metaKey==null", 0); // "null" check
|
||||
|
||||
@@ -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.TargetMetadataFields;
|
||||
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
|
||||
import org.eclipse.hawkbit.repository.model.MetaData;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetMetadata;
|
||||
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 target metadata")
|
||||
class RSQLTargetMetadataFieldsTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
private String controllerId;
|
||||
|
||||
@BeforeEach
|
||||
void setupBeforeTest() {
|
||||
final Target target = testdataFactory.createTarget("target");
|
||||
controllerId = target.getControllerId();
|
||||
|
||||
final List<MetaData> metadata = new ArrayList<>(5);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
metadata.add(entityFactory.generateTargetMetadata("" + i, "" + i));
|
||||
}
|
||||
|
||||
targetManagement.createMetaData(controllerId, metadata);
|
||||
|
||||
targetManagement.createMetaData(controllerId,
|
||||
Arrays.asList(entityFactory.generateTargetMetadata("emptyValueTest", null)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test filter target metadata by key")
|
||||
void testFilterByParameterKey() {
|
||||
assertRSQLQuery(TargetMetadataFields.KEY.name() + "==1", 1);
|
||||
assertRSQLQuery(TargetMetadataFields.KEY.name() + "!=1", 5);
|
||||
assertRSQLQuery(TargetMetadataFields.KEY.name() + "=in=(1,2)", 2);
|
||||
assertRSQLQuery(TargetMetadataFields.KEY.name() + "=out=(1,2)", 4);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test filter target metadata by value")
|
||||
void testFilterByParameterValue() {
|
||||
assertRSQLQuery(TargetMetadataFields.VALUE.name() + "==''", 1);
|
||||
assertRSQLQuery(TargetMetadataFields.VALUE.name() + "!=''", 5);
|
||||
assertRSQLQuery(TargetMetadataFields.VALUE.name() + "==1", 1);
|
||||
assertRSQLQuery(TargetMetadataFields.VALUE.name() + "!=1", 5);
|
||||
assertRSQLQuery(TargetMetadataFields.VALUE.name() + "=in=(1,2)", 2);
|
||||
assertRSQLQuery(TargetMetadataFields.VALUE.name() + "=out=(1,2)", 4);
|
||||
}
|
||||
|
||||
private void assertRSQLQuery(final String rsqlParam, final long expectedEntities) {
|
||||
|
||||
final Page<TargetMetadata> findEnitity = targetManagement
|
||||
.findMetaDataByControllerIdAndRsql(PageRequest.of(0, 100), controllerId, rsqlParam);
|
||||
final long countAllEntities = findEnitity.getTotalElements();
|
||||
assertThat(findEnitity).isNotNull();
|
||||
assertThat(countAllEntities).isEqualTo(expectedEntities);
|
||||
}
|
||||
}
|
||||
@@ -12,10 +12,10 @@
|
||||
|
||||
logging.level.org.eclipse.persistence=ERROR
|
||||
## Uncomment to see the debug of persistence, e.g. to see the generated SQLs
|
||||
logging.level.org.eclipse.persistence=DEBUG
|
||||
spring.jpa.properties.eclipselink.logging.level=FINE
|
||||
spring.jpa.properties.eclipselink.logging.level.sql=FINE
|
||||
spring.jpa.properties.eclipselink.logging.parameters=true
|
||||
#logging.level.org.eclipse.persistence=DEBUG
|
||||
#spring.jpa.properties.eclipselink.logging.level=FINE
|
||||
#spring.jpa.properties.eclipselink.logging.level.sql=FINE
|
||||
#spring.jpa.properties.eclipselink.logging.parameters=true
|
||||
|
||||
## Enable EclipseLink performance monitor (monitoring and profile)
|
||||
#spring.jpa.properties.eclipselink.profiler=PerformanceMonitor
|
||||
|
||||
Reference in New Issue
Block a user