Enable sorting in tables (#1279)
* provided infrastructure to enable sorting in grids * Fixed sorting in RolloutGroupTarget * fixed sorting with pinning and assigned SoftwareModules * Added sorting for columns createdBy, createdAt, lastModifiedBy and lastModifiedAt * Adapted status columns to be sortable * fixed unit tests * fixing sonar findings * making Sonar happy * added testcases for management classes regarding sorting * added testcases for management classes regarding sorting * using name for element ids in DOM * incorporated code review remarks Signed-off-by: Markus Block <markus.block@bosch-si.com>
This commit is contained in:
@@ -374,14 +374,14 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
|
||||
@Override
|
||||
public Slice<DistributionSet> findByDistributionSetFilterOrderByLinkedTarget(final Pageable pageable,
|
||||
final DistributionSetFilter distributionSetFilter, final String assignedOrInstalled) {
|
||||
final List<Specification<JpaDistributionSet>> specList = buildDistributionSetSpecifications(
|
||||
distributionSetFilter);
|
||||
specList.add(DistributionSetSpecification.orderedByLinkedTarget(assignedOrInstalled));
|
||||
|
||||
// remove default sort from pageable to not overwrite sorted spec
|
||||
final OffsetBasedPageRequest unsortedPage = new OffsetBasedPageRequest(pageable.getOffset(),
|
||||
pageable.getPageSize(), Sort.unsorted());
|
||||
|
||||
final List<Specification<JpaDistributionSet>> specList = buildDistributionSetSpecifications(
|
||||
distributionSetFilter);
|
||||
specList.add(DistributionSetSpecification.orderedByLinkedTarget(assignedOrInstalled, pageable.getSort()));
|
||||
|
||||
return JpaManagementHelper.findAllWithoutCountBySpec(distributionSetRepository, unsortedPage, specList);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Join;
|
||||
import javax.persistence.criteria.JoinType;
|
||||
import javax.persistence.criteria.ListJoin;
|
||||
import javax.persistence.criteria.Order;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
import org.eclipse.hawkbit.repository.RolloutGroupFields;
|
||||
@@ -51,7 +52,9 @@ import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.jpa.repository.query.QueryUtils;
|
||||
import org.springframework.orm.jpa.vendor.Database;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
@@ -259,7 +262,8 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
|
||||
|
||||
final CriteriaQuery<Object[]> multiselect = query.multiselect(targetJoin, actionJoin.get(JpaAction_.status))
|
||||
.where(cb.equal(targetRoot.get(RolloutTargetGroup_.rolloutGroup).get(JpaRolloutGroup_.id),
|
||||
rolloutGroupId));
|
||||
rolloutGroupId))
|
||||
.orderBy(getOrderBy(pageRequest, cb, targetJoin, actionJoin));
|
||||
final List<TargetWithActionStatus> targetWithActionStatus = entityManager.createQuery(multiselect)
|
||||
.setFirstResult((int) pageRequest.getOffset()).setMaxResults(pageRequest.getPageSize()).getResultList()
|
||||
.stream().map(o -> new TargetWithActionStatus((Target) o[0], (Action.Status) o[1]))
|
||||
@@ -268,6 +272,25 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
|
||||
return new PageImpl<>(targetWithActionStatus, pageRequest, totalCount);
|
||||
}
|
||||
|
||||
private List<Order> getOrderBy(final Pageable pageRequest, final CriteriaBuilder cb,
|
||||
final Join<RolloutTargetGroup, JpaTarget> targetJoin,
|
||||
final ListJoin<RolloutTargetGroup, JpaAction> actionJoin) {
|
||||
|
||||
return pageRequest.getSort().get().flatMap(order -> {
|
||||
final List<Order> orders;
|
||||
final String property = order.getProperty();
|
||||
// we consider status as property from JpaAction ...
|
||||
if ("status".equals(property)) {
|
||||
orders = QueryUtils.toOrders(Sort.by(order.getDirection(), property), actionJoin, cb);
|
||||
}
|
||||
// ... and every other property from JpaTarget
|
||||
else {
|
||||
orders = QueryUtils.toOrders(Sort.by(order.getDirection(), property), targetJoin, cb);
|
||||
}
|
||||
return orders.stream();
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countTargetsOfRolloutsGroup(final long rolloutGroupId) {
|
||||
throwExceptionIfRolloutGroupDoesNotExist(rolloutGroupId);
|
||||
|
||||
@@ -47,6 +47,7 @@ import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout_;
|
||||
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
|
||||
import org.eclipse.hawkbit.repository.jpa.specifications.RolloutSpecification;
|
||||
import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper;
|
||||
@@ -75,6 +76,8 @@ import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.integration.support.locks.LockRegistry;
|
||||
import org.springframework.orm.jpa.vendor.Database;
|
||||
@@ -162,7 +165,8 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
@Override
|
||||
public Page<Rollout> findAll(final Pageable pageable, final boolean deleted) {
|
||||
return JpaManagementHelper.findAllWithCountBySpec(rolloutRepository, pageable,
|
||||
Collections.singletonList(RolloutSpecification.isDeletedWithDistributionSet(deleted)));
|
||||
Collections
|
||||
.singletonList(RolloutSpecification.isDeletedWithDistributionSet(deleted, pageable.getSort())));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -170,7 +174,7 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
final List<Specification<JpaRollout>> specList = Lists.newArrayListWithExpectedSize(2);
|
||||
specList.add(
|
||||
RSQLUtility.buildRsqlSpecification(rsqlParam, RolloutFields.class, virtualPropertyReplacer, database));
|
||||
specList.add(RolloutSpecification.isDeletedWithDistributionSet(deleted));
|
||||
specList.add(RolloutSpecification.isDeletedWithDistributionSet(deleted, pageable.getSort()));
|
||||
|
||||
return JpaManagementHelper.findAllWithCountBySpec(rolloutRepository, pageable, specList);
|
||||
}
|
||||
@@ -298,7 +302,7 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
/**
|
||||
* In case the given group is missing conditions or actions, they will be
|
||||
* set from the supplied default conditions.
|
||||
*
|
||||
*
|
||||
* @param create
|
||||
* group to check
|
||||
* @param conditions
|
||||
@@ -481,7 +485,8 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
return rolloutRepository.count(RolloutSpecification.isDeletedWithDistributionSet(false));
|
||||
return rolloutRepository.count(
|
||||
RolloutSpecification.isDeletedWithDistributionSet(false, Sort.by(Direction.DESC, JpaRollout_.ID)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -553,7 +558,8 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
@Override
|
||||
public Slice<Rollout> findAllWithDetailedStatus(final Pageable pageable, final boolean deleted) {
|
||||
final Slice<Rollout> rollouts = JpaManagementHelper.findAllWithoutCountBySpec(rolloutRepository, pageable,
|
||||
Collections.singletonList(RolloutSpecification.isDeletedWithDistributionSet(deleted)));
|
||||
Collections
|
||||
.singletonList(RolloutSpecification.isDeletedWithDistributionSet(deleted, pageable.getSort())));
|
||||
setRolloutStatusDetails(rollouts);
|
||||
return rollouts;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Expression;
|
||||
import javax.persistence.criteria.JoinType;
|
||||
import javax.persistence.criteria.ListJoin;
|
||||
import javax.persistence.criteria.Order;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
@@ -73,7 +74,9 @@ import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.domain.SliceImpl;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.jpa.repository.query.QueryUtils;
|
||||
import org.springframework.orm.jpa.vendor.Database;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
@@ -308,6 +311,10 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
|
||||
}
|
||||
|
||||
@Override
|
||||
// In the interface org.springframework.data.domain.Pageable.getSort the
|
||||
// return value is not guaranteed to be non-null, therefore a null check is
|
||||
// necessary otherwise we rely on the implementation but this could change.
|
||||
@SuppressWarnings({ "squid:S2583", "squid:S2589" })
|
||||
public Slice<AssignedSoftwareModule> findAllOrderBySetAssignmentAndModuleNameAscModuleVersionAsc(
|
||||
final Pageable pageable, final long dsId, final String searchText, final Long smTypeId) {
|
||||
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
@@ -331,8 +338,16 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
|
||||
|
||||
query.groupBy(smRoot);
|
||||
|
||||
query.orderBy(cb.desc(assignedCaseMax), cb.asc(smRoot.get(JpaSoftwareModule_.name)),
|
||||
cb.asc(smRoot.get(JpaSoftwareModule_.version)));
|
||||
final Sort sort = pageable.getSort();
|
||||
final List<Order> orders = new ArrayList<>();
|
||||
orders.add(cb.desc(assignedCaseMax));
|
||||
if (sort == null || sort.isEmpty()) {
|
||||
orders.add(cb.asc(smRoot.get(JpaSoftwareModule_.name)));
|
||||
orders.add(cb.asc(smRoot.get(JpaSoftwareModule_.version)));
|
||||
} else {
|
||||
orders.addAll(QueryUtils.toOrders(sort, smRoot, cb));
|
||||
}
|
||||
query.orderBy(orders);
|
||||
|
||||
final int pageSize = pageable.getPageSize();
|
||||
final List<Tuple> smWithAssignedFlagList = entityManager.createQuery(query)
|
||||
@@ -486,7 +501,7 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
|
||||
|
||||
/**
|
||||
* Asserts the meta data quota for the software module with the given ID.
|
||||
*
|
||||
*
|
||||
* @param moduleId
|
||||
* The software module ID.
|
||||
* @param requested
|
||||
|
||||
@@ -638,7 +638,7 @@ public class JpaTargetManagement implements TargetManagement {
|
||||
pageable.getPageSize(), Sort.unsorted());
|
||||
|
||||
final List<Specification<JpaTarget>> specList = buildSpecificationList(filterParams);
|
||||
specList.add(TargetSpecifications.orderedByLinkedDistributionSet(orderByDistributionId));
|
||||
specList.add(TargetSpecifications.orderedByLinkedDistributionSet(orderByDistributionId, pageable.getSort()));
|
||||
|
||||
return JpaManagementHelper.findAllWithoutCountBySpec(targetRepository, unsortedPage, specList);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.Expression;
|
||||
import javax.persistence.criteria.JoinType;
|
||||
import javax.persistence.criteria.ListJoin;
|
||||
import javax.persistence.criteria.Order;
|
||||
import javax.persistence.criteria.Path;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
@@ -29,7 +30,9 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.jpa.repository.query.QueryUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
@@ -265,7 +268,8 @@ public final class DistributionSetSpecification {
|
||||
* Can be added to specification chain to order result by provided target
|
||||
*
|
||||
* Order: 1. Distribution set installed on target, 2. Distribution set(s)
|
||||
* assigned to target, 3. Based on distribution set id
|
||||
* assigned to target, 3. Based on requested sorting or id if
|
||||
* <code>null</code>.
|
||||
*
|
||||
* NOTE: Other specs, pagables and sort objects may alter the queries
|
||||
* orderBy entry too, possibly invalidating the applied order, keep in mind
|
||||
@@ -273,16 +277,26 @@ public final class DistributionSetSpecification {
|
||||
*
|
||||
* @param linkedControllerId
|
||||
* controller id to get installed/assigned DS for
|
||||
* @param sort
|
||||
* @return specification that applies order by target, may be overwritten
|
||||
*/
|
||||
public static Specification<JpaDistributionSet> orderedByLinkedTarget(final String linkedControllerId) {
|
||||
public static Specification<JpaDistributionSet> orderedByLinkedTarget(final String linkedControllerId,
|
||||
final Sort sort) {
|
||||
return (dsRoot, query, cb) -> {
|
||||
final Root<JpaTarget> targetRoot = query.from(JpaTarget.class);
|
||||
|
||||
final Expression<Object> assignedInstalledCase = cb.selectCase()
|
||||
.when(cb.equal(targetRoot.get(JpaTarget_.installedDistributionSet), dsRoot), 1)
|
||||
.when(cb.equal(targetRoot.get(JpaTarget_.assignedDistributionSet), dsRoot), 2).otherwise(3);
|
||||
query.orderBy(cb.asc(assignedInstalledCase), cb.asc(dsRoot.get(JpaDistributionSet_.id)));
|
||||
|
||||
final List<Order> orders = new ArrayList<>();
|
||||
orders.add(cb.asc(assignedInstalledCase));
|
||||
if (sort == null || sort.isEmpty()) {
|
||||
orders.add(cb.asc(dsRoot.get(JpaDistributionSet_.id)));
|
||||
} else {
|
||||
orders.addAll(QueryUtils.toOrders(sort, dsRoot, cb));
|
||||
}
|
||||
query.orderBy(orders);
|
||||
|
||||
return cb.equal(targetRoot.get(JpaTarget_.controllerId), linkedControllerId);
|
||||
};
|
||||
|
||||
@@ -13,7 +13,9 @@ import javax.persistence.criteria.Predicate;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout_;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.jpa.repository.query.QueryUtils;
|
||||
|
||||
/**
|
||||
* Specifications class for {@link Rollout}s. The class provides Spring Data
|
||||
@@ -29,17 +31,18 @@ public final class RolloutSpecification {
|
||||
* {@link Specification} for retrieving {@link Rollout}s by its DELETED
|
||||
* attribute. Includes fetch for stuff that is required for {@link Rollout}
|
||||
* queries.
|
||||
*
|
||||
*
|
||||
* @param isDeleted
|
||||
* TRUE/FALSE are compared to the attribute DELETED. If NULL the
|
||||
* attribute is ignored
|
||||
* @return the {@link Rollout} {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaRollout> isDeletedWithDistributionSet(final Boolean isDeleted) {
|
||||
public static Specification<JpaRollout> isDeletedWithDistributionSet(final Boolean isDeleted, final Sort sort) {
|
||||
return (root, query, cb) -> {
|
||||
|
||||
final Predicate predicate = cb.equal(root.<Boolean> get(JpaRollout_.deleted), isDeleted);
|
||||
root.fetch(JpaRollout_.distributionSet);
|
||||
query.orderBy(QueryUtils.toOrders(sort, root, cb));
|
||||
return predicate;
|
||||
};
|
||||
|
||||
@@ -47,14 +50,14 @@ public final class RolloutSpecification {
|
||||
|
||||
/**
|
||||
* Builds a {@link Specification} to search a rollout by name.
|
||||
*
|
||||
*
|
||||
* @param searchText
|
||||
* search string
|
||||
* @param isDeleted
|
||||
* <code>true</code> if deleted rollouts should be included in
|
||||
* the search. Otherwise <code>false</code>
|
||||
* @return criteria specification with a query for name of a rollout
|
||||
*
|
||||
*
|
||||
*/
|
||||
public static Specification<JpaRollout> likeName(final String searchText, final boolean isDeleted) {
|
||||
return (rolloutRoot, query, criteriaBuilder) -> {
|
||||
|
||||
@@ -18,6 +18,7 @@ import javax.persistence.criteria.Join;
|
||||
import javax.persistence.criteria.JoinType;
|
||||
import javax.persistence.criteria.ListJoin;
|
||||
import javax.persistence.criteria.MapJoin;
|
||||
import javax.persistence.criteria.Order;
|
||||
import javax.persistence.criteria.Path;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
@@ -47,7 +48,9 @@ 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.TargetUpdateStatus;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.jpa.repository.query.QueryUtils;
|
||||
|
||||
/**
|
||||
* Specifications class for {@link Target}s. The class provides Spring Data JPQL
|
||||
@@ -571,7 +574,7 @@ public final class TargetSpecifications {
|
||||
* distribution set
|
||||
*
|
||||
* Order: 1. Targets with DS installed, 2. Targets with DS assigned, 3.
|
||||
* Based on target id
|
||||
* Based on requested sorting or id if <code>null</code>.
|
||||
*
|
||||
* NOTE: Other specs, pagables and sort objects may alter the queries
|
||||
* orderBy entry too, possibly invalidating the applied order, keep in mind
|
||||
@@ -579,9 +582,12 @@ public final class TargetSpecifications {
|
||||
*
|
||||
* @param distributionSetIdForOrder
|
||||
* distribution set to consider
|
||||
* @param sort
|
||||
* the sorting requested
|
||||
* @return specification that applies order by ds, may be overwritten
|
||||
*/
|
||||
public static Specification<JpaTarget> orderedByLinkedDistributionSet(final long distributionSetIdForOrder) {
|
||||
public static Specification<JpaTarget> orderedByLinkedDistributionSet(final long distributionSetIdForOrder,
|
||||
final Sort sort) {
|
||||
return (targetRoot, query, cb) -> {
|
||||
// Enhance query with custom select based sort
|
||||
final Expression<Object> selectCase = cb.selectCase()
|
||||
@@ -590,7 +596,14 @@ public final class TargetSpecifications {
|
||||
.when(cb.equal(targetRoot.get(JpaTarget_.assignedDistributionSet).get(JpaDistributionSet_.id),
|
||||
distributionSetIdForOrder), 2)
|
||||
.otherwise(100);
|
||||
query.orderBy(cb.asc(selectCase), cb.desc(targetRoot.get(JpaTarget_.id)));
|
||||
final List<Order> orders = new ArrayList<>();
|
||||
orders.add(cb.asc(selectCase));
|
||||
if (sort == null || sort.isEmpty()) {
|
||||
orders.add(cb.desc(targetRoot.get(JpaTarget_.id)));
|
||||
} else {
|
||||
orders.addAll(QueryUtils.toOrders(sort, targetRoot, cb));
|
||||
}
|
||||
query.orderBy(orders);
|
||||
|
||||
// Spec only provides order, so no further filtering
|
||||
return query.getRestriction();
|
||||
|
||||
Reference in New Issue
Block a user