Cleanup TargetManagement (#2601)

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2025-08-08 13:13:28 +03:00
committed by GitHub
parent 5217297c24
commit bff77ac224
35 changed files with 433 additions and 2032 deletions

View File

@@ -9,33 +9,14 @@
*/
package org.eclipse.hawkbit.repository.jpa.management;
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_DELAY;
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_MAX;
import java.util.Collections;
import java.util.Optional;
import jakarta.persistence.EntityManager;
import org.eclipse.hawkbit.repository.DistributionSetTagFields;
import org.eclipse.hawkbit.repository.DistributionSetTagManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTagRepository;
import org.eclipse.hawkbit.repository.jpa.specifications.DistributionSetTagSpecifications;
import org.eclipse.hawkbit.repository.jpa.specifications.TagSpecification;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "distribution-set-tag-management" }, matchIfMissing = true)

View File

@@ -211,7 +211,7 @@ class JpaTargetFilterQueryManagement
private void assertMaxTargetsQuota(final String query, final String filterName, final long dsId) {
QuotaHelper.assertAssignmentQuota(filterName,
targetManagement.countByRsqlAndNonDSAndCompatibleAndUpdatable(dsId, query),
targetManagement.countByRsqlAndNonDsAndCompatibleAndUpdatable(dsId, query),
quotaManagement.getMaxTargetsPerAutoAssignment(), Target.class, TargetFilterQuery.class, null);
}

View File

