Introduce target grouping (#2538)
* Introduce target grouping Signed-off-by: strailov <Stanislav.Trailov@bosch.io> * minor refactor Signed-off-by: strailov <Stanislav.Trailov@bosch.io> * throw validation exception instead direct returning bad request response Signed-off-by: strailov <Stanislav.Trailov@bosch.io> * fix group query parameter Signed-off-by: strailov <Stanislav.Trailov@bosch.io> * remove wrongly added import Signed-off-by: strailov <Stanislav.Trailov@bosch.io> * add review fixes Signed-off-by: strailov <Stanislav.Trailov@bosch.io> * apply latest review changes Signed-off-by: strailov <Stanislav.Trailov@bosch.io> * apply latest changes after sybnc/review Signed-off-by: strailov <Stanislav.Trailov@bosch.io> * fix after review Signed-off-by: strailov <Stanislav.Trailov@bosch.io> --------- Signed-off-by: strailov <Stanislav.Trailov@bosch.io>
This commit is contained in:
committed by
GitHub
parent
e7373275bf
commit
b4793fcce1
@@ -620,6 +620,41 @@ public interface TargetManagement {
|
||||
@PreAuthorize(HAS_AUTH_UPDATE_TARGET)
|
||||
Target assignType(@NotEmpty String controllerId, @NotNull Long targetTypeId);
|
||||
|
||||
/**
|
||||
* Finds targets by group or subgroup.
|
||||
* @param group - provided group/subgroup to filter for
|
||||
* @param withSubgroups - whether is a subgroup or not e.g. x/y/z
|
||||
* @param pageable - page parameter
|
||||
* @return all matching targets to provided group/subgroup
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||
Page<Target> findTargetsByGroup(@NotEmpty String group, boolean withSubgroups, @NotNull Pageable pageable);
|
||||
/**
|
||||
* Finds all the distinct target groups in the scope of a tenant
|
||||
*
|
||||
* @return list of all distinct target groups
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||
List<String> findGroups();
|
||||
|
||||
/**
|
||||
* Assigns the target group of the targets matching the provided rsql filter.
|
||||
*
|
||||
* @param group target group parameter
|
||||
* @param rsql rsql filter for {@link Target}
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
|
||||
void assignTargetGroupWithRsql(String group, @NotNull String rsql);
|
||||
|
||||
/**
|
||||
* Assigns the provided group to the targets which are in the provided list of controllerIds.
|
||||
*
|
||||
* @param group target group parameter
|
||||
* @param controllerIds list of targets
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
|
||||
void assignTargetsWithGroup(String group, @NotEmpty List<String> controllerIds);
|
||||
|
||||
/**
|
||||
* updates the {@link Target}.
|
||||
*
|
||||
|
||||
@@ -73,6 +73,12 @@ public interface TargetCreate {
|
||||
*/
|
||||
TargetCreate status(@NotNull TargetUpdateStatus status);
|
||||
|
||||
/**
|
||||
* @param group for setting the group of the target
|
||||
* @return updated builder instance
|
||||
*/
|
||||
TargetCreate group(String group);
|
||||
|
||||
/**
|
||||
* @return peek on current state of {@link Target} in the builder
|
||||
*/
|
||||
|
||||
@@ -70,4 +70,11 @@ public interface TargetUpdate {
|
||||
* @return updated builder instance
|
||||
*/
|
||||
TargetUpdate requestAttributes(Boolean requestAttributes);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param group for {@link Target#getGroup()}
|
||||
* @return updated builder instance
|
||||
*/
|
||||
TargetUpdate group(String group);
|
||||
}
|
||||
@@ -124,4 +124,10 @@ public interface Target extends NamedEntity {
|
||||
* {@link #getControllerAttributes()}.
|
||||
*/
|
||||
boolean isRequestControllerAttributes();
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the group of the target
|
||||
*/
|
||||
String getGroup();
|
||||
}
|
||||
@@ -34,6 +34,7 @@ public class AbstractTargetUpdateCreate<T> extends AbstractNamedEntityBuilder<T>
|
||||
protected TargetUpdateStatus status;
|
||||
protected Boolean requestAttributes;
|
||||
protected Long targetTypeId;
|
||||
protected String group;
|
||||
|
||||
protected AbstractTargetUpdateCreate(final String controllerId) {
|
||||
this.controllerId = AbstractBaseEntityBuilder.strip(controllerId);
|
||||
@@ -83,6 +84,11 @@ public class AbstractTargetUpdateCreate<T> extends AbstractNamedEntityBuilder<T>
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public T group(final String group) {
|
||||
this.group = group;
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public String getControllerId() {
|
||||
return controllerId;
|
||||
}
|
||||
@@ -107,4 +113,6 @@ public class AbstractTargetUpdateCreate<T> extends AbstractNamedEntityBuilder<T>
|
||||
return targetTypeId;
|
||||
}
|
||||
|
||||
public Optional<String> getGroup() { return Optional.ofNullable(group); }
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE sp_target ADD COLUMN target_group VARCHAR(256);
|
||||
CREATE INDEX sp_idx_target_group ON sp_target (tenant, target_group);
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE sp_target ADD COLUMN target_group VARCHAR(256);
|
||||
CREATE INDEX sp_idx_target_group ON sp_target (tenant, target_group);
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE sp_target ADD COLUMN target_group VARCHAR(256);
|
||||
CREATE INDEX sp_idx_target_group ON sp_target (tenant, target_group);
|
||||
@@ -53,6 +53,7 @@ public class JpaTargetCreate extends AbstractTargetUpdateCreate<TargetCreate> im
|
||||
target.setAddress(address);
|
||||
target.setUpdateStatus(getStatus().orElse(TargetUpdateStatus.UNKNOWN));
|
||||
getLastTargetQuery().ifPresent(target::setLastTargetQuery);
|
||||
target.setGroup(getGroup().orElse(null));
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,9 @@ import java.util.stream.Collectors;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.CriteriaQuery;
|
||||
import jakarta.persistence.criteria.CriteriaUpdate;
|
||||
import jakarta.persistence.criteria.MapJoin;
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import jakarta.persistence.criteria.Root;
|
||||
import jakarta.persistence.metamodel.MapAttribute;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
@@ -562,6 +564,57 @@ public class JpaTargetManagement implements TargetManagement {
|
||||
return targetRepository.save(target);
|
||||
}
|
||||
|
||||
@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(targetRepository, List.of(TargetSpecifications.eqOrSubTargetGroup(group)), pageable);
|
||||
} else {
|
||||
return JpaManagementHelper
|
||||
.findAllWithCountBySpec(targetRepository, List.of(TargetSpecifications.eqTargetGroup(group)), pageable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> findGroups() {
|
||||
return targetRepository.findDistinctGroups();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void assignTargetGroupWithRsql(String group, String rsql) {
|
||||
final Specification<JpaTarget> rsqlSpecification = RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetFields.class);
|
||||
|
||||
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
final CriteriaUpdate<JpaTarget> criteriaUpdateQuery = cb.createCriteriaUpdate(JpaTarget.class);
|
||||
final Root<JpaTarget> root = criteriaUpdateQuery.getRoot();
|
||||
criteriaUpdateQuery.set("group", group);
|
||||
// get predicate from rsql specification using a dummy query in order to execute batch update
|
||||
final Predicate predicate = rsqlSpecification.toPredicate(root, entityManager.getCriteriaBuilder().createQuery(JpaTarget.class), cb);
|
||||
criteriaUpdateQuery.where(predicate);
|
||||
|
||||
entityManager.createQuery(criteriaUpdateQuery).executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void assignTargetsWithGroup(String group, List<String> controllerIds) {
|
||||
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
CriteriaUpdate<JpaTarget> criteriaQuery = cb.createCriteriaUpdate(JpaTarget.class);
|
||||
Root<JpaTarget> root = criteriaQuery.from(JpaTarget.class);
|
||||
CriteriaBuilder.In<String> in = cb.in(root.get("controllerId"));
|
||||
controllerIds.forEach(in::value);
|
||||
|
||||
entityManager.createQuery(criteriaQuery.set("group", group).where(in)).executeUpdate();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
|
||||
@@ -154,6 +154,11 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
||||
// set default request controller attributes to true, because we want to request them the first time
|
||||
private boolean requestControllerAttributes = true;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
@Column(name = "target_group")
|
||||
private String group;
|
||||
|
||||
// actually it is OneToOne - but lazy loading is not supported for OneToOne (at least for hibernate 6.6.2)
|
||||
@OneToMany(fetch = FetchType.LAZY, mappedBy = "target", cascade = { CascadeType.ALL }, orphanRemoval = true)
|
||||
@PrimaryKeyJoinColumn
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
package org.eclipse.hawkbit.repository.jpa.repository;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
@@ -96,4 +97,11 @@ public interface TargetRepository extends BaseEntityRepository<JpaTarget> {
|
||||
@Transactional
|
||||
@Query("DELETE FROM JpaTarget t WHERE t.tenant = :tenant")
|
||||
void deleteByTenant(@Param("tenant") String tenant);
|
||||
|
||||
/**
|
||||
* Finds all available distinct target groups
|
||||
* @return all target groups
|
||||
*/
|
||||
@Query(value = "SELECT DISTINCT target_group FROM sp_target WHERE target_group IS NOT NULL", nativeQuery = true)
|
||||
List<String> findDistinctGroups();
|
||||
}
|
||||
@@ -192,6 +192,22 @@ public final class TargetSpecifications {
|
||||
};
|
||||
}
|
||||
|
||||
public static Specification<JpaTarget> eqTargetGroup(final String targetGroup) {
|
||||
return (targetRoot, query, criteriaBuilder) -> {
|
||||
final String groupTextToLower = targetGroup.toLowerCase();
|
||||
return criteriaBuilder.equal(criteriaBuilder.lower(targetRoot.get(JpaTarget_.group)), groupTextToLower);
|
||||
};
|
||||
}
|
||||
|
||||
public static Specification<JpaTarget> eqOrSubTargetGroup(final String targetGroupSearch) {
|
||||
return (targetRoot, query, criteriaBuilder) -> {
|
||||
final String searchTextToLower = targetGroupSearch.toLowerCase();
|
||||
return criteriaBuilder.or(
|
||||
criteriaBuilder.equal(criteriaBuilder.lower(targetRoot.get(JpaTarget_.group)), searchTextToLower),
|
||||
criteriaBuilder.like(criteriaBuilder.lower(targetRoot.get(JpaTarget_.group)), searchTextToLower.concat("/%")));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Specification} for retrieving {@link Target}s by "like controllerId".
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user