Feature target type filter (#1197)

* Added Target type filter with drag and drop support

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Removed the unused enums and target type filter button class

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Resolved merge conflicts

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Fixed java doc issue with the method link in the comment

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Fixed the IN query overflow for target Type assignment

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>

* Fixed Review comments

Signed-off-by: Anand kumar <anand.kumar@bosch-si.com>
This commit is contained in:
Anand Kumar
2021-10-27 15:24:09 +02:00
committed by GitHub
parent dea6fa3ce6
commit 70779d1ac7
38 changed files with 1409 additions and 398 deletions

View File

@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.jpa;
import static org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications.orderedByLinkedDistributionSet;
import com.google.common.collect.Lists;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
@@ -65,6 +66,7 @@ import org.eclipse.hawkbit.repository.model.TargetMetadata;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetTagAssignmentResult;
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.model.helper.EventPublisherHolder;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
@@ -449,11 +451,16 @@ public class JpaTargetManagement implements TargetManagement {
}
@Override
public long countByFilters(final Collection<TargetUpdateStatus> status, final Boolean overdueState,
final String searchText, final Long installedOrAssignedDistributionSetId,
final Boolean selectTargetWithNoTag, final String... tagNames) {
final List<Specification<JpaTarget>> specList = buildSpecificationList(new FilterParams(status, overdueState,
searchText, installedOrAssignedDistributionSetId, selectTargetWithNoTag, tagNames));
public long countByFilters(Collection<TargetUpdateStatus> status, Boolean overdueState, String searchText,
Long installedOrAssignedDistributionSetId, Boolean selectTargetWithNoTag,
String... tagNames) {
return countByFilters(new FilterParams(status, overdueState, searchText, installedOrAssignedDistributionSetId,
selectTargetWithNoTag, tagNames));
}
@Override
public long countByFilters(final FilterParams filterParams) {
final List<Specification<JpaTarget>> specList = buildSpecificationList(filterParams);
return countByCriteriaAPI(specList);
}
@@ -479,6 +486,13 @@ public class JpaTargetManagement implements TargetManagement {
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;
}
@@ -490,6 +504,14 @@ public class JpaTargetManagement implements TargetManagement {
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 Slice<Target> findByCriteriaAPI(final Pageable pageable, final List<Specification<JpaTarget>> specList) {
if (CollectionUtils.isEmpty(specList)) {
return convertPage(targetRepository.findAllWithoutCount(pageable), pageable);
@@ -545,6 +567,59 @@ public class JpaTargetManagement implements TargetManagement {
return result;
}
@Override
@Transactional
@Retryable(include = {
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 TargetType type = targetTypeRepository.findById(typeId)
.orElseThrow(() -> new EntityNotFoundException(TargetType.class, typeId));
final List<JpaTarget> targetsWithSameType = findTargetsByInSpecification(controllerIds,
TargetSpecifications.hasTargetType(typeId));
final List<JpaTarget> targetsWithoutSameType = findTargetsByInSpecification(controllerIds,
TargetSpecifications.hasTargetTypeNot(typeId));
// set new target type to all targets without that type
targetsWithoutSameType.forEach(target -> target.setTargetType(type));
final TargetTypeAssignmentResult result = new TargetTypeAssignmentResult(targetsWithSameType.size(),
Collections
.unmodifiableList(targetsWithoutSameType.stream().map(targetRepository::save).collect(Collectors.toList())),
Collections.emptyList(), type);
// no reason to persist the type
entityManager.detach(type);
return result;
}
@Override
@Transactional
@Retryable(include = {
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).collect(Collectors.toList()));
}
// set new target type to null for all targets
allTargets.forEach(target -> target.setTargetType(null));
return new TargetTypeAssignmentResult(0, Collections.emptyList(), Collections
.unmodifiableList(allTargets.stream().map(targetRepository::save).collect(Collectors.toList())), null);
}
private List<JpaTarget> findTargetsByInSpecification(Collection<String> controllerIds,
Specification<JpaTarget> specification) {
return Lists.partition(new ArrayList<>(controllerIds), Constants.MAX_ENTRIES_IN_STATEMENT).stream()
.map(ids -> targetRepository.findAll(TargetSpecifications.hasControllerIdIn(ids).and(specification)))
.flatMap(List::stream).collect(Collectors.toList());
}
@Override
@Transactional
@Retryable(include = {
@@ -625,7 +700,7 @@ public class JpaTargetManagement implements TargetManagement {
/**
* Applies orderedByLinkedDistributionSet spec for order and adds additional
* specs based on filters
*
*
* @param orderByDistributionId
* distribution set used for order
* @param filterParams

View File

@@ -8,12 +8,6 @@
*/
package org.eclipse.hawkbit.repository.jpa;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.TargetTypeFields;
import org.eclipse.hawkbit.repository.TargetTypeManagement;
@@ -42,9 +36,14 @@ import org.springframework.orm.jpa.vendor.Database;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* JPA implementation of {@link TargetTypeManagement}.
*

View File

@@ -392,12 +392,16 @@ public final class TargetSpecifications {
return (targetRoot, query, cb) -> {
// Since the targetRoot is changed by joining we need to get the
// isNull predicate first
final Predicate targetTypeIsNull = targetRoot.get(JpaTarget_.targetType).isNull();
final Predicate targetTypeIsNull = getTargetTypeIsNullPredicate(targetRoot);
return cb.or(targetTypeIsNull, cb.equal(getDsTypeIdPath(targetRoot), distributionSetTypeId));
};
}
private static Predicate getTargetTypeIsNullPredicate(Root<JpaTarget> targetRoot) {
return targetRoot.get(JpaTarget_.targetType).isNull();
}
/**
* {@link Specification} for retrieving {@link Target}s that are NOT compatible
* with given {@link DistributionSetType}. Compatibility is evaluated by
@@ -532,13 +536,39 @@ public final class TargetSpecifications {
}
/**
* {@link Specification} for retrieving {@link Target}s that have a
* {@link org.eclipse.hawkbit.repository.model.TargetType} assigned
* {@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() {
return (targetRoot, query, cb) -> cb.isNotNull(targetRoot.get(JpaTarget_.targetType));
public static Specification<JpaTarget> hasTargetType(final long typeId) {
return (targetRoot, query, cb) -> cb.equal(targetRoot.get(JpaTarget_.targetType).get(JpaTargetType_.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(JpaTargetType_.id), typeId));
}
/**