@@ -11,7 +11,6 @@ package org.eclipse.hawkbit.repository.jpa.management;
import static org.eclipse.hawkbit.repository.jpa.JpaManagementHelper.combineWithAnd;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
@@ -35,14 +34,9 @@ import jakarta.persistence.criteria.Root;
import jakarta.persistence.metamodel.MapAttribute;
import jakarta.validation.constraints.NotEmpty;
import org.apache.commons.collections4.ListUtils;
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.TimestampCalculator;
import org.eclipse.hawkbit.repository.event.EventPublisherHolder;
import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEvent;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
@@ -52,9 +46,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
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.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.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetTagRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetTypeRepository;
@@ -66,12 +58,8 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
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.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetTypeAssignmentResult;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
@@ -82,7 +70,6 @@ import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated;
/**
@@ -100,9 +87,7 @@ public class JpaTargetManagement
private final QuotaManagement quotaManagement;
private final TargetTypeRepository targetTypeRepository;
private final RolloutGroupRepository rolloutGroupRepository;
private final TargetFilterQueryRepository targetFilterQueryRepository;
private final TargetTagRepository targetTagRepository;
private final TenantAware tenantAware;
@SuppressWarnings("java:S107")
protected JpaTargetManagement(
@@ -110,83 +95,41 @@ public class JpaTargetManagement
final JpaDistributionSetManagement distributionSetManagement, final QuotaManagement quotaManagement,
final TargetTypeRepository targetTypeRepository,
final RolloutGroupRepository rolloutGroupRepository,
final TargetFilterQueryRepository targetFilterQueryRepository,
final TargetTagRepository targetTagRepository,
final TenantAware tenantAware) {
final TargetTagRepository targetTagRepository) {
super(jpaRepository, entityManager);
this.distributionSetManagement = distributionSetManagement;
this.quotaManagement = quotaManagement;
this.targetTypeRepository = targetTypeRepository;
this.rolloutGroupRepository = rolloutGroupRepository;
this.targetFilterQueryRepository = targetFilterQueryRepository;
this.targetTagRepository = targetTagRepository;
this.tenantAware = tenantAware;
}
@Override
public long countByAssignedDistributionSet(final long distributionSetId) {
final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException((distributionSetId));
return jpaRepository.count(TargetSpecifications.hasAssignedDistributionSet(validDistSet.getId()));
public Map<String, String> getControllerAttributes(final String controllerId) {
return getMap(controllerId, JpaTarget_.controllerAttributes);
}
@Override
public long countByFilters(final FilterParams filterParams) {
final List<Specification<JpaTarget>> specList = buildSpecificationList(filterParams);
return JpaManagementHelper.countBySpec(jpaRepository, specList);
}
@Override
public long countByInstalledDistributionSet(final long distributionSetId) {
final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException((distributionSetId));
return jpaRepository.count(TargetSpecifications.hasInstalledDistributionSet(validDistSet.getId()));
}
@Override
public boolean existsByInstalledOrAssignedDistributionSet(final long distributionSetId) {
final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException((distributionSetId));
return jpaRepository.exists(TargetSpecifications.hasInstalledOrAssignedDistributionSet(validDistSet.getId()));
}
@Override
public long countByRsqlAndUpdatable(String rsql) {
public boolean isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(
final String controllerId, final long distributionSetId, final String targetFilterQuery) {
RsqlUtility.getInstance().validateRsqlFor(targetFilterQuery, TargetFields.class, JpaTarget.class);
final DistributionSet ds = distributionSetManagement.get(distributionSetId)
.orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, distributionSetId));
final Long distSetTypeId = ds.getType().getId();
final List<Specification<JpaTarget>> specList = List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetFields.class));
return jpaRepository.count(AccessController.Operation.UPDATE, combineWithAnd(specList));
RsqlUtility.getInstance().buildRsqlSpecification(targetFilterQuery, TargetFields.class),
TargetSpecifications.hasNotDistributionSetInActions(distributionSetId),
TargetSpecifications.isCompatibleWithDistributionSetType(distSetTypeId),
TargetSpecifications.hasControllerId(controllerId));
final Specification<JpaTarget> combinedSpecification = Objects
.requireNonNull(SpecificationsBuilder.combineWithAnd(specList));
return jpaRepository.exists(AccessController.Operation.UPDATE, combinedSpecification);
}
@Override
public long countByRsqlAndCompatible(final String rsql, final Long distributionSetIdTypeId) {
final List<Specification<JpaTarget>> specList = List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetFields.class),
TargetSpecifications.isCompatibleWithDistributionSetType(distributionSetIdTypeId));
return JpaManagementHelper.countBySpec(jpaRepository, specList);
}
@Override
public long countByFailedInRollout(final String rolloutId, final Long dsTypeId) {
final List<Specification<JpaTarget>> specList = List.of(TargetSpecifications.failedActionsForRollout(rolloutId));
return JpaManagementHelper.countBySpec(jpaRepository, specList);
}
@Override
public long countByTargetFilterQuery(final long targetFilterQueryId) {
final TargetFilterQuery targetFilterQuery = targetFilterQueryRepository
.findById(targetFilterQueryId)
.orElseThrow(() -> new EntityNotFoundException(TargetFilterQuery.class, targetFilterQueryId));
return countByRsql(targetFilterQuery.getQuery());
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void deleteByControllerID(final String controllerId) {
jpaRepository.delete(getByControllerIdAndThrowIfNotFound(controllerId));
}
@Override
public Slice<Target> findByTargetFilterQueryAndNonDSAndCompatibleAndUpdatable(final long distributionSetId, final String rsql,
final Pageable pageable) {
public Slice<Target> findByTargetFilterQueryAndNonDSAndCompatibleAndUpdatable(
final long distributionSetId, final String rsql, final Pageable pageable) {
final DistributionSet jpaDistributionSet = distributionSetManagement.getOrElseThrowException(distributionSetId);
final Long distSetTypeId = jpaDistributionSet.getType().getId();
@@ -202,16 +145,18 @@ public class JpaTargetManagement
}
@Override
public long countByRsqlAndNonDSAndCompatibleAndUpdatable(final long distributionSetId, final String rsql) {
final DistributionSet jpaDistributionSet = distributionSetManagement.getOrElseThrowException(distributionSetId);
final Long distSetTypeId = jpaDistributionSet.getType().getId();
public List<Target> getByControllerId(final Collection<String> controllerIDs) {
return Collections.unmodifiableList(jpaRepository.findAll(TargetSpecifications.byControllerIdWithAssignedDsInJoin(controllerIDs)));
}
return jpaRepository.count(
AccessController.Operation.UPDATE,
combineWithAnd(List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetFields.class),
TargetSpecifications.hasNotDistributionSetInActions(distributionSetId),
TargetSpecifications.isCompatibleWithDistributionSetType(distSetTypeId))));
@Override
public Optional<Target> getByControllerId(final String controllerId) {
return jpaRepository.findByControllerId(controllerId).map(Target.class::cast);
}
@Override
public Target getWithDetails(final String controllerId, final String detailsKey) {
return jpaRepository.getWithDetailsByControllerId(controllerId, "Target." + detailsKey);
}
@Override
@@ -227,16 +172,6 @@ public class JpaTargetManagement
.map(Target.class::cast);
}
@Override
public long countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(
final String rsql, final Collection<Long> groups, final DistributionSetType dsType) {
return jpaRepository.count(AccessController.Operation.UPDATE,
combineWithAnd(List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetFields.class),
TargetSpecifications.isNotInRolloutGroups(groups),
TargetSpecifications.isCompatibleWithDistributionSetType(dsType.getId()))));
}
@Override
public Slice<Target> findByFailedRolloutAndNotInRolloutGroups(String rolloutId, Collection<Long> groups, Pageable pageable) {
final List<Specification<JpaTarget>> specList = List.of(
@@ -245,14 +180,6 @@ public class JpaTargetManagement
return JpaManagementHelper.findAllWithCountBySpec(jpaRepository, specList, pageable);
}
@Override
public long countByFailedRolloutAndNotInRolloutGroups(String rolloutId, Collection<Long> groups) {
final List<Specification<JpaTarget>> specList = List.of(
TargetSpecifications.failedActionsForRollout(rolloutId),
TargetSpecifications.isNotInRolloutGroups(groups));
return JpaManagementHelper.countBySpec(jpaRepository, specList);
}
@Override
public Slice<Target> findByRsqlAndNoOverridingActionsAndNotInRolloutAndCompatibleAndUpdatable(
final long rolloutId, final String rsql, final DistributionSetType distributionSetType, final Pageable pageable) {
@@ -266,11 +193,6 @@ public class JpaTargetManagement
.map(Target.class::cast);
}
@Override
public long countByActionsInRolloutGroup(final long rolloutGroupId) {
return jpaRepository.count(TargetSpecifications.isInActionRolloutGroup(rolloutGroupId));
}
@Override
public Slice<Target> findByInRolloutGroupWithoutAction(final long group, final Pageable pageable) {
if (!rolloutGroupRepository.existsById(group)) {
@@ -301,27 +223,6 @@ public class JpaTargetManagement
return JpaManagementHelper.findAllWithCountBySpec(jpaRepository, specList, pageable);
}
@Override
public List<Target> getByControllerId(final Collection<String> controllerIDs) {
return Collections.unmodifiableList(jpaRepository.findAll(TargetSpecifications.byControllerIdWithAssignedDsInJoin(controllerIDs)));
}
@Override
public Optional<Target> getByControllerId(final String controllerId) {
return jpaRepository.findByControllerId(controllerId).map(Target.class::cast);
}
@Override
public Target getWithDetails(final String controllerId, final String detailsKey) {
return jpaRepository.getWithDetailsByControllerId(controllerId, "Target." + detailsKey);
}
@Override
public Slice<Target> findByFilters(final FilterParams filterParams, final Pageable pageable) {
final List<Specification<JpaTarget>> specList = buildSpecificationList(filterParams);
return JpaManagementHelper.findAllWithoutCountBySpec(jpaRepository, specList, pageable);
}
@Override
public Page<Target> findByInstalledDistributionSet(final long distributionSetId, final Pageable pageReq) {
final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException(distributionSetId);
@@ -341,23 +242,6 @@ public class JpaTargetManagement
return JpaManagementHelper.findAllWithCountBySpec(jpaRepository, specList, pageable);
}
@Override
public Page<Target> findByUpdateStatus(final TargetUpdateStatus status, final Pageable pageable) {
return JpaManagementHelper.findAllWithCountBySpec(
jpaRepository, List.of(TargetSpecifications.hasTargetUpdateStatus(status)), pageable);
}
@Override
public Slice<Target> findByTargetFilterQuery(final long targetFilterQueryId, final Pageable pageable) {
final TargetFilterQuery targetFilterQuery = targetFilterQueryRepository.findById(targetFilterQueryId)
.orElseThrow(() -> new EntityNotFoundException(TargetFilterQuery.class, targetFilterQueryId));
return JpaManagementHelper.findAllWithoutCountBySpec(
jpaRepository, List.of(RsqlUtility.getInstance().buildRsqlSpecification(
targetFilterQuery.getQuery(), TargetFields.class)), pageable
);
}
@Override
public Page<Target> findByTag(final long tagId, final Pageable pageable) {
throwEntityNotFoundExceptionIfTagDoesNotExist(tagId);
@@ -376,44 +260,61 @@ public class JpaTargetManagement
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public TargetTypeAssignmentResult assignType(final Collection<String> controllerIds, final Long typeId) {
final JpaTargetType type = targetTypeRepository
.findById(typeId)
.orElseThrow(() -> new EntityNotFoundException(TargetType.class, typeId));
public long countByRsqlAndCompatible(final String rsql, final Long distributionSetIdTypeId) {
final List<Specification<JpaTarget>> specList = List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetFields.class),
TargetSpecifications.isCompatibleWithDistributionSetType(distributionSetIdTypeId));
return JpaManagementHelper.countBySpec(jpaRepository, specList);
}
final List<JpaTarget> targetsWithSameType = findTargetsByInSpecification(controllerIds, TargetSpecifications.hasTargetType(typeId));
final List<JpaTarget> targetsWithoutSameType =
findTargetsByInSpecification(controllerIds, TargetSpecifications.hasTargetTypeNot(typeId));
@Override
public long countByFailedInRollout(final String rolloutId, final Long dsTypeId) {
final List<Specification<JpaTarget>> specList = List.of(TargetSpecifications.failedActionsForRollout(rolloutId));
return JpaManagementHelper.countBySpec(jpaRepository, specList);
}
// set new target type to all targets without that type
targetsWithoutSameType.forEach(target -> target.setTargetType(type));
@Override
public long countByRsqlAndNonDsAndCompatibleAndUpdatable(final long distributionSetId, final String rsql) {
final DistributionSet jpaDistributionSet = distributionSetManagement.getOrElseThrowException(distributionSetId);
final Long distSetTypeId = jpaDistributionSet.getType().getId();
final TargetTypeAssignmentResult result = new TargetTypeAssignmentResult(
targetsWithSameType.size(), jpaRepository.saveAll(targetsWithoutSameType), Collections.emptyList(), type);
return jpaRepository.count(
AccessController.Operation.UPDATE,
combineWithAnd(List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetFields.class),
TargetSpecifications.hasNotDistributionSetInActions(distributionSetId),
TargetSpecifications.isCompatibleWithDistributionSetType(distSetTypeId))));
}
// no reason to persist the type
entityManager.detach(type);
return result;
@Override
public long countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(
final String rsql, final Collection<Long> groups, final DistributionSetType dsType) {
return jpaRepository.count(AccessController.Operation.UPDATE,
combineWithAnd(List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetFields.class),
TargetSpecifications.isNotInRolloutGroups(groups),
TargetSpecifications.isCompatibleWithDistributionSetType(dsType.getId()))));
}
@Override
public long countByFailedRolloutAndNotInRolloutGroups(String rolloutId, Collection<Long> groups) {
final List<Specification<JpaTarget>> specList = List.of(
TargetSpecifications.failedActionsForRollout(rolloutId),
TargetSpecifications.isNotInRolloutGroups(groups));
return JpaManagementHelper.countBySpec(jpaRepository, specList);
}
@Override
public long countByActionsInRolloutGroup(final long rolloutGroupId) {
return jpaRepository.count(TargetSpecifications.isInActionRolloutGroup(rolloutGroupId));
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public TargetTypeAssignmentResult unassignType(final Collection<String> controllerIds) {
final List<JpaTarget> allTargets = findTargetsByInSpecification(controllerIds, null);
if (allTargets.size() < controllerIds.size()) {
throw new EntityNotFoundException(Target.class, controllerIds, allTargets.stream().map(Target::getControllerId).toList());
}
// set new target type to null for all targets
allTargets.forEach(target -> target.setTargetType(null));
return new TargetTypeAssignmentResult(0, Collections.emptyList(), jpaRepository.saveAll(allTargets), null);
public void deleteByControllerId(final String controllerId) {
jpaRepository.delete(getByControllerIdAndThrowIfNotFound(controllerId));
}
@Override
@@ -445,6 +346,12 @@ public class JpaTargetManagement
});
}
@Override
public Set<TargetTag> getTags(@NotEmpty String controllerId) {
// the method has PreAuthorized by itself
return ((JpaTarget) getWithDetails(controllerId, DETAILS_TAGS)).getTags();
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
@@ -474,16 +381,6 @@ public class JpaTargetManagement
});
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Target unassignType(final String controllerId) {
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
target.setTargetType(null);
return jpaRepository.save(target);
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
@@ -500,23 +397,15 @@ public class JpaTargetManagement
}
@Override
public Page<Target> findTargetsByGroup(String group, final boolean withSubgroups, final Pageable pageable) {
if (withSubgroups) {
// search for eq(group) and like(group%)
return JpaManagementHelper
.findAllWithCountBySpec(jpaRepository, List.of(TargetSpecifications.eqOrSubTargetGroup(group)), pageable);
} else {
return JpaManagementHelper
.findAllWithCountBySpec(jpaRepository, List.of(TargetSpecifications.eqTargetGroup(group)), pageable);
}
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public Target unassignType(final String controllerId) {
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
target.setTargetType(null);
return jpaRepository.save(target);
}
@Override
public List<String> findGroups() {
return jpaRepository.findDistinctGroups();
}
@Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
@@ -547,69 +436,23 @@ public class JpaTargetManagement
controllerIds.forEach(in::value);
entityManager.createQuery(criteriaQuery.set("group", group).where(in)).executeUpdate();
}
@Override
public boolean existsByControllerId(final String controllerId) {
return jpaRepository.exists(TargetSpecifications.hasControllerId(controllerId));
public Page<Target> findTargetsByGroup(String group, final boolean withSubgroups, final Pageable pageable) {
if (withSubgroups) {
// search for eq(group) and like(group%)
return JpaManagementHelper
.findAllWithCountBySpec(jpaRepository, List.of(TargetSpecifications.eqOrSubTargetGroup(group)), pageable);
} else {
return JpaManagementHelper
.findAllWithCountBySpec(jpaRepository, List.of(TargetSpecifications.eqTargetGroup(group)), pageable);
}
}
@Override
public boolean isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(
final String controllerId, final long distributionSetId, final String targetFilterQuery) {
RsqlUtility.getInstance().validateRsqlFor(targetFilterQuery, TargetFields.class, JpaTarget.class);
final DistributionSet ds = distributionSetManagement.get(distributionSetId)
.orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, distributionSetId));
final Long distSetTypeId = ds.getType().getId();
final List<Specification<JpaTarget>> specList = List.of(
RsqlUtility.getInstance().buildRsqlSpecification(targetFilterQuery, TargetFields.class),
TargetSpecifications.hasNotDistributionSetInActions(distributionSetId),
TargetSpecifications.isCompatibleWithDistributionSetType(distSetTypeId),
TargetSpecifications.hasControllerId(controllerId));
final Specification<JpaTarget> combinedSpecification = Objects
.requireNonNull(SpecificationsBuilder.combineWithAnd(specList));
return jpaRepository.exists(AccessController.Operation.UPDATE, combinedSpecification);
}
@Override
public Set<TargetTag> getTags(@NotEmpty String controllerId) {
// the method has PreAuthorized by itself
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 void requestControllerAttributes(final String controllerId) {
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
jpaRepository.getAccessController()
.ifPresent(acm -> acm.assertOperationAllowed(AccessController.Operation.UPDATE, target));
target.setRequestControllerAttributes(true);
AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit().afterCommit(() ->
EventPublisherHolder.getInstance().getEventPublisher()
.publishEvent(new TargetAttributesRequestedEvent(
tenantAware.getCurrentTenant(), target.getId(), JpaTarget.class, target.getControllerId(),
target.getAddress() != null ? target.getAddress().toString() : null
)));
}
@Override
public boolean isControllerAttributesRequested(final String controllerId) {
return getByControllerIdAndThrowIfNotFound(controllerId).isRequestControllerAttributes();
}
@Override
public Page<Target> findByControllerAttributesRequested(final Pageable pageable) {
return JpaManagementHelper.findAllWithCountBySpec(
jpaRepository, List.of(TargetSpecifications.hasRequestControllerAttributesTrue()), pageable);
public List<String> findGroups() {
return jpaRepository.findDistinctGroups();
}
@Override
@@ -695,22 +538,6 @@ public class JpaTargetManagement
.collect(Collectors.toMap(entry -> (String) entry[0], entry -> (String) entry[1], (v1, v2) -> v1, LinkedHashMap::new));
}
private static boolean hasTagsFilterActive(final FilterParams filterParams) {
final boolean isNoTagActive = Boolean.TRUE.equals(filterParams.getSelectTargetWithNoTag());
final boolean isAtLeastOneTagActive = filterParams.getFilterByTagNames() != null
&& filterParams.getFilterByTagNames().length > 0;
return isNoTagActive || isAtLeastOneTagActive;
}
private static boolean hasTypesFilterActive(final FilterParams filterParams) {
return filterParams.getFilterByTargetType() != null;
}
private static boolean hasNoTypeFilterActive(final FilterParams filterParams) {
return Boolean.TRUE.equals(filterParams.getSelectTargetWithNoTargetType());
}
private static Collection<String> notFound(final Collection<String> controllerIds, final List<JpaTarget> foundTargets) {
final Map<String, JpaTarget> foundTargetMap = foundTargets.stream()
.collect(Collectors.toMap(Target::getControllerId, Function.identity()));
@@ -730,43 +557,6 @@ public class JpaTargetManagement
QuotaHelper.assertAssignmentQuota(targetId, requested, limit, "Metadata", Target.class.getSimpleName(), null);
}
private List<Specification<JpaTarget>> buildSpecificationList(final FilterParams filterParams) {
final List<Specification<JpaTarget>> specList = new ArrayList<>();
if ((filterParams.getFilterByStatus() != null) && !filterParams.getFilterByStatus().isEmpty()) {
specList.add(TargetSpecifications.hasTargetUpdateStatus(filterParams.getFilterByStatus()));
}
if (filterParams.getOverdueState() != null && filterParams.getOverdueState()) {
specList.add(TargetSpecifications.isOverdue(TimestampCalculator.calculateOverdueTimestamp()));
}
if (filterParams.getFilterByDistributionId() != null) {
final DistributionSet validDistSet = distributionSetManagement
.getOrElseThrowException(filterParams.getFilterByDistributionId());
specList.add(TargetSpecifications.hasInstalledOrAssignedDistributionSet(validDistSet.getId()));
}
if (!ObjectUtils.isEmpty(filterParams.getFilterBySearchText())) {
specList.add(TargetSpecifications.likeControllerIdOrName(filterParams.getFilterBySearchText()));
}
if (hasTagsFilterActive(filterParams)) {
specList.add(TargetSpecifications.hasTags(filterParams.getFilterByTagNames(),
filterParams.getSelectTargetWithNoTag()));
}
if (hasTypesFilterActive(filterParams)) {
specList.add(TargetSpecifications.hasTargetType(filterParams.getFilterByTargetType()));
} else if (hasNoTypeFilterActive(filterParams)) {
specList.add(TargetSpecifications.hasNoTargetType());
}
return specList;
}
private List<JpaTarget> findTargetsByInSpecification(final Collection<String> controllerIds, final Specification<JpaTarget> specification) {
return ListUtils.partition(new ArrayList<>(controllerIds), Constants.MAX_ENTRIES_IN_STATEMENT).stream()
.map(ids -> jpaRepository.findAll(TargetSpecifications.hasControllerIdIn(ids).and(specification)))
.flatMap(List::stream).toList();
}
private List<Target> updateTag(
final Collection<String> controllerIds, final long targetTagId, final Consumer<Collection<String>> notFoundHandler,
final BiFunction<JpaTargetTag, JpaTarget, Target> updater) {
@@ -776,8 +566,7 @@ public class JpaTargetManagement
jpaRepository.findByControllerId(controllerIds.iterator().next())
.map(List::of)
.orElseGet(Collections::emptyList) :
jpaRepository
.findAll(TargetSpecifications.byControllerIdWithTagsInJoin(controllerIds));
jpaRepository.findAll(TargetSpecifications.byControllerIdWithTagsInJoin(controllerIds));
if (targets.size() < controllerIds.size()) {
final Collection<String> notFound = notFound(controllerIds, targets);
if (notFoundHandler == null) {

View File

@@ -78,10 +78,6 @@ public final class ActionSpecifications {
isNull ? cb.isNull(root.get(JpaAction_.weight)) : cb.isNotNull(root.get(JpaAction_.weight)));
}
public static Specification<JpaAction> byDistributionSetId(final Long distributionSetId) {
return (root, query, cb) -> cb.equal(root.get(JpaAction_.distributionSet).get(AbstractJpaBaseEntity_.id), distributionSetId);
}
public static Specification<JpaAction> byDistributionSetIdAndActive(final Long distributionSetId) {
return (root, query, cb) -> cb.and(
cb.equal(root.get(JpaAction_.distributionSet).get(AbstractJpaBaseEntity_.id), distributionSetId),

View File

@@ -77,23 +77,6 @@ public final class DistributionSetSpecification {
return (dsRoot, query, cb) -> cb.equal(dsRoot.get(JpaDistributionSet_.valid), isValid);
}
/**
* {@link Specification} for retrieving {@link DistributionSet} with given {@link DistributionSet#getId()}.
*
* @param distid to search
* @return the {@link DistributionSet} {@link Specification}
*/
public static Specification<JpaDistributionSet> byIdFetch(final Long distid) {
return (dsRoot, query, cb) -> {
final Predicate predicate = cb.equal(dsRoot.get(AbstractJpaBaseEntity_.id), distid);
dsRoot.fetch(JpaDistributionSet_.modules, JoinType.LEFT);
dsRoot.fetch(JpaDistributionSet_.type, JoinType.LEFT);
query.distinct(true);
return predicate;
};
}
/**
* {@link Specification} for retrieving {@link DistributionSet} with given {@link DistributionSet#getId()}s.
*
@@ -162,16 +145,6 @@ public final class DistributionSetSpecification {
return (dsRoot, query, cb) -> cb.equal(dsRoot.get(JpaDistributionSet_.type).get(AbstractJpaBaseEntity_.id), typeId);
}
/**
* {@link Specification} for retrieving {@link DistributionSet} for given id collection of {@link DistributionSet#getType()}.
*
* @param typeIds id collection of distribution set type to search
* @return the {@link DistributionSet} {@link Specification}
*/
public static Specification<JpaDistributionSet> hasType(final Collection<Long> typeIds) {
return (dsRoot, query, cb) -> dsRoot.get(JpaDistributionSet_.type).get(AbstractJpaBaseEntity_.id).in(typeIds);
}
/**
* {@link Specification} for retrieving {@link DistributionSet}s by tag.
*

View File

@@ -1,29 +0,0 @@
/**
* Copyright (c) 2023 Bosch.IO 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.specifications;
import jakarta.validation.constraints.NotEmpty;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaNamedEntity_;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
import org.springframework.data.jpa.domain.Specification;
/**
* Utility class for {@link JpaDistributionSetTag}s {@link Specification}s. The class provides Spring Data JPQL Specifications.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class DistributionSetTagSpecifications {
public static Specification<JpaDistributionSetTag> byName(@NotEmpty final String name) {
return (targetRoot, query, cb) -> cb.equal(targetRoot.get(AbstractJpaNamedEntity_.name), name);
}
}

View File

@@ -9,6 +9,8 @@
*/
package org.eclipse.hawkbit.repository.jpa.specifications;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaNamedEntity_;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaTypeEntity_;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
@@ -18,25 +20,11 @@ import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.springframework.data.jpa.domain.Specification;
/**
* Specifications class for {@link DistributionSetType}s. The class provides
* Spring Data JPQL Specifications.
* Specifications class for {@link DistributionSetType}s. The class provides Spring Data JPQL Specifications.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class DistributionSetTypeSpecification {
private DistributionSetTypeSpecification() {
// utility class
}
/**
* {@link Specification} for retrieving {@link DistributionSetType}s with
* DELETED attribute <code>false</code> - i.e. is not deleted.
*
* @return the {@link DistributionSetType} {@link Specification}
*/
public static Specification<JpaDistributionSetType> isNotDeleted() {
return (targetRoot, query, cb) -> cb.equal(targetRoot.<Boolean> get(JpaDistributionSetType_.deleted), false);
}
/**
* {@link Specification} for retrieving {@link DistributionSetType} with
* given {@link DistributionSetType#getName()} including fetching the
@@ -60,4 +48,4 @@ public final class DistributionSetTypeSpecification {
public static Specification<JpaDistributionSetType> byKey(final String key) {
return (targetRoot, query, cb) -> cb.equal(targetRoot.get(AbstractJpaTypeEntity_.key), key);
}
}
}

View File

@@ -11,91 +11,25 @@ package org.eclipse.hawkbit.repository.jpa.specifications;
import jakarta.persistence.criteria.ListJoin;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaNamedEntity_;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaNamedVersionedEntity_;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule_;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.springframework.data.jpa.domain.Specification;
/**
* Specifications class for {@link SoftwareModule}s. The class provides Spring
* Data JPQL Specifications
* Specifications class for {@link SoftwareModule}s. The class provides Spring Data JPQL Specifications
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class SoftwareModuleSpecification {
private SoftwareModuleSpecification() {
// utility class
}
/**
* {@link Specification} for retrieving {@link SoftwareModule} with given
* {@link DistributionSet#getId()}.
*
* @param swModuleId to search
* @return the {@link SoftwareModule} {@link Specification}
*/
public static Specification<JpaSoftwareModule> byId(final Long swModuleId) {
return (swRoot, query, cb) -> cb.equal(swRoot.get(AbstractJpaBaseEntity_.id), swModuleId);
}
public static Specification<JpaSoftwareModule> byAssignedToDs(final Long dsId) {
return (swRoot, query, cb) -> {
final ListJoin<JpaSoftwareModule, JpaDistributionSet> join = swRoot.join(JpaSoftwareModule_.assignedTo);
return cb.equal(join.get(AbstractJpaBaseEntity_.ID), dsId);
};
}
/**
* {@link Specification} for retrieving {@link SoftwareModule}s with
* DELETED attribute <code>false</code> - i.e. is not deleted.
*
* @return the {@link SoftwareModule} {@link Specification}
*/
public static Specification<JpaSoftwareModule> isNotDeleted() {
return (swRoot, query, cb) -> cb.equal(swRoot.get(JpaSoftwareModule_.deleted), false);
}
/**
* {@link Specification} for retrieving {@link SoftwareModule}s by "like
* name and like version".
*
* @param name to be filtered on
* @param version to be filtered on
* @return the {@link SoftwareModule} {@link Specification}
*/
public static Specification<JpaSoftwareModule> likeNameAndVersion(final String name, final String version) {
return (smRoot, query, cb) -> cb.and(
cb.like(cb.lower(smRoot.get(AbstractJpaNamedEntity_.name)), name.toLowerCase()),
cb.like(cb.lower(smRoot.get(AbstractJpaNamedVersionedEntity_.version)), version.toLowerCase()));
}
/**
* {@link Specification} for retrieving {@link SoftwareModule}s by "like
* name or like version".
*
* @param type to be filtered on
* @return the {@link SoftwareModule} {@link Specification}
*/
public static Specification<JpaSoftwareModule> equalType(final Long type) {
return (smRoot, query, cb) -> cb.equal(
smRoot.get(JpaSoftwareModule_.type).get(AbstractJpaBaseEntity_.id), type);
}
/**
* {@link Specification} for fetching {@link SoftwareModule}s type.
*
* @return the {@link SoftwareModule} {@link Specification}
*/
public static Specification<JpaSoftwareModule> fetchType() {
return (smRoot, query, cb) -> {
if (!query.getResultType().isAssignableFrom(Long.class)) {
smRoot.fetch(JpaSoftwareModule_.type);
}
return cb.conjunction();
};
}
}

