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());
}
}