View File

@@ -1,36 +0,0 @@
/**
* Copyright (c) 2022 Bosch.IO 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.specifications;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType_;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.springframework.data.jpa.domain.Specification;
/**
* Specifications class for {@link SoftwareModuleType}s. The class provides
* Spring Data JPQL Specifications.
*/
public class SoftwareModuleTypeSpecification {
private SoftwareModuleTypeSpecification() {
// utility class
}
/**
* {@link Specification} for retrieving {@link SoftwareModuleType}s with
* DELETED attribute <code>false</code> - i.e. is not deleted.
*
* @return the {@link SoftwareModuleType} {@link Specification}
*/
public static Specification<JpaSoftwareModuleType> isNotDeleted() {
return (root, query, cb) -> cb.equal(root.get(JpaSoftwareModuleType_.deleted), false);
}
}

View File

@@ -1,48 +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.specifications;
import jakarta.persistence.criteria.Join;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag_;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.springframework.data.jpa.domain.Specification;
/**
* Specifications class for {@link org.eclipse.hawkbit.repository.model.Tag}s.
* The class provides Spring Data JPQL Specifications.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class TagSpecification {
/**
* {@link Specification} for retrieving {@link DistributionSetTag}s by
* assigned {@link DistributionSet}.
*
* @param dsId of the distribution set
* @return the {@link JpaDistributionSetTag} {@link Specification}
*/
public static Specification<JpaDistributionSetTag> ofDistributionSet(final Long dsId) {
return (dsRoot, query, criteriaBuilder) -> {
final Join<JpaDistributionSetTag, JpaDistributionSet> tagJoin = dsRoot
.join(JpaDistributionSetTag_.assignedToDistributionSet);
query.distinct(true);
return criteriaBuilder.equal(tagJoin.get(AbstractJpaBaseEntity_.id), dsId);
};
}
}

View File

@@ -9,6 +9,8 @@
*/
package org.eclipse.hawkbit.repository.jpa.specifications;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery_;
import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -16,26 +18,11 @@ import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.springframework.data.jpa.domain.Specification;
/**
* Specifications class for {@link TargetFilterQuery}s. The class provides
* Spring Data JPQL Specifications.
* Specifications class for {@link TargetFilterQuery}s. The class provides Spring Data JPQL Specifications.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class TargetFilterQuerySpecification {
private TargetFilterQuerySpecification() {
// utility class
}
/**
* {@link Specification} for retrieving {@link JpaTargetFilterQuery}s based
* on is {@link JpaTargetFilterQuery#getQuery()}.
*
* @param queryValue the query of the filter
* @return the {@link JpaTargetFilterQuery} {@link Specification}
*/
public static Specification<JpaTargetFilterQuery> equalsQuery(final String queryValue) {
return (targetFilterQueryRoot, query, cb) -> cb.equal(targetFilterQueryRoot.get(JpaTargetFilterQuery_.query), queryValue);
}
/**
* {@link Specification} for retrieving {@link JpaTargetFilterQuery}s based
* on is {@link JpaTargetFilterQuery#getName()}.

View File

@@ -9,11 +9,9 @@
*/
package org.eclipse.hawkbit.repository.jpa.specifications;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.JoinType;
import jakarta.persistence.criteria.ListJoin;
@@ -27,7 +25,6 @@ import jakarta.validation.constraints.NotNull;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaNamedEntity_;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
@@ -108,16 +105,6 @@ public final class TargetSpecifications {
return (targetRoot, query, cb) -> targetRoot.get(AbstractJpaBaseEntity_.id).in(ids);
}
/**
* {@link Specification} for retrieving {@link Target}s that have the
* request controller attributes flag set
*
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> hasRequestControllerAttributesTrue() {
return (targetRoot, query, cb) -> cb.equal(targetRoot.get(JpaTarget_.requestControllerAttributes), true);
}
/**
* {@link Specification} for retrieving {@link JpaTarget}s including {@link JpaTarget#getAssignedDistributionSet()}.
*
@@ -132,26 +119,6 @@ public final class TargetSpecifications {
};
}
/**
* {@link Specification} for retrieving {@link Target}s by "equal to any given {@link TargetUpdateStatus}".
*
* @param updateStatus to be filtered on
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> hasTargetUpdateStatus(final Collection<TargetUpdateStatus> updateStatus) {
return (targetRoot, query, cb) -> targetRoot.get(JpaTarget_.updateStatus).in(updateStatus);
}
/**
* {@link Specification} for retrieving {@link Target}s by "equal to given {@link TargetUpdateStatus}".
*
* @param updateStatus to be filtered on
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> hasTargetUpdateStatus(final TargetUpdateStatus updateStatus) {
return (targetRoot, query, cb) -> cb.equal(targetRoot.get(JpaTarget_.updateStatus), updateStatus);
}
/**
* {@link Specification} for retrieving {@link Target}s by "not equal to given {@link TargetUpdateStatus}".
*
@@ -162,36 +129,6 @@ public final class TargetSpecifications {
return (targetRoot, query, cb) -> cb.not(cb.equal(targetRoot.get(JpaTarget_.updateStatus), updateStatus));
}
/**
* {@link Specification} for retrieving {@link Target}s that are overdue. A target is overdue if it did not respond during the configured
* intervals:<br>
* <em>poll_itvl + overdue_itvl</em>
*
* @param overdueTimestamp the calculated timestamp to compare with the last response of a target (lastTargetQuery).<br>
* The <code>overdueTimestamp</code> has to be calculated with the following expression:<br>
* <em>overdueTimestamp = nowTimestamp - poll_itvl - overdue_itvl</em>
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> isOverdue(final long overdueTimestamp) {
return (targetRoot, query, cb) ->
cb.lessThanOrEqualTo(targetRoot.get(JpaTarget_.lastTargetQuery), overdueTimestamp);
}
/**
* {@link Specification} for retrieving {@link Target}s by "like controllerId or like name".
*
* @param searchText to be filtered on
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> likeControllerIdOrName(final String searchText) {
return (targetRoot, query, cb) -> {
final String searchTextToLower = searchText.toLowerCase();
return cb.or(
cb.like(cb.lower(targetRoot.get(JpaTarget_.controllerId)), searchTextToLower),
cb.like(cb.lower(targetRoot.get(AbstractJpaNamedEntity_.name)), searchTextToLower));
};
}
public static Specification<JpaTarget> eqTargetGroup(final String targetGroup) {
return (targetRoot, query, criteriaBuilder) -> {
final String groupTextToLower = targetGroup.toLowerCase();
@@ -208,16 +145,6 @@ public final class TargetSpecifications {
};
}
/**
* {@link Specification} for retrieving {@link Target}s by "like controllerId".
*
* @param distributionId to be filtered on
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> hasInstalledOrAssignedDistributionSet(@NotNull final Long distributionId) {
return hasInstalledDistributionSet(distributionId).or(hasAssignedDistributionSet(distributionId));
}
/**
* Finds all targets by given {@link Target#getControllerId()}s and which are not yet assigned to given {@link DistributionSet}.
*
@@ -225,42 +152,14 @@ public final class TargetSpecifications {
* @param distributionId set that is not yet assigned
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> hasControllerIdAndAssignedDistributionSetIdNot(final List<String> tIDs,
@NotNull final Long distributionId) {
public static Specification<JpaTarget> hasControllerIdAndAssignedDistributionSetIdNot(
final List<String> tIDs, @NotNull final Long distributionId) {
return (targetRoot, query, cb) -> cb.and(targetRoot.get(JpaTarget_.controllerId).in(tIDs),
cb.or(
cb.notEqual(targetRoot.get(JpaTarget_.assignedDistributionSet).get(AbstractJpaBaseEntity_.id), distributionId),
cb.isNull(targetRoot.get(JpaTarget_.assignedDistributionSet))));
}
/**
* {@link Specification} for retrieving {@link Target}s based on a {@link TargetTag} name.
*
* @param tagName to search for
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> hasTagName(final String tagName) {
return (targetRoot, query, cb) -> {
final SetJoin<JpaTarget, JpaTargetTag> join = targetRoot.join(JpaTarget_.tags);
return cb.equal(join.get(AbstractJpaNamedEntity_.name), tagName);
};
}
/**
* {@link Specification} for retrieving {@link Target}s by "has no tag names"or "has at least on of the given tag names".
*
* @param tagNames to be filtered on
* @param selectTargetWithNoTag flag to get targets with no tag assigned
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> hasTags(final String[] tagNames, final Boolean selectTargetWithNoTag) {
return (targetRoot, query, cb) -> {
final Predicate predicate = getHasTagsPredicate(targetRoot, cb, selectTargetWithNoTag, tagNames);
query.distinct(true);
return predicate;
};
}
/**
* {@link Specification} for retrieving {@link Target}s by assigned distribution set.
*
@@ -297,7 +196,7 @@ public final class TargetSpecifications {
public static Specification<JpaTarget> isCompatibleWithDistributionSetType(final Long distributionSetTypeId) {
return (targetRoot, query, cb) -> {
// Since the targetRoot is changed by joining we need to get the isNull predicate first
final Predicate targetTypeIsNull = getTargetTypeIsNullPredicate(targetRoot);
final Predicate targetTypeIsNull = targetRoot.get(JpaTarget_.targetType).isNull();
return cb.or(targetTypeIsNull, cb.equal(getDsTypeIdPath(targetRoot), distributionSetTypeId));
};
}
@@ -409,36 +308,6 @@ public final class TargetSpecifications {
};
}
/**
* {@link Specification} for retrieving {@link Target}s by target type id
*
* @param typeId the id of the target type
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> hasTargetType(final long typeId) {
return (targetRoot, query, cb) -> cb.equal(targetRoot.get(JpaTarget_.targetType).get(AbstractJpaBaseEntity_.id), typeId);
}
/**
* {@link Specification} for retrieving {@link Target}s by target type id is equal to null
*
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> hasNoTargetType() {
return (targetRoot, query, cb) -> cb.isNull(targetRoot.get(JpaTarget_.targetType));
}
/**
* {@link Specification} for retrieving {@link Target}s that don't have target type assigned
*
* @param typeId the id of the target type
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> hasTargetTypeNot(final Long typeId) {
return (targetRoot, query, cb) -> cb.or(getTargetTypeIsNullPredicate(targetRoot),
cb.notEqual(targetRoot.get(JpaTarget_.targetType).get(AbstractJpaBaseEntity_.id), typeId));
}
public static Specification<JpaTarget> failedActionsForRollout(final String rolloutId) {
return (targetRoot, query, cb) -> {
final Join<JpaTarget, Action> targetActions = targetRoot.join("actions");
@@ -461,35 +330,6 @@ public final class TargetSpecifications {
};
}
private static Predicate getHasTagsPredicate(final Root<JpaTarget> targetRoot, final CriteriaBuilder cb,
final Boolean selectTargetWithNoTag, final String[] tagNames) {
final SetJoin<JpaTarget, JpaTargetTag> tags = targetRoot.join(JpaTarget_.tags, JoinType.LEFT);
final Path<String> exp = tags.get(AbstractJpaNamedEntity_.name);
final List<Predicate> hasTagsPredicates = new ArrayList<>();
if (isNoTagActive(selectTargetWithNoTag)) {
hasTagsPredicates.add(exp.isNull());
}
if (isAtLeastOneTagActive(tagNames)) {
hasTagsPredicates.add(exp.in((Object[]) tagNames));
}
return hasTagsPredicates.stream().reduce(cb::or)
.orElseThrow(() -> new RuntimeException("Neither NO_TAG, nor TAG target tag filter was provided!"));
}
private static boolean isNoTagActive(final Boolean selectTargetWithNoTag) {
return Boolean.TRUE.equals(selectTargetWithNoTag);
}
private static boolean isAtLeastOneTagActive(final String[] tagNames) {
return tagNames != null && tagNames.length > 0;
}
private static Predicate getTargetTypeIsNullPredicate(final Root<JpaTarget> targetRoot) {
return targetRoot.get(JpaTarget_.targetType).isNull();
}
private static Path<Long> getDsTypeIdPath(final Root<JpaTarget> root) {
final Join<JpaTarget, JpaTargetType> targetTypeJoin = root.join(JpaTarget_.targetType, JoinType.LEFT);
return targetTypeJoin.join(JpaTargetType_.distributionSetTypes, JoinType.LEFT).get(AbstractJpaBaseEntity_.id);

View File

@@ -31,16 +31,6 @@ import org.springframework.data.jpa.domain.Specification;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class TargetTypeSpecification {
/**
* {@link Specification} for retrieving {@link TargetType}s by controllerId
*
* @param ids to search for
* @return the {@link TargetType} {@link Specification}
*/
public static Specification<JpaTargetType> hasIdIn(final Collection<Long> ids) {
return (targetRoot, query, cb) -> targetRoot.get(AbstractJpaBaseEntity_.id).in(ids);
}
/**
* {@link Specification} for retrieving {@link TargetType}s based on a {@link DistributionSetType} name.
*
@@ -73,14 +63,4 @@ public final class TargetTypeSpecification {
public static Specification<JpaTargetType> hasName(final String name) {
return (targetRoot, query, cb) -> cb.equal(targetRoot.get(AbstractJpaNamedEntity_.name), name);
}
/**
* {@link Specification} for retrieving {@link TargetType}s by "like name".
*
* @param name to be filtered on
* @return the {@link TargetType} {@link Specification}
*/
public static Specification<JpaTargetType> likeName(final String name) {
return (targetRoot, query, cb) -> cb.like(cb.lower(targetRoot.get(AbstractJpaNamedEntity_.name)), name.toLowerCase());
}
}

View File

@@ -32,7 +32,9 @@ import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction_;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
@@ -58,14 +60,12 @@ 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.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetTypeAssignmentResult;
import org.eclipse.hawkbit.repository.test.TestConfiguration;
import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest;
import org.eclipse.hawkbit.repository.test.util.RolloutTestApprovalStrategy;
@@ -191,9 +191,8 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
return distributionSetManagement.unassignTag(sets.stream().map(DistributionSet::getId).toList(), tag.getId());
}
protected TargetTypeAssignmentResult initiateTypeAssignment(final Collection<Target> targets, final TargetType type) {
return targetManagement.assignType(
targets.stream().map(Target::getControllerId).toList(), type.getId());
protected void initiateTypeAssignment(final Collection<Target> targets, final TargetType type) {
targets.stream().map(Target::getControllerId).forEach(id -> targetManagement.assignType(id, type.getId()));
}
protected void assertRollout(final Rollout rollout, final boolean dynamic, final Rollout.RolloutStatus status, final int groupCreated,
@@ -312,6 +311,10 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest
return rolloutGroupRepository.findById(group.getId()).get();
}
protected static Specification<JpaAction> byDistributionSetId(final Long distributionSetId) {
return (root, query, cb) -> cb.equal(root.get(JpaAction_.distributionSet).get(AbstractJpaBaseEntity_.id), distributionSetId);
}
private JpaRollout refresh(final Rollout rollout) {
return rolloutRepository.findById(rollout.getId()).get();
}

View File

@@ -24,7 +24,6 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.eclipse.hawkbit.repository.FilterParams;
import org.eclipse.hawkbit.repository.Identifiable;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement.AutoAssignDistributionSetUpdate;
@@ -76,10 +75,6 @@ class TargetAccessControllerTest extends AbstractJpaIntegrationTest {
assertThat(targetManagement.findByRsql("id==*", Pageable.unpaged()).get().map(Identifiable::getId).toList())
.containsOnly(permittedTarget.getId());
// verify targetManagement#findByUpdateStatus
assertThat(targetManagement.findByUpdateStatus(TargetUpdateStatus.REGISTERED, Pageable.unpaged()).get()
.map(Identifiable::getId).toList()).containsOnly(permittedTarget.getId());
// verify targetManagement#getByControllerID
assertThat(targetManagement.getByControllerId(permittedTarget.getControllerId())).isPresent();
final String hiddenTargetControllerId = hiddenTarget.getControllerId();
@@ -106,19 +101,6 @@ class TargetAccessControllerTest extends AbstractJpaIntegrationTest {
.as("Target should not be found.")
.isInstanceOf(InsufficientPermissionException.class);
});
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
.create(TargetFilterQueryManagement.Create.builder().name("test").query("id==*").build());
runAs(withUser("user", READ_TARGET + "/controllerId==" + permittedTarget.getControllerId()), () -> {
// verify targetManagement#findByTargetFilterQuery
assertThat(targetManagement.findByTargetFilterQuery(targetFilterQuery.getId(), Pageable.unpaged()).get()
.map(Identifiable::getId).toList()).containsOnly(permittedTarget.getId());
// verify targetManagement#findByTargetFilterQuery (used by UI)
assertThat(targetManagement.findByFilters(new FilterParams(null, null, null, null), Pageable.unpaged()).get()
.map(Identifiable::getId).toList()).containsOnly(permittedTarget.getId());
});
}
@Test
@@ -208,11 +190,6 @@ class TargetAccessControllerTest extends AbstractJpaIntegrationTest {
.create(Create.builder().controllerId("device02").updateStatus(TargetUpdateStatus.REGISTERED).build())
.getControllerId();
runAs(withUser("user", READ_TARGET + "/controllerId==" + permittedTarget.getControllerId()), () ->
// verify targetManagement#findByUpdateStatus before assignment
assertThat(targetManagement.findByUpdateStatus(TargetUpdateStatus.REGISTERED, Pageable.unpaged()).get()
.map(Identifiable::getId).toList()).containsOnly(permittedTarget.getId()));
runAs(withUser("user",
READ_TARGET + "/controllerId==" + permittedTarget.getControllerId(),
UPDATE_TARGET + "/controllerId==" + permittedTarget.getControllerId(),
@@ -221,14 +198,6 @@ class TargetAccessControllerTest extends AbstractJpaIntegrationTest {
assertThat(assignDistributionSet(dsId, permittedTarget.getControllerId()).getAssigned()).isEqualTo(1);
// assigning of not allowed target behaves as not found
assertThatThrownBy(() -> assignDistributionSet(dsId, hiddenTargetControllerId)).isInstanceOf(AssertionError.class);
// verify targetManagement#findByUpdateStatus(REGISTERED) after assignment
assertThat(targetManagement.findByUpdateStatus(TargetUpdateStatus.REGISTERED, Pageable.unpaged())
.getTotalElements()).isZero();
// verify targetManagement#findByUpdateStatus(PENDING) after assignment
assertThat(targetManagement.findByUpdateStatus(TargetUpdateStatus.PENDING, Pageable.unpaged()).get()
.map(Identifiable::getId).toList()).containsOnly(permittedTarget.getId());
});
}

View File

@@ -145,7 +145,7 @@ class AutoAssignCheckerIntTest extends AbstractJpaIntegrationTest {
verifyThatTargetsHaveDistributionSetAssignment(setB, targets.subList(10, 20), targetsCount);
// Count the number of targets that will be assigned with setA
assertThat(targetManagement.countByRsqlAndNonDSAndCompatibleAndUpdatable(setA.getId(), targetFilterQuery.getQuery()))
assertThat(targetManagement.countByRsqlAndNonDsAndCompatibleAndUpdatable(setA.getId(), targetFilterQuery.getQuery()))
.isEqualTo(15);
// Run the check
@@ -405,7 +405,7 @@ class AutoAssignCheckerIntTest extends AbstractJpaIntegrationTest {
final List<Long> compatibleTargets = Stream
.of(compatibleTargetsSingleType, compatibleTargetsMultiType, compatibleTargetsWithoutType)
.flatMap(Collection::stream).map(Target::getId).toList();
final long compatibleCount = targetManagement.countByRsqlAndNonDSAndCompatibleAndUpdatable(testDs.getId(),
final long compatibleCount = targetManagement.countByRsqlAndNonDsAndCompatibleAndUpdatable(testDs.getId(),
testFilter.getQuery());
assertThat(compatibleCount).isEqualTo(compatibleTargets.size());
@@ -498,7 +498,7 @@ class AutoAssignCheckerIntTest extends AbstractJpaIntegrationTest {
private Slice<Action> findActionsByDistributionSet(final Pageable pageable, final long distributionSetId) {
return actionRepository
.findAll(ActionSpecifications.byDistributionSetId(distributionSetId), pageable)
.findAll(byDistributionSetId(distributionSetId), pageable)
.map(Action.class::cast);
}
}

View File

@@ -95,7 +95,7 @@ class RepositoryEntityEventTest extends AbstractJpaIntegrationTest {
void targetDeletedEventIsPublished() throws InterruptedException {
final Target createdTarget = testdataFactory.createTarget("12345");
targetManagement.deleteByControllerID("12345");
targetManagement.deleteByControllerId("12345");
final TargetDeletedEvent targetDeletedEvent = eventListener.waitForEvent(TargetDeletedEvent.class);
assertThat(targetDeletedEvent).isNotNull();

View File

@@ -1788,7 +1788,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
distributionSetManagement.get(distributionSetId).orElseThrow(() ->
new EntityNotFoundException(DistributionSet.class, distributionSetId));
return actionRepository
.findAll(ActionSpecifications.byDistributionSetId(distributionSetId), pageable)
.findAll(byDistributionSetId(distributionSetId), pageable)
.map(Action.class::cast);
}

View File

@@ -958,11 +958,12 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
expectedTargetCountStatus.put(TotalTargetCountStatus.Status.FINISHED, 9L);
validateRolloutActionStatus(rolloutTwo.getId(), expectedTargetCountStatus);
changeStatusForAllRunningActions(rolloutTwo, Status.FINISHED);
final Page<Target> targetPage = targetManagement.findByUpdateStatus(TargetUpdateStatus.IN_SYNC, PAGE);
final List<Target> targetList = targetPage.getContent();
final List<? extends Target> targetList = findByUpdateStatus(TargetUpdateStatus.IN_SYNC, PAGE);
// 15 targets in finished/IN_SYNC status and same DS assigned
assertThat(targetList).hasSize(amountTargetsForRollout);
targetList.stream().map(Target::getControllerId).map(deploymentManagement::getAssignedDistributionSet)
targetList.stream()
.map(Target::getControllerId)
.map(deploymentManagement::getAssignedDistributionSet)
.forEach(d -> assertThat(d).contains(distributionSet));
}

View File

@@ -12,25 +12,18 @@ package org.eclipse.hawkbit.repository.jpa.management;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.FilterParams;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.TargetTagManagement;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.junit.jupiter.api.Test;
/**
@@ -39,157 +32,6 @@ import org.junit.jupiter.api.Test;
*/
class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
private static final String SPACE_AND_DESCRIPTION = " description";
/**
* Verifies that targets with given target type are returned from repository.
*/
@Test
void findTargetByTargetType() {
final TargetType testType = testdataFactory.createTargetType("testType", Set.of(standardDsType));
final List<Target> unassigned = testdataFactory.createTargets(9, "unassigned");
final List<Target> assigned = testdataFactory.createTargetsWithType(11, "assigned", testType);
assertThat(targetManagement.findByFilters(new FilterParams(null, null, false, testType.getId()), PAGE))
.as("Contains the targets with set type").containsAll(assigned)
.as("and that means the following expected amount").hasSize(11);
assertThat(targetManagement.countByFilters(new FilterParams(null, null, false, testType.getId())))
.as("Count the targets with set type").isEqualTo(11);
assertThat(targetManagement.findByFilters(new FilterParams(null, null, true, null), PAGE))
.as("Contains the targets without a type").containsAll(unassigned)
.as("and that means the following expected amount").hasSize(9);
assertThat(targetManagement.countByFilters(new FilterParams(null, null, true, null)))
.as("Counts the targets without a type").isEqualTo(9);
}
/**
* Tests different parameter combinations for target search operations.
* and query definitions by RSQL (named and un-named).
*/
@Test
void targetSearchWithVariousFilterCombinations() {
final TargetTag targTagX = targetTagManagement.create(TargetTagManagement.Create.builder().name("TargTag-X").build());
final TargetTag targTagY = targetTagManagement.create(TargetTagManagement.Create.builder().name("TargTag-Y").build());
final TargetTag targTagZ = targetTagManagement.create(TargetTagManagement.Create.builder().name("TargTag-Z").build());
final TargetTag targTagW = targetTagManagement.create(TargetTagManagement.Create.builder().name("TargTag-W").build());
final DistributionSet setA = testdataFactory.createDistributionSet("A");
final DistributionSet setB = testdataFactory.createDistributionSet("B");
final TargetType targetTypeX = testdataFactory.createTargetType("TargetTypeX", Set.of(setB.getType()));
final DistributionSet installedSet = testdataFactory.createDistributionSet("another");
final Long lastTargetQueryNotOverdue = System.currentTimeMillis();
final Long lastTargetQueryAlwaysOverdue = 0L;
final String targetDsAIdPref = "targ-A";
List<Target> targAs = testdataFactory.createTargets(100, targetDsAIdPref,
targetDsAIdPref.concat(SPACE_AND_DESCRIPTION), lastTargetQueryNotOverdue);
targAs = assignTag(targAs, targTagX);
final Target targSpecialName = targetManagement
.update(TargetManagement.Update.builder().id(targAs.get(0).getId()).name("targ-A-special").build());
final String targetDsBIdPref = "targ-B";
List<Target> targBs = testdataFactory.createTargets(100, targetDsBIdPref,
targetDsBIdPref.concat(SPACE_AND_DESCRIPTION), lastTargetQueryAlwaysOverdue);
targBs = assignTag(targBs, targTagY);
targBs = assignTag(targBs, targTagW);
final String targetDsCIdPref = "targ-C";
List<Target> targCs = testdataFactory.createTargets(100, targetDsCIdPref,
targetDsCIdPref.concat(SPACE_AND_DESCRIPTION), lastTargetQueryAlwaysOverdue);
targCs = assignTag(targCs, targTagZ);
targCs = assignTag(targCs, targTagW);
final String targetDsDIdPref = "targ-D";
final List<Target> targDs = testdataFactory.createTargets(100, targetDsDIdPref,
targetDsDIdPref.concat(SPACE_AND_DESCRIPTION), null);
final String targetDsEIdPref = "targ-E";
final List<Target> targEs = testdataFactory.createTargetsWithType(100, targetDsEIdPref, targetTypeX);
final String assignedC = targCs.iterator().next().getControllerId();
assignDistributionSet(setA.getId(), assignedC);
final String assignedA = targAs.iterator().next().getControllerId();
assignDistributionSet(setA.getId(), assignedA);
final String assignedB = targBs.iterator().next().getControllerId();
assignDistributionSet(setA.getId(), assignedB);
final String assignedE = targEs.iterator().next().getControllerId();
assignDistributionSet(setB.getId(), assignedE);
final String installedC = targCs.iterator().next().getControllerId();
final Long actionId = getFirstAssignedActionId(assignDistributionSet(installedSet.getId(), assignedC));
// set one installed DS also
controllerManagement.addUpdateActionStatus(
entityFactory.actionStatus().create(actionId).status(Status.FINISHED).message("message"));
assignDistributionSet(setA.getId(), installedC);
final List<TargetUpdateStatus> unknown = List.of(TargetUpdateStatus.UNKNOWN);
final List<TargetUpdateStatus> pending = List.of(TargetUpdateStatus.PENDING);
final List<TargetUpdateStatus> both = List.of(TargetUpdateStatus.UNKNOWN, TargetUpdateStatus.PENDING);
// get final updated version of targets
targAs = targetManagement.getByControllerId(targAs.stream().map(Target::getControllerId).toList());
targBs = targetManagement.getByControllerId(targBs.stream().map(Target::getControllerId).toList());
targCs = targetManagement.getByControllerId(targCs.stream().map(Target::getControllerId).toList());
// try to find several targets with different filter settings
verifyThat1TargetHasNameAndId("targ-A-special", targSpecialName.getControllerId());
verifyThatRepositoryContains500Targets();
verifyThat200TargetsHaveTagD(targTagW, concat(targBs, targCs));
verifyThat100TargetsContainsGivenTextAndHaveTagAssigned(targTagY, targTagW, targBs);
verifyThat1TargetHasTagHasDescOrNameAndDs(targTagW, setA, targetManagement.getByControllerId(assignedC).get());
verifyThat0TargetsWithTagAndDescOrNameHasDS(targTagW, setA);
verifyThat0TargetsWithNameOrdescAndDSHaveTag(targTagX, setA);
verifyThat3TargetsHaveDSAssigned(setA, targetManagement.getByControllerId(Arrays.asList(assignedA, assignedB, assignedC)));
verifyThat1TargetWithDescOrNameHasDS(setA, targetManagement.getByControllerId(assignedA).get());
List<Target> expected = concat(targAs, targBs, targCs, targDs);
expected.removeAll(targetManagement.getByControllerId(Arrays.asList(assignedA, assignedB, assignedC)));
verifyThat496TargetsAreInStatusUnknown(unknown, expected);
expected = concat(targBs, targCs);
expected.removeAll(targetManagement.getByControllerId(Arrays.asList(assignedB, assignedC)));
verifyThat198TargetsAreInStatusUnknownAndHaveGivenTags(targTagY, targTagW, unknown, expected);
verifyThat0TargetsAreInStatusUnknownAndHaveDSAssigned(setA, unknown);
expected = concat(targAs);
expected.remove(targetManagement.getByControllerId(assignedA).get());
verifyThat99TargetsWithNameOrDescriptionAreInGivenStatus(unknown, expected);
expected = concat(targBs);
expected.remove(targetManagement.getByControllerId(assignedB).get());
verifyThat99TargetsWithGivenNameOrDescAndTagAreInStatusUnknown(targTagW, unknown, expected);
verifyThat4TargetsAreInStatusPending(pending,
targetManagement.getByControllerId(Arrays.asList(assignedA, assignedB, assignedC, assignedE)));
verifyThat3TargetsWithGivenDSAreInPending(setA, pending,
targetManagement.getByControllerId(Arrays.asList(assignedA, assignedB, assignedC)));
verifyThat1TargetWithGivenNameOrDescAndDSIsInPending(setA, pending,
targetManagement.getByControllerId(assignedA).get());
verifyThat1TargetWithGivenNameOrDescAndTagAndDSIsInPending(targTagW, setA, pending,
targetManagement.getByControllerId(assignedB).get());
verifyThat2TargetsWithGivenTagAndDSIsInPending(targTagW, setA, pending,
targetManagement.getByControllerId(Arrays.asList(assignedB, assignedC)));
verifyThat2TargetsWithGivenTagAreInPending(targTagW, pending,
targetManagement.getByControllerId(Arrays.asList(assignedB, assignedC)));
verifyThat200targetsWithGivenTagAreInStatusPendingOrUnknown(targTagW, both, concat(targBs, targCs));
verifyThat1TargetAIsInStatusPendingAndHasDSInstalled(installedSet, pending,
targetManagement.getByControllerId(installedC).get());
verifyThat1TargetHasTypeAndDSAssigned(targetTypeX, setB, targetManagement.getByControllerId(assignedE).get());
verifyThatTargetsHasNoTypeAndDSAssignedOrInstalled(setA,
targetManagement.getByControllerId(Arrays.asList(assignedA, assignedB, assignedC)));
verifyThatTargetsHasNoTypeAndDSAssignedOrInstalled(installedSet,
targetManagement.getByControllerId(Collections.singletonList(installedC)));
verifyThat100TargetsContainsGivenTextAndHaveTypeAssigned(targetTypeX, targEs);
verifyThat400TargetsContainsGivenTextAndHaveNoTypeAssigned(concat(targAs, targBs, targCs, targDs));
expected = concat(targBs, targCs);
expected.removeAll(targetManagement.getByControllerId(Arrays.asList(assignedB, assignedC)));
verifyThat198TargetsAreInStatusUnknownAndOverdue(unknown, expected);
}
/**
* Verifies that targets with given assigned DS are returned from repository.
*/
@@ -305,358 +147,4 @@ class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
.containsExactlyInAnyOrderElementsOf(testTargets).as("does not contain incompatible targets")
.doesNotContainAnyElementsOf(targetsWithIncompatibleType);
}
private void verifyThat1TargetAIsInStatusPendingAndHasDSInstalled(final DistributionSet installedSet,
final List<TargetUpdateStatus> pending, final Target expected) {
final FilterParams filterParams = new FilterParams(pending, null, null, installedSet.getId(), Boolean.FALSE);
final String query = "updatestatus==pending and installedds.name==" + installedSet.getName();
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(1).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsExactly(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat200targetsWithGivenTagAreInStatusPendingOrUnknown(final TargetTag targTagW,
final List<TargetUpdateStatus> both, final List<Target> expected) {
final FilterParams filterParams = new FilterParams(both, null, null, null, Boolean.FALSE, targTagW.getName());
final String query = "(updatestatus==pending or updatestatus==unknown) and tag==" + targTagW.getName();
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(200).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat2TargetsWithGivenTagAreInPending(final TargetTag targTagW,
final List<TargetUpdateStatus> pending, final List<Target> expected) {
final FilterParams filterParams = new FilterParams(pending, null, null, null, Boolean.FALSE,
targTagW.getName());
final String query = "updatestatus==pending and tag==" + targTagW.getName();
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(2).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat2TargetsWithGivenTagAndDSIsInPending(final TargetTag targTagW, final DistributionSet setA,
final List<TargetUpdateStatus> pending, final List<Target> expected) {
final FilterParams filterParams = new FilterParams(pending, null, null, setA.getId(), Boolean.FALSE,
targTagW.getName());
final String query = "updatestatus==pending and (assignedds.name==" + setA.getName() + " or installedds.name=="
+ setA.getName() + ") and tag==" + targTagW.getName();
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(2).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat1TargetWithGivenNameOrDescAndTagAndDSIsInPending(final TargetTag targTagW,
final DistributionSet setA, final List<TargetUpdateStatus> pending, final Target expected) {
final FilterParams filterParams = new FilterParams(pending, null, "%targ-B%", setA.getId(), Boolean.FALSE,
targTagW.getName());
final String query = "updatestatus==pending and (assignedds.name==" + setA.getName() + " or installedds.name=="
+ setA.getName() + ") and (name==*targ-B* or description==*targ-B*) and tag==" + targTagW.getName();
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(1).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsExactly(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat1TargetWithGivenNameOrDescAndDSIsInPending(final DistributionSet setA,
final List<TargetUpdateStatus> pending, final Target expected) {
final FilterParams filterParams = new FilterParams(pending, null, "%targ-A%", setA.getId(), Boolean.FALSE);
final String query = "updatestatus==pending and (assignedds.name==" + setA.getName() + " or installedds.name=="
+ setA.getName() + ") and (name==*targ-A* or description==*targ-A*)";
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(1).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsExactly(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat3TargetsWithGivenDSAreInPending(final DistributionSet setA,
final List<TargetUpdateStatus> pending, final List<Target> expected) {
final FilterParams filterParams = new FilterParams(pending, null, null, setA.getId(), Boolean.FALSE);
final String query = "updatestatus==pending and (assignedds.name==" + setA.getName() + " or installedds.name=="
+ setA.getName() + ")";
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(3).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat4TargetsAreInStatusPending(final List<TargetUpdateStatus> pending,
final List<Target> expected) {
final FilterParams filterParams = new FilterParams(pending, null, null, null, Boolean.FALSE);
final String query = "updatestatus==pending";
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(4).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat99TargetsWithGivenNameOrDescAndTagAreInStatusUnknown(final TargetTag targTagW,
final List<TargetUpdateStatus> unknown, final List<Target> expected) {
final FilterParams filterParams = new FilterParams(unknown, null, "%targ-B%", null, Boolean.FALSE,
targTagW.getName());
final String query = "updatestatus==unknown and (name==*targ-B* or description==*targ-B*) and tag=="
+ targTagW.getName();
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(99).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat99TargetsWithNameOrDescriptionAreInGivenStatus(final List<TargetUpdateStatus> unknown,
final List<Target> expected) {
final FilterParams filterParams = new FilterParams(unknown, null, "%targ-A%", null, Boolean.FALSE);
final String query = "updatestatus==unknown and (name==*targ-A* or description==*targ-A*)";
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(99).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat0TargetsAreInStatusUnknownAndHaveDSAssigned(final DistributionSet setA,
final List<TargetUpdateStatus> unknown) {
final FilterParams filterParams = new FilterParams(unknown, null, null, setA.getId(), Boolean.FALSE);
final String query = "updatestatus==unknown and (assignedds.name==" + setA.getName() + " or installedds.name=="
+ setA.getName() + ")";
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent())
.as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and filter query returns the same result")
.hasSize(targetManagement.findByRsql(query, PAGE).getContent().size())
.as("has number of elements")
.isEmpty();
}
private void verifyThat198TargetsAreInStatusUnknownAndHaveGivenTags(final TargetTag targTagY,
final TargetTag targTagW, final List<TargetUpdateStatus> unknown, final List<Target> expected) {
final FilterParams filterParams = new FilterParams(unknown, null, null, null, Boolean.FALSE, targTagY.getName(),
targTagW.getName());
final String query = "updatestatus==unknown and (tag==" + targTagY.getName() + " or tag==" + targTagW.getName()
+ ")";
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(198).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat496TargetsAreInStatusUnknown(final List<TargetUpdateStatus> unknown,
final List<Target> expected) {
final FilterParams filterParams = new FilterParams(unknown, null, null, null, Boolean.FALSE);
final String query = "updatestatus==unknown";
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(496).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat198TargetsAreInStatusUnknownAndOverdue(final List<TargetUpdateStatus> unknown,
final List<Target> expected) {
final FilterParams filterParams = new FilterParams(unknown, Boolean.TRUE, null, null, Boolean.FALSE);
// be careful: simple filters are concatenated using AND-gating
final String query = "lastcontrollerrequestat=le=${overdue_ts};updatestatus==UNKNOWN";
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(198).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat1TargetWithDescOrNameHasDS(final DistributionSet setA, final Target expected) {
final FilterParams filterParams = new FilterParams(null, null, "%targ-A%", setA.getId(), Boolean.FALSE);
final String query = "(name==*targ-A* or description==*targ-A*) and (assignedds.name==" + setA.getName()
+ " or installedds.name==" + setA.getName() + ")";
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(1).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsExactly(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat3TargetsHaveDSAssigned(final DistributionSet setA, final List<Target> expected) {
final FilterParams filterParams = new FilterParams(null, null, null, setA.getId(), Boolean.FALSE);
final String query = "assignedds.name==" + setA.getName() + " or installedds.name==" + setA.getName();
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(3).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat0TargetsWithNameOrdescAndDSHaveTag(final TargetTag targTagX, final DistributionSet setA) {
final FilterParams filterParams = new FilterParams(null, null, "%targ-C%", setA.getId(), Boolean.FALSE,
targTagX.getName());
final String query = "(name==*targ-C* or description==*targ-C*) and tag==" + targTagX.getName()
+ " and (assignedds.name==" + setA.getName() + " or installedds.name==" + setA.getName() + ")";
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent())
.as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and filter query returns the same result")
.hasSize(targetManagement.findByRsql(query, PAGE).getContent().size())
.as("has number of elements")
.isEmpty();
}
private void verifyThat0TargetsWithTagAndDescOrNameHasDS(final TargetTag targTagW, final DistributionSet setA) {
final FilterParams filterParams = new FilterParams(null, null, "%targ-A%", setA.getId(), Boolean.FALSE,
targTagW.getName());
final String query = "(name==*targ-A* or description==*targ-A*) and tag==" + targTagW.getName()
+ " and (assignedds.name==" + setA.getName() + " or installedds.name==" + setA.getName() + ")";
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent())
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and filter query returns the same result")
.hasSize(targetManagement.findByRsql(query, PAGE).getContent().size())
.as("has number of elements")
.isEmpty();
}
private void verifyThat1TargetHasTagHasDescOrNameAndDs(final TargetTag targTagW, final DistributionSet setA,
final Target expected) {
final FilterParams filterParams = new FilterParams(null, null, "%targ-C%", setA.getId(), Boolean.FALSE,
targTagW.getName());
final String query = "(name==*targ-c* or description==*targ-C*) and tag==" + targTagW.getName()
+ " and (assignedds.name==" + setA.getName() + " or installedds.name==" + setA.getName() + ")";
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(1).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsExactly(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThat1TargetHasNameAndId(final String name, final String controllerId) {
final FilterParams filterParamsByName = new FilterParams(null, null, name, null, Boolean.FALSE);
assertThat(targetManagement.findByFilters(filterParamsByName, PAGE).getContent()).as("has number of elements")
.hasSize(1).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParamsByName));
final FilterParams filterParamsByControllerId = new FilterParams(null, null, controllerId, null, Boolean.FALSE);
assertThat(targetManagement.findByFilters(filterParamsByControllerId, PAGE).getContent())
.as("has number of elements").hasSize(1).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParamsByControllerId));
}
private void verifyThat100TargetsContainsGivenTextAndHaveTagAssigned(final TargetTag targTagY,
final TargetTag targTagW, final List<Target> expected) {
final FilterParams filterParams = new FilterParams(null, null, "%targ-B%", null, Boolean.FALSE,
targTagY.getName(), targTagW.getName());
final String query = "(name==*targ-B* or description==*targ-B*) and (tag==" + targTagY.getName() + " or tag=="
+ targTagW.getName() + ")";
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(100).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
@SafeVarargs
private List<Target> concat(final List<Target>... targets) {
final List<Target> result = new ArrayList<>();
Arrays.asList(targets).forEach(result::addAll);
return result;
}
private void verifyThat200TargetsHaveTagD(final TargetTag targTagD, final List<Target> expected) {
final FilterParams filterParams = new FilterParams(null, null, null, null, Boolean.FALSE, targTagD.getName());
final String query = "tag==" + targTagD.getName();
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("Expected number of results is")
.hasSize(200).as("and is expected number of results is equal to ")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected)
.as("and filter query returns the same result")
.containsAll(targetManagement.findByRsql(query, PAGE).getContent());
}
private void verifyThatRepositoryContains500Targets() {
final FilterParams filterParams = new FilterParams(null, null, null, null, null);
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent())
.as("Overall we expect that many targets in the repository").hasSize(500)
.as("which is also reflected by repository count").hasSize((int) targetManagement.count())
.as("which is also reflected by call without specification")
.containsAll(targetManagement.findAll(PAGE).getContent());
}
private void verifyThat1TargetHasTypeAndDSAssigned(final TargetType type, final DistributionSet set,
final Target expected) {
final FilterParams filterParams = new FilterParams(null, set.getId(), Boolean.FALSE, type.getId());
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(1).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsExactly(expected);
}
private void verifyThatTargetsHasNoTypeAndDSAssignedOrInstalled(final DistributionSet set,
final List<Target> expected) {
final FilterParams filterParams = new FilterParams(null, set.getId(), Boolean.TRUE, null);
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(expected.size()).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected);
}
private void verifyThat100TargetsContainsGivenTextAndHaveTypeAssigned(final TargetType targetType,
final List<Target> expected) {
final FilterParams filterParams = new FilterParams("%targ-E%", null, Boolean.FALSE, targetType.getId());
final List<Target> filteredTargets = targetManagement.findByFilters(filterParams, PAGE).getContent();
assertThat(filteredTargets).as("has number of elements").hasSize(100)
.as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams));
// Comparing the controller ids, as one of the targets was modified, so
// a 1:1
// comparison of the objects is not possible
assertThat(filteredTargets.stream().map(Target::getControllerId).toList())
.containsAll(expected.stream().map(Target::getControllerId).toList());
}
private void verifyThat400TargetsContainsGivenTextAndHaveNoTypeAssigned(final List<Target> expected) {
final FilterParams filterParams = new FilterParams("%targ-%", null, Boolean.TRUE, null);
assertThat(targetManagement.findByFilters(filterParams, PAGE).getContent()).as("has number of elements")
.hasSize(400).as("that number is also returned by count query")
.hasSize((int) targetManagement.countByFilters(filterParams))
.as("and contains the following elements").containsAll(expected);
}
}

View File

@@ -14,11 +14,9 @@ import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.FilterParams;
import org.eclipse.hawkbit.repository.TargetManagement.Create;
import org.eclipse.hawkbit.repository.TargetManagement.Update;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.junit.jupiter.api.Test;
@@ -29,42 +27,6 @@ import org.junit.jupiter.api.Test;
@Slf4j
class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void countByAssignedDistributionSetPermissionsCheck() {
assertPermissions(() -> targetManagement.countByAssignedDistributionSet(1L),
List.of(SpPermission.READ_TARGET, SpPermission.READ_REPOSITORY));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void countByFiltersPermissionsCheck() {
assertPermissions(() -> targetManagement.countByFilters(new FilterParams(null, null, null, null, null, null)),
List.of(SpPermission.READ_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void countByInstalledDistributionSetPermissionsCheck() {
assertPermissions(() -> targetManagement.countByInstalledDistributionSet(1L),
List.of(SpPermission.READ_TARGET, SpPermission.READ_REPOSITORY));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void existsByInstalledOrAssignedDistributionSetPermissionsCheck() {
assertPermissions(() -> targetManagement.existsByInstalledOrAssignedDistributionSet(1L),
List.of(SpPermission.READ_TARGET, SpPermission.READ_REPOSITORY));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@@ -73,14 +35,6 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
assertPermissions(() -> targetManagement.countByRsql("controllerId==id"), List.of(SpPermission.READ_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void countByRsqlAndUpdatablePermissionsCheck() {
assertPermissions(() -> targetManagement.countByRsqlAndUpdatable("controllerId==id"), List.of(SpPermission.READ_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@@ -138,21 +92,13 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void deleteByControllerIDPermissionsCheck() {
void deleteByControllerIdPermissionsCheck() {
assertPermissions(() -> {
targetManagement.deleteByControllerID("controllerId");
targetManagement.deleteByControllerId("controllerId");
return null;
}, List.of(SpPermission.DELETE_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void countByTargetFilterQueryPermissionsCheck() {
assertPermissions(() -> targetManagement.countByTargetFilterQuery(1L), List.of(SpPermission.READ_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@@ -166,8 +112,8 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void countByRsqlAndNonDSAndCompatibleAndUpdatablePermissionsCheck() {
assertPermissions(() -> targetManagement.countByRsqlAndNonDSAndCompatibleAndUpdatable(1L, "controllerId==id"),
void countByRsqlAndNonDsAndCompatibleAndUpdatablePermissionsCheck() {
assertPermissions(() -> targetManagement.countByRsqlAndNonDsAndCompatibleAndUpdatable(1L, "controllerId==id"),
List.of(SpPermission.READ_TARGET, SpPermission.READ_REPOSITORY));
}
@@ -262,15 +208,6 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
assertPermissions(() -> targetManagement.getByControllerId("controllerId"), List.of(SpPermission.READ_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void findByFiltersPermissionsCheck() {
assertPermissions(() -> targetManagement.findByFilters(new FilterParams(null, null, null, null, null, null), PAGE),
List.of(SpPermission.READ_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@@ -289,14 +226,6 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
List.of(SpPermission.READ_TARGET, SpPermission.READ_REPOSITORY));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void findByUpdateStatusPermissionsCheck() {
assertPermissions(() -> targetManagement.findByUpdateStatus(TargetUpdateStatus.IN_SYNC, PAGE), List.of(SpPermission.READ_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@@ -313,14 +242,6 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
assertPermissions(() -> targetManagement.findByRsql("controllerId==id", PAGE), List.of(SpPermission.READ_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void findByTargetFilterQueryPermissionsCheck() {
assertPermissions(() -> targetManagement.findByTargetFilterQuery(1L, PAGE), List.of(SpPermission.READ_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@@ -337,14 +258,6 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
assertPermissions(() -> targetManagement.findByRsqlAndTag("controllerId==id", 1L, PAGE), List.of(SpPermission.READ_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void assignTypePermissionsCheck() {
assertPermissions(() -> targetManagement.assignType(List.of("controllerId"), 1L), List.of(SpPermission.UPDATE_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@@ -388,14 +301,6 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
List.of(SpPermission.UPDATE_TARGET, SpPermission.READ_REPOSITORY));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void unassignTypePermissionsCheck() {
assertPermissions(() -> targetManagement.unassignType(List.of("controllerId")), List.of(SpPermission.UPDATE_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@@ -428,14 +333,6 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
assertPermissions(() -> targetManagement.get(List.of(1L)), List.of(SpPermission.READ_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void existsByControllerIdPermissionsCheck() {
assertPermissions(() -> targetManagement.existsByControllerId("controllerId"), List.of(SpPermission.READ_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@@ -462,33 +359,6 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
assertPermissions(() -> targetManagement.getControllerAttributes("controllerId"), List.of(SpPermission.READ_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void requestControllerAttributesPermissionsCheck() {
assertPermissions(() -> {
targetManagement.requestControllerAttributes("controllerId");
return null;
}, List.of(SpPermission.UPDATE_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void isControllerAttributesRequestedPermissionsCheck() {
assertPermissions(() -> targetManagement.isControllerAttributesRequested("controllerId"), List.of(SpPermission.READ_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/
@Test
void findByControllerAttributesRequestedPermissionsCheck() {
assertPermissions(() -> targetManagement.findByControllerAttributesRequested(PAGE), List.of(SpPermission.READ_TARGET));
}
/**
* Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.
*/

View File

@@ -26,13 +26,18 @@ import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.Set;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.JoinType;
import jakarta.persistence.criteria.Path;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.criteria.SetJoin;
import jakarta.validation.ConstraintViolationException;
import org.assertj.core.api.Assertions;
import org.awaitility.Awaitility;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.im.authentication.SpRole;
import org.eclipse.hawkbit.repository.FilterParams;
import org.eclipse.hawkbit.repository.Identifiable;
import org.eclipse.hawkbit.repository.TargetManagement.Create;
import org.eclipse.hawkbit.repository.TargetManagement.Update;
@@ -54,12 +59,16 @@ import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InvalidTargetAddressException;
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.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaNamedEntity_;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -70,9 +79,7 @@ import org.eclipse.hawkbit.repository.model.Tag;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetTypeAssignmentResult;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility;
import org.eclipse.hawkbit.repository.test.matcher.Expect;
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
import org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch;
@@ -81,6 +88,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.domain.Specification;
/**
* Feature: Component Tests - Repository<br/>
@@ -91,7 +99,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
private static final String WHITESPACE_ERROR = "target with whitespaces in controller id should not be created";
/**
* Verifies that management get access react as specified on calls for non existing entities by means
* Verifies that management get access react as specified on calls for non existing entities by means
* of Optional not present.
*/
@Test
@@ -104,8 +112,8 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
}
/**
* Verifies that management queries react as specified on calls for non existing entities
* by means of throwing EntityNotFoundException.
* Verifies that management queries react as specified on calls for non existing entities
* by means of throwing EntityNotFoundException.
*/
@Test
@ExpectEvents({
@@ -122,15 +130,10 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
verifyThrownExceptionBy(() -> targetManagement.findByTag(NOT_EXIST_IDL, PAGE), "TargetTag");
verifyThrownExceptionBy(() -> targetManagement.findByRsqlAndTag("name==*", NOT_EXIST_IDL, PAGE), "TargetTag");
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==*"),
verifyThrownExceptionBy(() -> targetManagement.countByRsqlAndNonDsAndCompatibleAndUpdatable(NOT_EXIST_IDL, "name==*"),
"DistributionSet");
verifyThrownExceptionBy(() -> targetManagement.deleteByControllerID(NOT_EXIST_ID), "Target");
verifyThrownExceptionBy(() -> targetManagement.deleteByControllerId(NOT_EXIST_ID), "Target");
verifyThrownExceptionBy(() -> targetManagement.delete(Collections.singletonList(NOT_EXIST_IDL)), "Target");
verifyThrownExceptionBy(
@@ -166,7 +169,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
*/
@Test
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1) })
void getTargetSecurityTokenOnlyWithCorrectPermission() throws Exception {
void getTargetSecurityTokenOnlyWithCorrectPermission() {
final Target createdTarget = targetManagement
.create(Create.builder().controllerId("targetWithSecurityToken").securityToken("token").build());
@@ -280,10 +283,8 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
target = createTargetWithAttributes("4711");
assertThat(targetManagement.count()).as("target count is wrong").isEqualTo(1);
assertThat(targetManagement.existsByControllerId("4711")).isTrue();
targetManagement.delete(Collections.singletonList(target.getId()));
assertThat(targetManagement.count()).as("target count is wrong").isZero();
assertThat(targetManagement.existsByControllerId("4711")).isFalse();
final List<Long> targets = new ArrayList<>();
for (int i = 0; i < 5; i++) {
@@ -316,19 +317,6 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
final DistributionSet testDs1 = testdataFactory.createDistributionSet("test");
final DistributionSet testDs2 = testdataFactory.createDistributionSet("test2");
assertThat(targetManagement.countByAssignedDistributionSet(testDs1.getId()))
.as("For newly created distributions sets the assigned target count should be zero").isZero();
assertThat(targetManagement.countByInstalledDistributionSet(testDs1.getId()))
.as("For newly created distributions sets the installed target count should be zero").isZero();
assertThat(targetManagement.existsByInstalledOrAssignedDistributionSet(testDs1.getId()))
.as("Exists assigned or installed query should return false for new distribution sets").isFalse();
assertThat(targetManagement.countByAssignedDistributionSet(testDs2.getId()))
.as("For newly created distributions sets the assigned target count should be zero").isZero();
assertThat(targetManagement.countByInstalledDistributionSet(testDs2.getId()))
.as("For newly created distributions sets the installed target count should be zero").isZero();
assertThat(targetManagement.existsByInstalledOrAssignedDistributionSet(testDs2.getId()))
.as("For newly created distributions sets the assigned target count should be zero").isFalse();
createTargetWithAttributes("4711");
final long current = System.currentTimeMillis();
@@ -344,19 +332,6 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
Target target = targetManagement.getByControllerId("4711").orElseThrow(IllegalStateException::new);
// read data
assertThat(targetManagement.countByAssignedDistributionSet(testDs1.getId())).as("Target count is wrong")
.isZero();
assertThat(targetManagement.countByInstalledDistributionSet(testDs1.getId())).as("Target count is wrong")
.isEqualTo(1);
assertThat(targetManagement.existsByInstalledOrAssignedDistributionSet(testDs1.getId()))
.as("Target count is wrong").isTrue();
assertThat(targetManagement.countByAssignedDistributionSet(testDs2.getId())).as("Target count is wrong")
.isEqualTo(1);
assertThat(targetManagement.countByInstalledDistributionSet(testDs2.getId())).as("Target count is wrong")
.isZero();
assertThat(targetManagement.existsByInstalledOrAssignedDistributionSet(testDs2.getId()))
.as("Target count is wrong").isTrue();
assertThat(target.getLastTargetQuery()).as("Target query is not work").isGreaterThanOrEqualTo(current);
final DistributionSet assignedDs = deploymentManagement.getAssignedDistributionSet("4711")
@@ -401,8 +376,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
@ExpectEvents({
@Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 1) })
void singleTargetIsInsertedIntoRepo() throws Exception {
void singleTargetIsInsertedIntoRepo() {
final String myCtrlID = "myCtrlID";
Target savedTarget = testdataFactory.createTarget(myCtrlID);
@@ -482,7 +456,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
}
}
targetManagement.deleteByControllerID(extra.getControllerId());
targetManagement.deleteByControllerId(extra.getControllerId());
final int numberToDelete = 50;
final Collection<? extends Target> targetsToDelete = firstList.subList(0, numberToDelete);
@@ -495,7 +469,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
assertThat(firstList.spliterator().getExactSizeIfKnown() - numberToDelete).as("Size of split list")
.isEqualTo(targetsLeft.spliterator().getExactSizeIfKnown());
Assertions.<Target>assertThat(targetsLeft).as("Not all undeleted found").doesNotContain(deletedTargets);
Assertions.<Target> assertThat(targetsLeft).as("Not all undeleted found").doesNotContain(deletedTargets);
}
/**
@@ -567,7 +541,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
assignTag(tagABCTargets, tagB);
assignTag(tagABCTargets, tagC);
assertThat(targetManagement.countByFilters(new FilterParams(null, null, null, null, Boolean.FALSE, "X")))
assertThat(countByFilters(new FilterParams(Boolean.FALSE, "X")))
.as("Target count is wrong").isZero();
// search for targets with tag tagA
@@ -597,11 +571,11 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
checkTargetHasNotTags(tagCTargets, tagA, tagB);
// check again target lists refreshed from DB
assertThat(targetManagement.countByFilters(new FilterParams(null, null, null, null, Boolean.FALSE, "A")))
assertThat(countByFilters(new FilterParams(Boolean.FALSE, "A")))
.as("Target count is wrong").isEqualTo(targetWithTagA.size());
assertThat(targetManagement.countByFilters(new FilterParams(null, null, null, null, Boolean.FALSE, "B")))
assertThat(countByFilters(new FilterParams(Boolean.FALSE, "B")))
.as("Target count is wrong").isEqualTo(targetWithTagB.size());
assertThat(targetManagement.countByFilters(new FilterParams(null, null, null, null, Boolean.FALSE, "C")))
assertThat(countByFilters(new FilterParams(Boolean.FALSE, "C")))
.as("Target count is wrong").isEqualTo(targetWithTagC.size());
}
@@ -682,12 +656,10 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
testdataFactory.createTargets(25, "target-id-B", "first description");
final String[] tagNames = null;
final List<Target> targetsListWithNoTag = targetManagement
.findByFilters(new FilterParams(null, null, null, null, Boolean.TRUE, tagNames), PAGE).getContent();
final long targetsListWithNoTag = countByFilters(new FilterParams(Boolean.TRUE, tagNames));
assertThat(targetManagement.count()).as("Total targets").isEqualTo(50L);
assertThat(targetsListWithNoTag).as("Targets with no tag").hasSize(25);
assertThat(targetsListWithNoTag).as("Targets with no tag").isEqualTo(25);
}
/**
@@ -758,16 +730,11 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
void verifyRequestControllerAttributes() {
final String knownControllerId = "KnownControllerId";
final Target target = createTargetWithAttributes(knownControllerId);
assertThat(targetManagement.findByControllerAttributesRequested(PAGE)).isEmpty();
assertThat(targetManagement.isControllerAttributesRequested(knownControllerId)).isFalse();
targetManagement.requestControllerAttributes(knownControllerId);
final Target updated = targetManagement.getByControllerId(knownControllerId).get();
assertThat(target.isRequestControllerAttributes()).isFalse();
assertThat(targetManagement.findByControllerAttributesRequested(PAGE).getContent()).contains(updated);
assertThat(targetManagement.isControllerAttributesRequested(knownControllerId)).isTrue();
targetManagement.update(Update.builder().id(target.getId()).requestControllerAttributes(true).build());
final Target updated = targetManagement.getByControllerId(knownControllerId).orElseThrow();
assertThat(updated.isRequestControllerAttributes()).isTrue();
}
/**
@@ -949,8 +916,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
@ExpectEvents({
@Expect(type = TargetCreatedEvent.class, count = 20),
@Expect(type = TargetTypeCreatedEvent.class, count = 2),
@Expect(type = TargetUpdatedEvent.class, count = 29),
@Expect(type = TargetDeletedEvent.class, count = 1) })
@Expect(type = TargetUpdatedEvent.class, count = 20) })
void targetTypeBulkAssignments() {
final List<Target> typeATargets = testdataFactory.createTargets(10, "typeATargets", "first description");
final List<Target> typeBTargets = testdataFactory.createTargets(10, "typeBTargets", "first description");
@@ -960,30 +926,16 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
final TargetType typeB = testdataFactory.createTargetType("B", Set.of(standardDsType));
// assign target type to target
TargetTypeAssignmentResult resultA = initiateTypeAssignment(typeATargets, typeA);
TargetTypeAssignmentResult resultB = initiateTypeAssignment(typeBTargets, typeB);
assertThat(resultA.getAssigned()).isEqualTo(10);
assertThat(resultB.getAssigned()).isEqualTo(10);
initiateTypeAssignment(typeATargets, typeA);
initiateTypeAssignment(typeBTargets, typeB);
checkTargetsHaveType(typeATargets, typeA);
checkTargetsHaveType(typeBTargets, typeB);
// double assignment does not unassign
resultA = initiateTypeAssignment(typeATargets, typeA);
resultB = initiateTypeAssignment(typeBTargets, typeB);
assertThat(resultA.getAssigned()).isZero();
assertThat(resultB.getAssigned()).isZero();
assertThat(resultA.getAlreadyAssigned()).isEqualTo(10);
assertThat(resultB.getAlreadyAssigned()).isEqualTo(10);
initiateTypeAssignment(typeATargets, typeA);
initiateTypeAssignment(typeBTargets, typeB);
checkTargetsHaveType(typeATargets, typeA);
checkTargetsHaveType(typeBTargets, typeB);
// verify that type assignment does not throw an error if target list
// includes an unknown id
targetManagement.deleteByControllerID(typeATargets.get(0).getControllerId());
final TargetTypeAssignmentResult resultC = initiateTypeAssignment(typeATargets, typeB);
assertThat(resultC.getAssigned()).isEqualTo(9);
assertThat(resultC.getAlreadyAssigned()).isZero();
checkTargetsHaveType(typeATargets, typeB);
}
/**
@@ -1170,13 +1122,11 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
final DistributionSet ds = testdataFactory.createDistributionSet();
assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(
"notExisting", ds.getId(),"name==*")).isFalse();
"notExisting", ds.getId(), "name==*")).isFalse();
}
/**
* Tests action based aspects of the dynamic group assignment filters.
*/
/**
* Target matches filter no active action with ge weight.
*/
@Test
@@ -1344,6 +1294,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
.as("target with too long address should not be updated")
.isThrownBy(() -> targetManagement.update(targetUpdate));
}
private void createTargetWithInvalidControllerId() {
final Create targetCreateEmpty = Create.builder().controllerId("").build();
assertThatExceptionOfType(ConstraintViolationException.class)
@@ -1424,8 +1375,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
private void checkTargetHasNotTags(final Iterable<Target> targets, final TargetTag... tags) {
for (final Target tl : targets) {
targetManagement.getByControllerId(tl.getControllerId()).get();
targetManagement.getByControllerId(tl.getControllerId()).orElseThrow();
for (final Tag tag : tags) {
for (final Tag tt : getTargetTags(tl.getControllerId())) {
if (tag.getName().equals(tt.getName())) {
@@ -1438,7 +1388,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
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);
assertThat(targetManagement.getMetadata(target.getControllerId())).containsEntry(knownKey, knownValue);
}
private void checkTargetsHaveType(final List<Target> targets, final TargetType type) {
@@ -1461,14 +1411,11 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
return target;
}
private Target createTargetWithTargetTypeAndMetadata(final String controllerId, final TargetType targetType, final int count) {
private void createTargetWithTargetTypeAndMetadata(final String controllerId, final TargetType targetType, final int count) {
final Target target = testdataFactory.createTarget(controllerId, controllerId, targetType);
for (int index = 1; index <= count; index++) {
insertMetadata("key" + index, controllerId + "-value" + index, target);
}
return target;
}
private void createAction(final Target target, final Rollout rollout, final Integer weight, final Action.Status status,
@@ -1499,4 +1446,65 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
assertThat(foundTargetsByMetadataAndControllerId.getContent().stream().map(Target::getControllerId))
.as("Targets found by RSQL filter have wrong controller ids").containsExactlyInAnyOrder(controllerIds);
}
}
private long countByFilters(final FilterParams filterParams) {
final List<Specification<JpaTarget>> specList = buildSpecificationList(filterParams);
return JpaManagementHelper.countBySpec(targetRepository, specList);
}
private List<Specification<JpaTarget>> buildSpecificationList(final FilterParams filterParams) {
final List<Specification<JpaTarget>> specList = new ArrayList<>();
if (hasTagsFilterActive(filterParams)) {
specList.add(hasTags(filterParams.filterByTagNames(), filterParams.selectTargetWithNoTag()));
}
return specList;
}
private static boolean hasTagsFilterActive(final FilterParams filterParams) {
final boolean isNoTagActive = Boolean.TRUE.equals(filterParams.selectTargetWithNoTag());
final boolean isAtLeastOneTagActive = filterParams.filterByTagNames() != null && filterParams.filterByTagNames().length > 0;
return isNoTagActive || isAtLeastOneTagActive;
}
/**
* {@link Specification} for retrieving {@link Target}s by "has no tag names"or "has at least on of the given tag names".
*
* @param tagNames to be filtered on
* @param selectTargetWithNoTag flag to get targets with no tag assigned
* @return the {@link Target} {@link Specification}
*/
private static Specification<JpaTarget> hasTags(final String[] tagNames, final Boolean selectTargetWithNoTag) {
return (targetRoot, query, cb) -> {
final Predicate predicate = getHasTagsPredicate(targetRoot, cb, selectTargetWithNoTag, tagNames);
query.distinct(true);
return predicate;
};
}
private static Predicate getHasTagsPredicate(
final Root<JpaTarget> targetRoot, final CriteriaBuilder cb, final Boolean selectTargetWithNoTag, final String[] tagNames) {
final SetJoin<JpaTarget, JpaTargetTag> tags = targetRoot.join(JpaTarget_.tags, JoinType.LEFT);
final Path<String> exp = tags.get(AbstractJpaNamedEntity_.name);
final List<Predicate> hasTagsPredicates = new ArrayList<>();
if (isNoTagActive(selectTargetWithNoTag)) {
hasTagsPredicates.add(exp.isNull());
}
if (isAtLeastOneTagActive(tagNames)) {
hasTagsPredicates.add(exp.in((Object[]) tagNames));
}
return hasTagsPredicates.stream().reduce(cb::or)
.orElseThrow(() -> new RuntimeException("Neither NO_TAG, nor TAG target tag filter was provided!"));
}
private static boolean isNoTagActive(final Boolean selectTargetWithNoTag) {
return Boolean.TRUE.equals(selectTargetWithNoTag);
}
private static boolean isAtLeastOneTagActive(final String[] tagNames) {
return tagNames != null && tagNames.length > 0;
}
private record FilterParams(Boolean selectTargetWithNoTag, String... filterByTagNames) {}
}