Extend Query Langage Support (#2696)

* add option for parser replacement - so, one could replace the query language and don't use RSQL
* add easy way to modify query root Node 
* add easy Node modification utilities
* code refactored and naming more 'QL' than 'RSQL' oriented

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2025-09-26 14:19:25 +03:00
committed by GitHub
parent d0fe120a3b
commit b181b66ee5
45 changed files with 308 additions and 211 deletions

View File

@@ -17,7 +17,7 @@ import lombok.Getter;
* Sort and search fields for actions.
*/
@Getter
public enum ActionFields implements RsqlQueryField, FieldValueConverter<ActionFields> {
public enum ActionFields implements QueryField, FieldValueConverter<ActionFields> {
ID("id"),
STATUS("active"),

View File

@@ -15,7 +15,7 @@ import lombok.Getter;
* Sort and search fields for action status.
*/
@Getter
public enum ActionStatusFields implements RsqlQueryField {
public enum ActionStatusFields implements QueryField {
ID("id"),
@Deprecated

View File

@@ -18,7 +18,7 @@ import lombok.Getter;
* REST API e.g. for sorting etc.
*/
@Getter
public enum DistributionSetFields implements RsqlQueryField {
public enum DistributionSetFields implements QueryField {
ID("id"),
TYPE("type",
@@ -48,7 +48,7 @@ public enum DistributionSetFields implements RsqlQueryField {
@Override
public String getDefaultSubEntityAttribute() {
return this == TYPE ? DistributionSetTypeFields.KEY.getJpaEntityFieldName() : RsqlQueryField.super.getDefaultSubEntityAttribute();
return this == TYPE ? DistributionSetTypeFields.KEY.getJpaEntityFieldName() : QueryField.super.getDefaultSubEntityAttribute();
}
@Override

View File

@@ -20,7 +20,7 @@ import lombok.Getter;
* filtering over distribution set fields also.
*/
@Getter
public enum DistributionSetTagFields implements RsqlQueryField {
public enum DistributionSetTagFields implements QueryField {
ID(TagFields.ID.getJpaEntityFieldName()),
NAME(TagFields.NAME.getJpaEntityFieldName()),

View File

@@ -16,7 +16,7 @@ import lombok.Getter;
* the REST API e.g. for sorting etc.
*/
@Getter
public enum DistributionSetTypeFields implements RsqlQueryField {
public enum DistributionSetTypeFields implements QueryField {
ID("id"),
KEY("key"),

View File

@@ -16,9 +16,9 @@ import java.util.Map;
import jakarta.validation.constraints.NotNull;
/**
* An RSQL query field interface extended by all the fields that could be used in RSQL queries.
* A query field interface extended by all the fields that could be used in queries.
*/
public interface RsqlQueryField {
public interface QueryField {
/**
* Separator for the sub attributes

View File

@@ -17,7 +17,7 @@ import lombok.Getter;
* Describing the fields of the Rollout model which can be used in the REST API e.g. for sorting etc.
*/
@Getter
public enum RolloutFields implements RsqlQueryField {
public enum RolloutFields implements QueryField {
ID("id"),
NAME("name"),

View File

@@ -15,7 +15,7 @@ import lombok.Getter;
* Describing the fields of the RolloutGroup model which can be used in the REST API e.g. for sorting etc.
*/
@Getter
public enum RolloutGroupFields implements RsqlQueryField {
public enum RolloutGroupFields implements QueryField {
ID("id"),
NAME("name"),

View File

@@ -17,7 +17,7 @@ import lombok.Getter;
* Describing the fields of the SoftwareModule model which can be used in the REST API e.g. for sorting etc.
*/
@Getter
public enum SoftwareModuleFields implements RsqlQueryField {
public enum SoftwareModuleFields implements QueryField {
ID("id"),
TYPE("type",
@@ -44,7 +44,7 @@ public enum SoftwareModuleFields implements RsqlQueryField {
@Override
public String getDefaultSubEntityAttribute() {
return this == TYPE ? SoftwareModuleTypeFields.KEY.getJpaEntityFieldName() : RsqlQueryField.super.getDefaultSubEntityAttribute();
return this == TYPE ? SoftwareModuleTypeFields.KEY.getJpaEntityFieldName() : QueryField.super.getDefaultSubEntityAttribute();
}
@Override

View File

@@ -15,7 +15,7 @@ import lombok.Getter;
* Describing the fields of the SoftwareModuleType model which can be used in the REST API e.g. for sorting etc.
*/
@Getter
public enum SoftwareModuleTypeFields implements RsqlQueryField {
public enum SoftwareModuleTypeFields implements QueryField {
ID("id"),
KEY("key"),

View File

@@ -15,7 +15,7 @@ import lombok.Getter;
* Describing the fields of the Tag model which can be used in the REST API e.g. for sorting etc.
*/
@Getter
public enum TagFields implements RsqlQueryField {
public enum TagFields implements QueryField {
ID("id"),
NAME("name"),

View File

@@ -18,7 +18,7 @@ import lombok.Getter;
* e.g. for sorting etc.
*/
@Getter
public enum TargetFields implements RsqlQueryField {
public enum TargetFields implements QueryField {
ID("controllerId"),
NAME("name"),
@@ -62,7 +62,7 @@ public enum TargetFields implements RsqlQueryField {
@Override
public String getDefaultSubEntityAttribute() {
return this == TYPE ? TargetTypeFields.KEY.getJpaEntityFieldName() : RsqlQueryField.super.getDefaultSubEntityAttribute();
return this == TYPE ? TargetTypeFields.KEY.getJpaEntityFieldName() : QueryField.super.getDefaultSubEntityAttribute();
}
@Override

View File

@@ -18,7 +18,7 @@ import lombok.Getter;
* Describing the fields of the Target model which can be used in the REST API e.g. for sorting etc.
*/
@Getter
public enum TargetFilterQueryFields implements RsqlQueryField {
public enum TargetFilterQueryFields implements QueryField {
ID("id"),
NAME("name"),

View File

@@ -16,7 +16,7 @@ import lombok.Getter;
* Additionally, here were added fields for Target in order filtering over target fields also.
*/
@Getter
public enum TargetTagFields implements RsqlQueryField {
public enum TargetTagFields implements QueryField {
ID(TagFields.ID.getJpaEntityFieldName()),
NAME(TagFields.NAME.getJpaEntityFieldName()),

View File

@@ -15,7 +15,7 @@ import lombok.Getter;
* Describing the fields of the TargetType model which can be used in the REST API
*/
@Getter
public enum TargetTypeFields implements RsqlQueryField {
public enum TargetTypeFields implements QueryField {
ID("id"),
KEY("key"),

View File

@@ -18,7 +18,7 @@ import io.github.classgraph.ClassInfo;
import io.github.classgraph.ScanResult;
import org.junit.jupiter.api.Test;
class RsqlQueryFieldsTest {
class QueryFieldsTest {
/**
* Verifies that fields classes are correctly implemented
@@ -28,11 +28,11 @@ class RsqlQueryFieldsTest {
void repositoryManagementMethodsArePreAuthorizedAnnotated() {
final String packageName = getClass().getPackage().getName();
try (final ScanResult scanResult = new ClassGraph().acceptPackages(packageName).scan()) {
final List<? extends Class<? extends RsqlQueryField>> matchingClasses = scanResult.getAllClasses()
final List<? extends Class<? extends QueryField>> matchingClasses = scanResult.getAllClasses()
.stream()
.filter(classInPackage -> classInPackage.implementsInterface(RsqlQueryField.class))
.filter(classInPackage -> classInPackage.implementsInterface(QueryField.class))
.map(ClassInfo::loadClass)
.map(clazz -> (Class<? extends RsqlQueryField>) clazz)
.map(clazz -> (Class<? extends QueryField>) clazz)
.toList();
assertThat(matchingClasses).isNotEmpty();
matchingClasses.forEach(providerClass -> assertThat(providerClass.getEnumConstants()).isNotEmpty());

View File

@@ -18,7 +18,7 @@ import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.mgmt.rest.resource.exception.SortParameterSyntaxErrorException;
import org.eclipse.hawkbit.mgmt.rest.resource.exception.SortParameterUnsupportedDirectionException;
import org.eclipse.hawkbit.mgmt.rest.resource.exception.SortParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.RsqlQueryField;
import org.eclipse.hawkbit.repository.QueryField;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
@@ -44,15 +44,15 @@ public final class SortUtility {
* sorting will be mapped into the {@link Direction} enum.
*
* @param enumType the class of the enum which the fields in the sort string should be related to.
* @param <T> the type of the enumeration which must be derived from {@link RsqlQueryField}
* @param <T> the type of the enumeration which must be derived from {@link QueryField}
* @param sortString the string representation of the query parameters. Might be {@code null} or an empty string.
* @return a list which holds the {@link RsqlQueryField} and the specific {@link Direction} for them as a tuple. Never {@code null}.
* @return a list which holds the {@link QueryField} and the specific {@link Direction} for them as a tuple. Never {@code null}.
* In case of no sorting parameters an empty map will be returned.
* @throws SortParameterSyntaxErrorException if the sorting query parameter is not well-formed
* @throws SortParameterUnsupportedFieldException if a field name cannot be mapped to the enum type
* @throws SortParameterUnsupportedDirectionException if the given direction is not "ASC" or "DESC"
*/
public static <T extends Enum<T> & RsqlQueryField> List<Order> parse(final Class<T> enumType, final String sortString)
public static <T extends Enum<T> & QueryField> List<Order> parse(final Class<T> enumType, final String sortString)
throws SortParameterSyntaxErrorException {
final List<Order> orders = new ArrayList<>();
// scan the sort tuples e.g. field:direction

View File

@@ -0,0 +1,41 @@
/**
* 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.exception;
import java.io.Serial;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.eclipse.hawkbit.exception.AbstractServerRtException;
import org.eclipse.hawkbit.exception.SpServerError;
/**
* Exception used by the REST API in case of RSQL search filter query.
*/
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public abstract class QueryException extends AbstractServerRtException {
QueryException(final SpServerError error) {
super(error);
}
QueryException(final SpServerError error, final String message) {
super(error, message);
}
QueryException(final SpServerError error, final Throwable cause) {
super(error, cause);
}
QueryException(final SpServerError error, final String message, final Throwable cause) {
super(error, message, cause);
}
}

View File

@@ -21,7 +21,7 @@ import org.eclipse.hawkbit.exception.SpServerError;
*/
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class RSQLParameterSyntaxException extends AbstractServerRtException {
public class RSQLParameterSyntaxException extends QueryException {
@Serial
private static final long serialVersionUID = 1L;

View File

@@ -21,7 +21,7 @@ import org.eclipse.hawkbit.exception.SpServerError;
*/
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class RSQLParameterUnsupportedFieldException extends AbstractServerRtException {
public class RSQLParameterUnsupportedFieldException extends QueryException {
@Serial
private static final long serialVersionUID = 1L;

View File

@@ -12,6 +12,7 @@ package org.eclipse.hawkbit.repository.jpa.ql;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.UnaryOperator;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
@@ -38,6 +39,25 @@ public interface Node {
return op(this, Logical.Operator.OR, other);
}
// utility method that maps this node with a mapper that could modify comparisons - e.g. change keys, values, operators, or whatever
// if there are no changes the same instance is returned
default Node map(final UnaryOperator<Comparison> mapper) {
if (this instanceof Comparison comparison) {
return mapper.apply(comparison);
} else {
final List<Node> mappedChildren = new ArrayList<>();
boolean modified = false;
for (final Node child : ((Logical) this).getChildren()) {
final Node mapped = child.map(mapper);
mappedChildren.add(mapped);
if (!mapped.equals(child)) {
modified = true;
}
}
return modified ? new Logical(((Logical) this).getOp(), mappedChildren) : this;
}
}
static Logical op(final Node node1, final Logical.Operator op, final Node node2) {
if (node1 instanceof Logical logical1 && logical1.getOp() == op) {
if (node2 instanceof Logical logical2 && logical2.getOp() == op) { // same op
@@ -121,7 +141,7 @@ public interface Node {
private final String symbol;
Operator(String symbol) {
Operator(final String symbol) {
this.symbol = symbol;
}

View File

@@ -7,23 +7,26 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.rsql;
package org.eclipse.hawkbit.repository.jpa.ql;
import java.util.Objects;
import jakarta.persistence.EntityManager;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import cz.jirutka.rsql.parser.RSQLParserException;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.text.StrLookup;
import org.eclipse.hawkbit.repository.RsqlQueryField;
import org.eclipse.hawkbit.repository.QueryField;
import org.eclipse.hawkbit.repository.exception.QueryException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.jpa.ql.SpecificationBuilder;
import org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlParser;
import org.eclipse.hawkbit.repository.jpa.rsql.legacy.SpecificationBuilderLegacy;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyResolver;
@@ -69,20 +72,20 @@ import org.springframework.orm.jpa.vendor.Database;
@Slf4j
@Getter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class RsqlUtility {
public class QLSupport {
private static final RsqlUtility SINGLETON = new RsqlUtility();
private static final QLSupport SINGLETON = new QLSupport();
public enum RsqlToSpecBuilder {
public enum SpecBuilder {
LEGACY_G1, // legacy RSQL visitor
LEGACY_G2, // G2 RSQL visitor
G3 // G3 RSQL visitor - still experimental / yet default
G3 // G3 specification builder - still experimental / yet default
}
/**
* If RSQL comparison operators shall ignore the case. If ignore case is <code>true</code> "x == ax" will match "x == aX"
* If QL comparison operators shall ignore the case. If ignore case is <code>true</code> "x == ax" will match "x == aX"
*/
@Value("${hawkbit.rsql.ignore-case:true}")
@Value("${hawkbit.ql.ignore-case:true}")
private boolean ignoreCase;
/**
* Declares if the database is case-insensitive, by default assumes <code>false</code>. In case it is case-sensitive and,
@@ -100,8 +103,10 @@ public class RsqlUtility {
@Setter // for tests only
@Deprecated(forRemoval = true, since = "0.6.0")
@Value("${hawkbit.rsql.rsql-to-spec-builder:G3}") //
private RsqlToSpecBuilder rsqlToSpecBuilder;
private SpecBuilder specBuilder;
@SuppressWarnings({ "rawtypes", "unchecked" })
private QueryParser parser;
private VirtualPropertyReplacer virtualPropertyReplacer;
private Database database;
private EntityManager entityManager;
@@ -109,10 +114,15 @@ public class RsqlUtility {
/**
* @return The holder singleton instance.
*/
public static RsqlUtility getInstance() {
public static QLSupport getInstance() {
return SINGLETON;
}
@Autowired
void setQueryParser(final QueryParser parser) {
this.parser = parser;
}
@Autowired(required = false)
void setVirtualPropertyReplacer(final VirtualPropertyReplacer virtualPropertyReplacer) {
this.virtualPropertyReplacer = virtualPropertyReplacer;
@@ -132,37 +142,71 @@ public class RsqlUtility {
* Builds a JPA {@link Specification} which corresponds with the given RSQL query. The specification can be used to filter for JPA entities
* with the given RSQL query.
*
* @param rsql the rsql query to be parsed
* @param rsqlQueryFieldType the enum class type which implements the {@link RsqlQueryField}
* @param query the rsql query to be parsed
* @param queryFieldType the enum class type which implements the {@link QueryField}
* @return a specification which can be used with JPA
* @throws RSQLParameterUnsupportedFieldException if a field in the RSQL string is used but not provided by the
* given {@code fieldNameProvider}
* @throws RSQLParameterSyntaxException if the RSQL syntax is wrong
*/
public <A extends Enum<A> & RsqlQueryField, T> Specification<T> buildRsqlSpecification(
final String rsql, final Class<A> rsqlQueryFieldType) {
if (rsqlToSpecBuilder == RsqlToSpecBuilder.G3) {
return new SpecificationBuilder<T>(virtualPropertyReplacer, !caseInsensitiveDB && ignoreCase, database)
.specification(RsqlParser.parse(caseInsensitiveDB || ignoreCase ? rsql.toLowerCase() : rsql, rsqlQueryFieldType));
public <A extends Enum<A> & QueryField, T> Specification<T> buildSpec(
final String query, final Class<A> queryFieldType) {
if (specBuilder == SpecBuilder.G3) {
return new SpecificationBuilder<T>(!caseInsensitiveDB && ignoreCase, database)
.specification(parser.parse(caseInsensitiveDB || ignoreCase ? query.toLowerCase() : query, queryFieldType));
} else {
return new SpecificationBuilderLegacy<A, T>(rsqlQueryFieldType, virtualPropertyReplacer, database).specification(rsql);
return new SpecificationBuilderLegacy<A, T>(queryFieldType, virtualPropertyReplacer, database).specification(query);
}
}
/**
* Validates the RSQL string
* Validates the query string
*
* @param rsql RSQL string to validate
* @param rsqlQueryFieldType the enum class type which implements the {@link RsqlQueryField}
* @param query query string to validate
* @param queryFieldType the enum class type which implements the {@link QueryField}
* @param jpaType the JPA entity type to validate against
* @throws RSQLParserException if RSQL syntax is invalid
* @throws RSQLParameterUnsupportedFieldException if RSQL key is not allowed
* @throws QueryException if query is invalid
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public <A extends Enum<A> & RsqlQueryField> void validateRsqlFor(
final String rsql, final Class<A> rsqlQueryFieldType, final Class<?> jpaType) {
public <A extends Enum<A> & QueryField> void validateQuery(final String query, final Class<A> queryFieldType, final Class<?> jpaType) {
final CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
final CriteriaQuery<?> criteriaQuery = criteriaBuilder.createQuery(jpaType);
buildRsqlSpecification(rsql, rsqlQueryFieldType).toPredicate(criteriaQuery.from((Class) jpaType), criteriaQuery, criteriaBuilder);
buildSpec(query, queryFieldType).toPredicate(criteriaQuery.from((Class) jpaType), criteriaQuery, criteriaBuilder);
}
public interface QueryParser {
<T extends Enum<T> & QueryField> Node parse(final String query, final Class<T> queryFieldType) throws QueryException;
}
public static class DefaultQueryParser implements QueryParser {
private VirtualPropertyReplacer virtualPropertyReplacer;
@Autowired(required = false)
void setVirtualPropertyReplacer(final VirtualPropertyReplacer virtualPropertyReplacer) {
this.virtualPropertyReplacer = virtualPropertyReplacer;
}
@Override
public <T extends Enum<T> & QueryField> Node parse(final String query, final Class<T> queryFieldType) throws QueryException {
return RsqlParser.parse(query, queryFieldType).map(this::map);
}
protected Comparison map(final Comparison comparison) {
final String key = mapKey(comparison.getKey(), comparison).toString();
final Object value = mapValue(comparison.getValue(), comparison);
return key.equals(comparison.getKey()) && Objects.equals(value, comparison.getValue())
? comparison : Comparison.builder().key(key).op(comparison.getOp()).value(value).build();
}
// just extension points for subclasses
protected Object mapKey(final String key, final Comparison comparison) {
return key;
}
protected Object mapValue(final Object value, final Comparison comparison) {
return value instanceof String strValue ? virtualPropertyReplacer.replace(strValue) : value;
}
}
}

View File

@@ -63,14 +63,10 @@ public class SpecificationBuilder<T> {
private static final char ESCAPE_CHAR = '\\';
private final VirtualPropertyReplacer virtualPropertyReplacer;
private final boolean ensureIgnoreCase;
private final Database database;
public SpecificationBuilder(
final VirtualPropertyReplacer virtualPropertyReplacer,
final boolean ensureIgnoreCase, final Database database) {
this.virtualPropertyReplacer = virtualPropertyReplacer;
public SpecificationBuilder(final boolean ensureIgnoreCase, final Database database) {
this.ensureIgnoreCase = ensureIgnoreCase;
this.database = database;
}
@@ -244,10 +240,6 @@ public class SpecificationBuilder<T> {
private List<Object> getValues(final Comparison comparison, final Class<?> javaType) {
final Object value = comparison.getValue();
final List<Object> values = (value == null ? NULL_VALUE : (value instanceof List<?> list ? list : List.of(value))).stream()
// if lookup is available, replace macros ...
.map(element -> virtualPropertyReplacer != null && element instanceof String strElement
? virtualPropertyReplacer.replace(strElement)
: element)
// converts value to the correct type
.map(element -> element instanceof String strElement
? convertValueIfNecessary(strElement, javaType, comparison)

View File

@@ -17,9 +17,9 @@ import jakarta.persistence.TypedQuery;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import org.eclipse.hawkbit.repository.RsqlQueryField;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility.RsqlToSpecBuilder;
import org.eclipse.hawkbit.repository.QueryField;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport.SpecBuilder;
public class HawkbitQlToSql {
@@ -31,8 +31,8 @@ public class HawkbitQlToSql {
isEclipselink = entityManager.getProperties().keySet().stream().anyMatch(key -> key.startsWith("eclipselink."));
}
public <T, A extends Enum<A> & RsqlQueryField> String toSQL(
final Class<T> domainClass, final Class<A> fieldsClass, final String rsql, final RsqlToSpecBuilder rsqlToSpecBuilder) {
public <T, A extends Enum<A> & QueryField> String toSQL(
final Class<T> domainClass, final Class<A> fieldsClass, final String rsql, final SpecBuilder rsqlToSpecBuilder) {
final CriteriaQuery<T> query = createQuery(domainClass, fieldsClass, rsql, rsqlToSpecBuilder);
final TypedQuery<?> typedQuery = entityManager.createQuery(query);
if (isEclipselink) {
@@ -54,20 +54,20 @@ public class HawkbitQlToSql {
}
}
private <T, A extends Enum<A> & RsqlQueryField> CriteriaQuery<T> createQuery(
final Class<T> domainClass, final Class<A> fieldsClass, final String rsql, final RsqlToSpecBuilder rsqlToSpecBuilder) {
private <T, A extends Enum<A> & QueryField> CriteriaQuery<T> createQuery(
final Class<T> domainClass, final Class<A> fieldsClass, final String rsql, final SpecBuilder rsqlToSpecBuilder) {
final CriteriaQuery<T> query = entityManager.getCriteriaBuilder().createQuery(domainClass);
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final RsqlToSpecBuilder defaultRsqlToSpecBuilder = RsqlUtility.getInstance().getRsqlToSpecBuilder();
final SpecBuilder defaultRsqlToSpecBuilder = QLSupport.getInstance().getSpecBuilder();
if (defaultRsqlToSpecBuilder != rsqlToSpecBuilder) {
RsqlUtility.getInstance().setRsqlToSpecBuilder(rsqlToSpecBuilder);
QLSupport.getInstance().setSpecBuilder(rsqlToSpecBuilder);
}
try {
return query.where(RsqlUtility.getInstance().<A, T> buildRsqlSpecification(rsql, fieldsClass)
return query.where(QLSupport.getInstance().<A, T> buildSpec(rsql, fieldsClass)
.toPredicate(query.from(domainClass), cb.createQuery(domainClass), cb));
} finally {
if (defaultRsqlToSpecBuilder != rsqlToSpecBuilder) {
RsqlUtility.getInstance().setRsqlToSpecBuilder(defaultRsqlToSpecBuilder);
QLSupport.getInstance().setSpecBuilder(defaultRsqlToSpecBuilder);
}
}
}

View File

@@ -9,8 +9,8 @@
*/
package org.eclipse.hawkbit.repository.jpa.rsql;
import static org.eclipse.hawkbit.repository.RsqlQueryField.SUB_ATTRIBUTE_SEPARATOR;
import static org.eclipse.hawkbit.repository.RsqlQueryField.SUB_ATTRIBUTE_SPLIT_REGEX;
import static org.eclipse.hawkbit.repository.QueryField.SUB_ATTRIBUTE_SEPARATOR;
import static org.eclipse.hawkbit.repository.QueryField.SUB_ATTRIBUTE_SPLIT_REGEX;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.EQ;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.GT;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.GTE;
@@ -42,7 +42,7 @@ import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.FieldValueConverter;
import org.eclipse.hawkbit.repository.RsqlQueryField;
import org.eclipse.hawkbit.repository.QueryField;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.jpa.ql.Node;
@@ -82,8 +82,8 @@ public class RsqlParser {
return parse(rsql, (Function<String, Key>) null);
}
public static <T extends Enum<T> & RsqlQueryField> Node parse(final String rsql, final Class<T> rsqlQueryFieldType) {
return parse(rsql, rsqlQueryFieldType == null ? null : key -> resolveKey(key, rsqlQueryFieldType));
public static <T extends Enum<T> & QueryField> Node parse(final String rsql, final Class<T> queryFieldType) {
return parse(rsql, queryFieldType == null ? null : key -> resolveKey(key, queryFieldType));
}
private static Node parse(final String rsql, final Function<String, Key> keyResolver) {
@@ -97,7 +97,7 @@ public class RsqlParser {
}
@SuppressWarnings("java:S3776") // java:S3776 - group in single method for easier read of whole logic
private static <T extends Enum<T> & RsqlQueryField> Key resolveKey(final String key, final Class<T> rsqlQueryFieldType) {
private static <T extends Enum<T> & QueryField> Key resolveKey(final String key, final Class<T> rsqlQueryFieldType) {
final int firstSeparatorIndex = key.indexOf(SUB_ATTRIBUTE_SEPARATOR);
final String enumName = (firstSeparatorIndex == -1 ? key : key.substring(0, firstSeparatorIndex)).toUpperCase();
log.debug("Get field identifier by name {} of enum type {}", enumName, rsqlQueryFieldType);
@@ -151,9 +151,7 @@ public class RsqlParser {
}
}
return new
Key(attribute, RsqlVisitor.valueConverter(enumValue));
return new Key(attribute, RsqlVisitor.valueConverter(enumValue));
}
private record Key(String path, UnaryOperator<Object> converter) {}

View File

@@ -23,7 +23,7 @@ import cz.jirutka.rsql.parser.ast.ComparisonOperator;
import cz.jirutka.rsql.parser.ast.RSQLOperators;
import lombok.Value;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.RsqlQueryField;
import org.eclipse.hawkbit.repository.QueryField;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.jpa.ql.SpecificationBuilder;
import org.springframework.util.ObjectUtils;
@@ -33,7 +33,7 @@ import org.springframework.util.ObjectUtils;
*/
@Deprecated(forRemoval = true, since = "0.9.0")
@Slf4j
public abstract class AbstractRSQLVisitor<A extends Enum<A> & RsqlQueryField> {
public abstract class AbstractRSQLVisitor<A extends Enum<A> & QueryField> {
static final ComparisonOperator IS = new ComparisonOperator("=is=", "=eq=");
static final ComparisonOperator NOT = new ComparisonOperator("=not=", "=ne=");
@@ -56,7 +56,7 @@ public abstract class AbstractRSQLVisitor<A extends Enum<A> & RsqlQueryField> {
@SuppressWarnings("java:S1066") // java:S1066 - more readable with separate "if" statements
protected QueryPath getQueryPath(final ComparisonNode node) {
final int firstSeparatorIndex = node.getSelector().indexOf(RsqlQueryField.SUB_ATTRIBUTE_SEPARATOR);
final int firstSeparatorIndex = node.getSelector().indexOf(QueryField.SUB_ATTRIBUTE_SEPARATOR);
final String enumName = (firstSeparatorIndex == -1
? node.getSelector()
: node.getSelector().substring(0, firstSeparatorIndex)).toUpperCase();
@@ -99,14 +99,14 @@ public abstract class AbstractRSQLVisitor<A extends Enum<A> & RsqlQueryField> {
private String[] getSplit(final A enumValue, final String rsqlFieldName) {
if (enumValue.isMap()) {
final String[] split = rsqlFieldName.split(RsqlQueryField.SUB_ATTRIBUTE_SPLIT_REGEX, 2);
final String[] split = rsqlFieldName.split(QueryField.SUB_ATTRIBUTE_SPLIT_REGEX, 2);
if (split.length != 2 || ObjectUtils.isEmpty(split[1])) {
throw new RSQLParameterUnsupportedFieldException(
"The syntax of the given map search parameter field {" + rsqlFieldName + "} is wrong. Syntax is: <enum name>.<key name>");
}
return split;
} else {
return rsqlFieldName.split(RsqlQueryField.SUB_ATTRIBUTE_SPLIT_REGEX);
return rsqlFieldName.split(QueryField.SUB_ATTRIBUTE_SPLIT_REGEX);
}
}
@@ -123,7 +123,7 @@ public abstract class AbstractRSQLVisitor<A extends Enum<A> & RsqlQueryField> {
}
for (final String attribute : subEntityAttributes) {
final String[] graph = attribute.split(RsqlQueryField.SUB_ATTRIBUTE_SPLIT_REGEX);
final String[] graph = attribute.split(QueryField.SUB_ATTRIBUTE_SPLIT_REGEX);
for (final String subAttribute : graph) {
if (subAttribute.equalsIgnoreCase(propertyField)) {
return true;
@@ -158,7 +158,7 @@ public abstract class AbstractRSQLVisitor<A extends Enum<A> & RsqlQueryField> {
.filter(enumField -> enumField.getSubEntityAttributes().isEmpty()).map(enumField -> {
final String enumFieldName = enumField.name().toLowerCase();
if (enumField.isMap()) {
return enumFieldName + RsqlQueryField.SUB_ATTRIBUTE_SEPARATOR + "keyName";
return enumFieldName + QueryField.SUB_ATTRIBUTE_SEPARATOR + "keyName";
} else {
return enumFieldName;
}
@@ -167,7 +167,7 @@ public abstract class AbstractRSQLVisitor<A extends Enum<A> & RsqlQueryField> {
.filter(enumField -> !enumField.getSubEntityAttributes().isEmpty()).flatMap(enumField -> {
final List<String> subEntity = enumField
.getSubEntityAttributes().stream().map(fieldName -> enumField.name().toLowerCase()
+ RsqlQueryField.SUB_ATTRIBUTE_SEPARATOR + fieldName)
+ QueryField.SUB_ATTRIBUTE_SEPARATOR + fieldName)
.toList();
return subEntity.stream();

View File

@@ -43,7 +43,7 @@ import cz.jirutka.rsql.parser.ast.RSQLVisitor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.math.NumberUtils;
import org.eclipse.hawkbit.repository.FieldValueConverter;
import org.eclipse.hawkbit.repository.RsqlQueryField;
import org.eclipse.hawkbit.repository.QueryField;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
@@ -65,7 +65,7 @@ import org.springframework.util.ObjectUtils;
*/
@Deprecated(forRemoval = true, since = "0.6.0")
@Slf4j
public class JpaQueryRsqlVisitor<A extends Enum<A> & RsqlQueryField, T> extends AbstractRSQLVisitor<A>
public class JpaQueryRsqlVisitor<A extends Enum<A> & QueryField, T> extends AbstractRSQLVisitor<A>
implements RSQLVisitor<List<Predicate>, String> {
public static final Character LIKE_WILDCARD = '*';

View File

@@ -41,7 +41,7 @@ import cz.jirutka.rsql.parser.ast.RSQLVisitor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.math.NumberUtils;
import org.eclipse.hawkbit.repository.FieldValueConverter;
import org.eclipse.hawkbit.repository.RsqlQueryField;
import org.eclipse.hawkbit.repository.QueryField;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.jpa.ql.SpecificationBuilder;
@@ -62,7 +62,7 @@ import org.springframework.util.ObjectUtils;
*/
@Deprecated(forRemoval = true, since = "0.9.0")
@Slf4j
public class JpaQueryRsqlVisitorG2<A extends Enum<A> & RsqlQueryField, T>
public class JpaQueryRsqlVisitorG2<A extends Enum<A> & QueryField, T>
extends AbstractRSQLVisitor<A> implements RSQLVisitor<List<Predicate>, String> {
public static final Character LIKE_WILDCARD = '*';

View File

@@ -10,7 +10,7 @@
package org.eclipse.hawkbit.repository.jpa.rsql.legacy;
import static org.eclipse.hawkbit.repository.jpa.rsql.legacy.AbstractRSQLVisitor.OPERATORS;
import static org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility.RsqlToSpecBuilder.LEGACY_G1;
import static org.eclipse.hawkbit.repository.jpa.ql.QLSupport.SpecBuilder.LEGACY_G1;
import java.util.List;
@@ -21,10 +21,10 @@ import cz.jirutka.rsql.parser.RSQLParserException;
import cz.jirutka.rsql.parser.ast.Node;
import cz.jirutka.rsql.parser.ast.RSQLVisitor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.RsqlQueryField;
import org.eclipse.hawkbit.repository.QueryField;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.jpa.ql.SpecificationBuilder;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.orm.jpa.vendor.Database;
@@ -35,7 +35,7 @@ import org.springframework.util.CollectionUtils;
*/
@Deprecated(forRemoval = true, since = "0.9.0")
@Slf4j
public class SpecificationBuilderLegacy<A extends Enum<A> & RsqlQueryField, T> {
public class SpecificationBuilderLegacy<A extends Enum<A> & QueryField, T> {
private final Class<A> rsqlQueryFieldType;
private final VirtualPropertyReplacer virtualPropertyReplacer;
@@ -50,14 +50,14 @@ public class SpecificationBuilderLegacy<A extends Enum<A> & RsqlQueryField, T> {
public Specification<T> specification(final String rsql) {
return (root, query, cb) -> {
final RsqlUtility rsqlUtility = RsqlUtility.getInstance();
final QLSupport rsqlUtility = QLSupport.getInstance();
final Node rootNode = parseRsql(rsql, rsqlUtility);
query.distinct(true);
final boolean ensureIgnoreCase = !rsqlUtility.isCaseInsensitiveDB() && rsqlUtility.isIgnoreCase();
final RSQLVisitor<List<Predicate>, String> jpqQueryRSQLVisitor =
rsqlUtility.getRsqlToSpecBuilder() == LEGACY_G1
rsqlUtility.getSpecBuilder() == LEGACY_G1
? new JpaQueryRsqlVisitor<>(root, cb, rsqlQueryFieldType, virtualPropertyReplacer, database, query, ensureIgnoreCase)
: new JpaQueryRsqlVisitorG2<>(rsqlQueryFieldType, root, query, cb, database, virtualPropertyReplacer, ensureIgnoreCase);
final List<Predicate> accept = rootNode.accept(jpqQueryRSQLVisitor);
@@ -70,7 +70,7 @@ public class SpecificationBuilderLegacy<A extends Enum<A> & RsqlQueryField, T> {
};
}
private static Node parseRsql(final String rsql, final RsqlUtility rsqlUtility) {
private static Node parseRsql(final String rsql, final QLSupport rsqlUtility) {
log.debug("Parsing rsql string {}", rsql);
try {
return new RSQLParser(OPERATORS).parse(rsqlUtility.isCaseInsensitiveDB() || rsqlUtility.isIgnoreCase() ? rsql.toLowerCase() : rsql);

View File

@@ -9,17 +9,16 @@
*/
package org.eclipse.hawkbit.repository.jpa.ql;
import static org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility.RsqlToSpecBuilder.LEGACY_G1;
import static org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility.RsqlToSpecBuilder.LEGACY_G2;
import static org.eclipse.hawkbit.repository.jpa.ql.QLSupport.SpecBuilder.LEGACY_G1;
import static org.eclipse.hawkbit.repository.jpa.ql.QLSupport.SpecBuilder.LEGACY_G2;
import java.util.Arrays;
import java.util.List;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.RsqlQueryField;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility.RsqlToSpecBuilder;
import org.eclipse.hawkbit.repository.QueryField;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport.SpecBuilder;
import org.eclipse.hawkbit.repository.jpa.rsql.legacy.SpecificationBuilderLegacy;
import org.junit.jupiter.api.Test;
import org.springframework.data.jpa.domain.Specification;
@@ -31,13 +30,13 @@ class SpecificationBuilderLegacyTest extends SpecificationBuilderTest {
private final SpecificationBuilderLegacy<RootField, Root> builder = new SpecificationBuilderLegacy<>(RootField.class, null, Database.H2);
private static void runWithRsqlToSpecBuilder(final Runnable runnable, final RsqlToSpecBuilder rsqlToSpecBuilder) {
final RsqlToSpecBuilder defaultBuilder = RsqlUtility.getInstance().getRsqlToSpecBuilder();
RsqlUtility.getInstance().setRsqlToSpecBuilder(rsqlToSpecBuilder);
private static void runWithRsqlToSpecBuilder(final Runnable runnable, final SpecBuilder rsqlToSpecBuilder) {
final SpecBuilder defaultBuilder = QLSupport.getInstance().getSpecBuilder();
QLSupport.getInstance().setSpecBuilder(rsqlToSpecBuilder);
try {
runnable.run();
} finally {
RsqlUtility.getInstance().setRsqlToSpecBuilder(defaultBuilder);
QLSupport.getInstance().setSpecBuilder(defaultBuilder);
}
}
@@ -110,7 +109,7 @@ class SpecificationBuilderLegacyTest extends SpecificationBuilderTest {
}
@Getter
private enum RootField implements RsqlQueryField {
private enum RootField implements QueryField {
INTVALUE("intValue"),
STRVALUE("strValue"),

View File

@@ -10,9 +10,9 @@
package org.eclipse.hawkbit.repository.jpa.ql;
import static org.assertj.core.api.Assertions.assertThat;
import static org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility.RsqlToSpecBuilder.G3;
import static org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility.RsqlToSpecBuilder.LEGACY_G1;
import static org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility.RsqlToSpecBuilder.LEGACY_G2;
import static org.eclipse.hawkbit.repository.jpa.ql.QLSupport.SpecBuilder.G3;
import static org.eclipse.hawkbit.repository.jpa.ql.QLSupport.SpecBuilder.LEGACY_G1;
import static org.eclipse.hawkbit.repository.jpa.ql.QLSupport.SpecBuilder.LEGACY_G2;
import java.util.List;
import java.util.Map;
@@ -22,9 +22,9 @@ import java.util.stream.StreamSupport;
import jakarta.persistence.EntityManager;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport.SpecBuilder;
import org.eclipse.hawkbit.repository.jpa.ql.utils.HawkbitQlToSql;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlParser;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
@@ -51,7 +51,7 @@ class SpecificationBuilderTest {
@Autowired
private EntityManager entityManager;
private final SpecificationBuilder<Root> builder = new SpecificationBuilder<>(null, false, Database.H2);
private final SpecificationBuilder<Root> builder = new SpecificationBuilder<>(false, Database.H2);
@Test
void singularStringAttribute() {
@@ -573,8 +573,8 @@ class SpecificationBuilderTest {
return builder.specification(RsqlParser.parse(rsql));
}
private static RsqlUtility.RsqlToSpecBuilder getRsqlToSpecBuilder() {
return RsqlUtility.getInstance().getRsqlToSpecBuilder();
private static SpecBuilder getRsqlToSpecBuilder() {
return QLSupport.getInstance().getSpecBuilder();
}
@SpringBootConfiguration

View File

@@ -91,11 +91,9 @@ public class DefaultRolloutApprovalStrategy implements RolloutApprovalStrategy {
// scheduler under SYSTEM user context, thus we get the
// user based on the properties of initially created rollout entity
if (RolloutStatus.CREATING == rollout.getStatus()) {
final String actor = rollout.getLastModifiedBy() != null ? rollout.getLastModifiedBy()
: rollout.getCreatedBy();
final String actor = rollout.getLastModifiedBy() != null ? rollout.getLastModifiedBy() : rollout.getCreatedBy();
if (!ObjectUtils.isEmpty(actor)) {
return systemSecurityContext.runAsSystem(
() -> userAuthoritiesResolver.getUserAuthorities(rollout.getTenant(), actor));
return systemSecurityContext.runAsSystem(() -> userAuthoritiesResolver.getUserAuthorities(rollout.getTenant(), actor));
}
}

View File

@@ -67,6 +67,8 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder;
import org.eclipse.hawkbit.repository.jpa.model.helper.EntityInterceptorHolder;
import org.eclipse.hawkbit.repository.jpa.model.helper.TenantAwareHolder;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport.DefaultQueryParser;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport.QueryParser;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTypeRepository;
@@ -86,7 +88,7 @@ import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupEvaluati
import org.eclipse.hawkbit.repository.jpa.rollout.condition.StartNextGroupRolloutGroupSuccessAction;
import org.eclipse.hawkbit.repository.jpa.rollout.condition.ThresholdRolloutGroupErrorCondition;
import org.eclipse.hawkbit.repository.jpa.rollout.condition.ThresholdRolloutGroupSuccessCondition;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
import org.eclipse.hawkbit.repository.jpa.utils.ExceptionMapper;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
@@ -513,14 +515,20 @@ public class JpaRepositoryConfiguration {
return new RolloutScheduler(rolloutHandler, systemManagement, systemSecurityContext, threadPoolSize, meterRegistry);
}
@Bean
@ConditionalOnMissingBean
QueryParser queryParser() {
return new DefaultQueryParser();
}
/**
* Register the {@link RsqlUtility} bean to force Spring to inject values and auto-wired fields.
* Register the {@link QLSupport} bean to force Spring to inject values and auto-wired fields.
*
* @return The {@link RsqlUtility} singleton.
* @return The {@link QLSupport} singleton.
*/
@Bean
RsqlUtility rsqlUtility() {
return RsqlUtility.getInstance();
QLSupport rsqlUtility() {
return QLSupport.getInstance();
}
/**

View File

@@ -21,10 +21,10 @@ import java.util.regex.Pattern;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.ContextAware;
import org.eclipse.hawkbit.repository.RsqlQueryField;
import org.eclipse.hawkbit.repository.QueryField;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.jpa.ql.EntityMatcher;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.security.core.GrantedAuthority;
@@ -32,7 +32,7 @@ import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.ObjectUtils;
@Slf4j
public class DefaultAccessController<A extends Enum<A> & RsqlQueryField, T> implements AccessController<T> {
public class DefaultAccessController<A extends Enum<A> & QueryField, T> implements AccessController<T> {
private final Class<A> rsqlQueryFieldType;
private final Map<Operation, List<String>> permissions = new EnumMap<>(Operation.class);
@@ -69,7 +69,7 @@ public class DefaultAccessController<A extends Enum<A> & RsqlQueryField, T> impl
scopes.size() == 1
? scopes.get(0) // single scope
: "(" + String.join(") or (", scopes) + ")") // join multiple scopes with 'or' - union
.map(rsql -> RsqlUtility.getInstance().buildRsqlSpecification(rsql, rsqlQueryFieldType));
.map(rsql -> QLSupport.getInstance().buildSpec(rsql, rsqlQueryFieldType));
}
@Override

View File

@@ -39,7 +39,7 @@ import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.Identifiable;
import org.eclipse.hawkbit.repository.RepositoryManagement;
import org.eclipse.hawkbit.repository.RsqlQueryField;
import org.eclipse.hawkbit.repository.QueryField;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InvalidDistributionSetException;
import org.eclipse.hawkbit.repository.jpa.Jpa;
@@ -49,7 +49,7 @@ import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
import org.eclipse.hawkbit.repository.jpa.repository.BaseEntityRepository;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
import org.eclipse.hawkbit.utils.ObjectCopyUtil;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
@@ -79,7 +79,7 @@ import org.springframework.validation.annotation.Validated;
@Transactional(readOnly = true)
@Validated
@Slf4j
abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity, C, U extends Identifiable<Long>, R extends BaseEntityRepository<T>, A extends Enum<A> & RsqlQueryField>
abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity, C, U extends Identifiable<Long>, R extends BaseEntityRepository<T>, A extends Enum<A> & QueryField>
implements RepositoryManagement<T, C, U> {
public static final String DELETED = "deleted";
@@ -258,8 +258,8 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
@NotNull
private List<Specification<T>> rsqlSpec(final String rsql) {
return isNotDeleted()
.map(isNotDeleted -> List.of(RsqlUtility.getInstance().<A, T> buildRsqlSpecification(rsql, fieldsClass()), isNotDeleted))
.orElseGet(() -> List.of(RsqlUtility.getInstance().buildRsqlSpecification(rsql, fieldsClass())));
.map(isNotDeleted -> List.of(QLSupport.getInstance().<A, T> buildSpec(rsql, fieldsClass()), isNotDeleted))
.orElseGet(() -> List.of(QLSupport.getInstance().buildSpec(rsql, fieldsClass())));
}
protected void checkUpdate(final U update, final T distributionSet) {}

View File

@@ -26,9 +26,8 @@ import jakarta.persistence.EntityManager;
import org.eclipse.hawkbit.repository.Identifiable;
import org.eclipse.hawkbit.repository.MetadataSupport;
import org.eclipse.hawkbit.repository.RsqlQueryField;
import org.eclipse.hawkbit.repository.QueryField;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InvalidDistributionSetException;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity;
import org.eclipse.hawkbit.repository.jpa.model.WithMetadata;
import org.eclipse.hawkbit.repository.jpa.repository.BaseEntityRepository;
@@ -39,7 +38,7 @@ import org.springframework.retry.annotation.Retryable;
import org.springframework.transaction.annotation.Transactional;
@SuppressWarnings("java:S119") // java:S119 - better self explainable
abstract class AbstractJpaRepositoryWithMetadataManagement<T extends AbstractJpaBaseEntity & WithMetadata<MV, MVI>, C, U extends Identifiable<Long>, R extends BaseEntityRepository<T>, A extends Enum<A> & RsqlQueryField, MV, MVI extends MV>
abstract class AbstractJpaRepositoryWithMetadataManagement<T extends AbstractJpaBaseEntity & WithMetadata<MV, MVI>, C, U extends Identifiable<Long>, R extends BaseEntityRepository<T>, A extends Enum<A> & QueryField, MV, MVI extends MV>
extends AbstractJpaRepositoryManagement<T, C, U, R, A> implements MetadataSupport<MV> {
private final Supplier<MVI> metadataValueCreator;

View File

@@ -65,7 +65,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.repository.ActionStatusRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
import org.eclipse.hawkbit.repository.jpa.specifications.ActionSpecifications;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper;
@@ -252,7 +252,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
assertTargetReadAllowed(controllerId);
final List<Specification<JpaAction>> specList = Arrays.asList(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, ActionFields.class),
QLSupport.getInstance().buildSpec(rsql, ActionFields.class),
ActionSpecifications.byTargetControllerId(controllerId));
return JpaManagementHelper.countBySpec(actionRepository, specList);
@@ -265,7 +265,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
@Override
public long countActions(final String rsql) {
final List<Specification<JpaAction>> specList = List.of(RsqlUtility.getInstance().buildRsqlSpecification(rsql, ActionFields.class));
final List<Specification<JpaAction>> specList = List.of(QLSupport.getInstance().buildSpec(rsql, ActionFields.class));
return JpaManagementHelper.countBySpec(actionRepository, specList);
}
@@ -288,7 +288,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
@Override
public Slice<Action> findActions(final String rsql, final Pageable pageable) {
final List<Specification<JpaAction>> specList = List.of(RsqlUtility.getInstance().buildRsqlSpecification(rsql, ActionFields.class));
final List<Specification<JpaAction>> specList = List.of(QLSupport.getInstance().buildSpec(rsql, ActionFields.class));
return JpaManagementHelper.findAllWithoutCountBySpec(actionRepository, specList, pageable);
}
@@ -297,7 +297,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
assertTargetReadAllowed(controllerId);
final List<Specification<JpaAction>> specList = Arrays.asList(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, ActionFields.class),
QLSupport.getInstance().buildSpec(rsql, ActionFields.class),
ActionSpecifications.byTargetControllerId(controllerId));
return JpaManagementHelper.findAllWithCountBySpec(actionRepository, specList, pageable);

View File

@@ -18,7 +18,6 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
@@ -47,7 +46,7 @@ import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTagRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetFilterQueryRepository;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
import org.eclipse.hawkbit.repository.jpa.specifications.DistributionSetSpecification;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -324,7 +323,7 @@ public class JpaDistributionSetManagement
return JpaManagementHelper.findAllWithCountBySpec(
jpaRepository,
List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, DistributionSetFields.class),
QLSupport.getInstance().buildSpec(rsql, DistributionSetFields.class),
DistributionSetSpecification.hasTag(tagId), DistributionSetSpecification.isNotDeleted()),
pageable);
}

View File

@@ -13,7 +13,6 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import jakarta.persistence.EntityManager;
@@ -40,7 +39,7 @@ import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
import org.eclipse.hawkbit.repository.jpa.repository.RolloutRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
@@ -136,7 +135,7 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
throwEntityNotFoundExceptionIfRolloutDoesNotExist(rolloutId);
final List<Specification<JpaRolloutGroup>> specList = Arrays.asList(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, RolloutGroupFields.class),
QLSupport.getInstance().buildSpec(rsql, RolloutGroupFields.class),
(root, query, cb) -> cb.equal(root.get(JpaRolloutGroup_.rollout).get(AbstractJpaBaseEntity_.id), rolloutId));
return JpaManagementHelper.findAllWithCountBySpec(rolloutGroupRepository, specList, pageable);
@@ -194,7 +193,7 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
throwExceptionIfRolloutGroupDoesNotExist(rolloutGroupId);
final List<Specification<JpaTarget>> specList = Arrays.asList(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetFields.class),
QLSupport.getInstance().buildSpec(rsql, TargetFields.class),
(root, query, cb) -> {
final ListJoin<JpaTarget, RolloutTargetGroup> rolloutTargetJoin = root.join(JpaTarget_.rolloutTargetGroup);
return cb.equal(rolloutTargetJoin.get(RolloutTargetGroup_.rolloutGroup).get(AbstractJpaBaseEntity_.id), rolloutGroupId);

View File

@@ -66,7 +66,7 @@ import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
import org.eclipse.hawkbit.repository.jpa.repository.RolloutRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.rollout.condition.StartNextGroupRolloutGroupSuccessAction;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
import org.eclipse.hawkbit.repository.jpa.specifications.ActionSpecifications;
import org.eclipse.hawkbit.repository.jpa.specifications.RolloutSpecification;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
@@ -285,7 +285,7 @@ public class JpaRolloutManagement implements RolloutManagement {
@Override
public Page<Rollout> findByRsql(final String rsql, final boolean deleted, final Pageable pageable) {
final List<Specification<JpaRollout>> specList = List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, RolloutFields.class),
QLSupport.getInstance().buildSpec(rsql, RolloutFields.class),
RolloutSpecification.isDeleted(deleted, pageable.getSort()));
return JpaManagementHelper.convertPage(rolloutRepository.findAll(JpaManagementHelper.combineWithAnd(specList), pageable), pageable);
}
@@ -293,7 +293,7 @@ public class JpaRolloutManagement implements RolloutManagement {
@Override
public Page<Rollout> findByRsqlWithDetailedStatus(final String rsql, final boolean deleted, final Pageable pageable) {
final List<Specification<JpaRollout>> specList = List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, RolloutFields.class),
QLSupport.getInstance().buildSpec(rsql, RolloutFields.class),
RolloutSpecification.isDeleted(deleted, pageable.getSort()));
return appendStatusDetails(JpaManagementHelper.convertPage(
rolloutRepository.findAll(JpaManagementHelper.combineWithAnd(specList), JpaRollout_.GRAPH_ROLLOUT_DS, pageable), pageable));

View File

@@ -36,7 +36,7 @@ import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
import org.eclipse.hawkbit.repository.jpa.repository.TargetFilterQueryRepository;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetFilterQuerySpecification;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
import org.eclipse.hawkbit.repository.jpa.utils.WeightValidationHelper;
@@ -117,7 +117,7 @@ class JpaTargetFilterQueryManagement
@Override
public void verifyTargetFilterQuerySyntax(final String query) {
try {
RsqlUtility.getInstance().validateRsqlFor(query, TargetFields.class, JpaTarget.class);
QLSupport.getInstance().validateQuery(query, TargetFields.class, JpaTarget.class);
} catch (final RSQLParserException | RSQLParameterUnsupportedFieldException e) {
log.debug("The RSQL query '{}}' is invalid.", query, e);
throw new RSQLParameterSyntaxException("Cannot create a Rollout with an empty target query filter!");
@@ -136,7 +136,7 @@ class JpaTargetFilterQueryManagement
final List<Specification<JpaTargetFilterQuery>> specList = new ArrayList<>(2);
specList.add(TargetFilterQuerySpecification.byAutoAssignDS(distributionSet));
if (!ObjectUtils.isEmpty(rsql)) {
specList.add(RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetFilterQueryFields.class));
specList.add(QLSupport.getInstance().buildSpec(rsql, TargetFilterQueryFields.class));
}
return JpaManagementHelper.findAllWithCountBySpec(jpaRepository, specList, pageable);
@@ -225,7 +225,7 @@ class JpaTargetFilterQueryManagement
});
Optional.ofNullable(create.getQuery()).ifPresent(query -> {
// validate the RSQL query syntax
RsqlUtility.getInstance().validateRsqlFor(query, TargetFields.class, JpaTarget.class);
QLSupport.getInstance().validateQuery(query, TargetFields.class, JpaTarget.class);
// enforce the 'max targets per auto assign' quota right here even if the result of the filter query can vary over time
Optional.ofNullable(create.getAutoAssignDistributionSet()).ifPresent(dsId -> {
@@ -242,7 +242,7 @@ class JpaTargetFilterQueryManagement
final JpaTargetFilterQuery targetFilterQuery = jpaRepository.getById(update.getId());
Optional.ofNullable(update.getQuery()).ifPresent(query -> {
// validate the RSQL query syntax
RsqlUtility.getInstance().validateRsqlFor(query, TargetFields.class, JpaTarget.class);
QLSupport.getInstance().validateQuery(query, TargetFields.class, JpaTarget.class);
Optional.ofNullable(targetFilterQuery.getAutoAssignDistributionSet()).ifPresent(autoAssignDs -> {
// enforce the 'max targets per auto assignment'-quota only if the query is going to change

View File

@@ -50,7 +50,7 @@ import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetTagRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetTypeRepository;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
@@ -59,7 +59,6 @@ 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.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;
@@ -112,11 +111,11 @@ public class JpaTargetManagement
@Override
public boolean isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(
final String controllerId, final long distributionSetId, final String targetFilterQuery) {
RsqlUtility.getInstance().validateRsqlFor(targetFilterQuery, TargetFields.class, JpaTarget.class);
QLSupport.getInstance().validateQuery(targetFilterQuery, TargetFields.class, JpaTarget.class);
final DistributionSet ds = distributionSetManagement.get(distributionSetId);
final Long distSetTypeId = ds.getType().getId();
final List<Specification<JpaTarget>> specList = List.of(
RsqlUtility.getInstance().buildRsqlSpecification(targetFilterQuery, TargetFields.class),
QLSupport.getInstance().buildSpec(targetFilterQuery, TargetFields.class),
TargetSpecifications.hasNotDistributionSetInActions(distributionSetId),
TargetSpecifications.isCompatibleWithDistributionSetType(distSetTypeId),
TargetSpecifications.hasControllerId(controllerId));
@@ -157,7 +156,7 @@ public class JpaTargetManagement
.findAllWithoutCount(
AccessController.Operation.UPDATE,
combineWithAnd(List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetFields.class),
QLSupport.getInstance().buildSpec(rsql, TargetFields.class),
TargetSpecifications.hasNotDistributionSetInActions(distributionSetId),
TargetSpecifications.isCompatibleWithDistributionSetType(distSetTypeId))),
pageable)
@@ -170,7 +169,7 @@ public class JpaTargetManagement
return jpaRepository
.findAllWithoutCount(AccessController.Operation.UPDATE,
combineWithAnd(List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetFields.class),
QLSupport.getInstance().buildSpec(rsql, TargetFields.class),
TargetSpecifications.isNotInRolloutGroups(groups),
TargetSpecifications.isCompatibleWithDistributionSetType(dsType.getId()))),
pageable)
@@ -191,7 +190,7 @@ public class JpaTargetManagement
return jpaRepository
.findAllWithoutCount(AccessController.Operation.UPDATE,
combineWithAnd(List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetFields.class),
QLSupport.getInstance().buildSpec(rsql, TargetFields.class),
TargetSpecifications.hasNoOverridingActionsAndNotInRollout(rolloutId),
TargetSpecifications.isCompatibleWithDistributionSetType(distributionSetType.getId()))),
pageable)
@@ -222,7 +221,7 @@ public class JpaTargetManagement
final DistributionSet validDistSet = distributionSetManagement.get(distributionSetId);
final List<Specification<JpaTarget>> specList = List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetFields.class),
QLSupport.getInstance().buildSpec(rsql, TargetFields.class),
TargetSpecifications.hasAssignedDistributionSet(validDistSet.getId()));
return JpaManagementHelper.findAllWithCountBySpec(jpaRepository, specList, pageable);
@@ -241,7 +240,7 @@ public class JpaTargetManagement
final DistributionSet validDistSet = distributionSetManagement.get(distributionSetId);
final List<Specification<JpaTarget>> specList = List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetFields.class),
QLSupport.getInstance().buildSpec(rsql, TargetFields.class),
TargetSpecifications.hasInstalledDistributionSet(validDistSet.getId()));
return JpaManagementHelper.findAllWithCountBySpec(jpaRepository, specList, pageable);
@@ -258,7 +257,7 @@ public class JpaTargetManagement
throwEntityNotFoundExceptionIfTagDoesNotExist(tagId);
final List<Specification<JpaTarget>> specList = List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetFields.class),
QLSupport.getInstance().buildSpec(rsql, TargetFields.class),
TargetSpecifications.hasTag(tagId));
return JpaManagementHelper.findAllWithCountBySpec(jpaRepository, specList, pageable);
@@ -267,7 +266,7 @@ public class JpaTargetManagement
@Override
public long countByRsqlAndCompatible(final String rsql, final Long distributionSetIdTypeId) {
final List<Specification<JpaTarget>> specList = List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetFields.class),
QLSupport.getInstance().buildSpec(rsql, TargetFields.class),
TargetSpecifications.isCompatibleWithDistributionSetType(distributionSetIdTypeId));
return JpaManagementHelper.countBySpec(jpaRepository, specList);
}
@@ -286,7 +285,7 @@ public class JpaTargetManagement
return jpaRepository.count(
AccessController.Operation.UPDATE,
combineWithAnd(List.of(
RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetFields.class),
QLSupport.getInstance().buildSpec(rsql, TargetFields.class),
TargetSpecifications.hasNotDistributionSetInActions(distributionSetId),
TargetSpecifications.isCompatibleWithDistributionSetType(distSetTypeId))));
}
@@ -296,7 +295,7 @@ public class JpaTargetManagement
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),
QLSupport.getInstance().buildSpec(rsql, TargetFields.class),
TargetSpecifications.isNotInRolloutGroups(groups),
TargetSpecifications.isCompatibleWithDistributionSetType(dsType.getId()))));
}
@@ -416,7 +415,7 @@ public class JpaTargetManagement
@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 Specification<JpaTarget> rsqlSpecification = QLSupport.getInstance().buildSpec(rsql, TargetFields.class);
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaUpdate<JpaTarget> criteriaUpdateQuery = cb.createCriteriaUpdate(JpaTarget.class);

View File

@@ -12,7 +12,7 @@ package org.eclipse.hawkbit.repository.jpa.management;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.fail;
import static org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility.RsqlToSpecBuilder.LEGACY_G1;
import static org.eclipse.hawkbit.repository.jpa.ql.QLSupport.SpecBuilder.LEGACY_G1;
import java.net.URI;
import java.util.ArrayList;
@@ -62,7 +62,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionStatusCreate;
import org.eclipse.hawkbit.repository.model.Action.Status;
@@ -636,7 +636,7 @@ class TargetManagementTest extends AbstractRepositoryManagementWithMetadataTest<
*/
@Test
void findTargetsByRsqlWithTypeAndMetadata() {
if (RsqlUtility.getInstance().getRsqlToSpecBuilder() == LEGACY_G1) {
if (QLSupport.getInstance().getSpecBuilder() == LEGACY_G1) {
// legacy visitor fail with that
return;
}

View File

@@ -11,8 +11,8 @@ package org.eclipse.hawkbit.repository.jpa.rsql;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility.RsqlToSpecBuilder.LEGACY_G2;
import static org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility.RsqlToSpecBuilder.LEGACY_G1;
import static org.eclipse.hawkbit.repository.jpa.ql.QLSupport.SpecBuilder.LEGACY_G2;
import static org.eclipse.hawkbit.repository.jpa.ql.QLSupport.SpecBuilder.LEGACY_G1;
import java.util.Arrays;
import java.util.Map;
@@ -25,6 +25,7 @@ import org.eclipse.hawkbit.repository.TargetTypeManagement;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetTag;
@@ -177,7 +178,7 @@ class RsqlTargetFieldTest extends AbstractJpaIntegrationTest {
assertRSQLQuery(TargetFields.ATTRIBUTE.name() + ".test.null==null", 1); // "null" check
assertRSQLQuery(TargetFields.ATTRIBUTE.name() + ".test.n/a==null", 0); // "null" check
if (RsqlUtility.getInstance().getRsqlToSpecBuilder() != LEGACY_G1) {
if (QLSupport.getInstance().getSpecBuilder() != LEGACY_G1) {
assertRSQLQuery(TargetFields.ATTRIBUTE.name() + ".test.dot=is=value.dot", 1);
assertRSQLQuery(TargetFields.ATTRIBUTE.name() + ".test.null=is=null", 5); // null check
assertRSQLQuery(TargetFields.ATTRIBUTE.name() + ".test.n/a=is=null", 1 + 5); // null check
@@ -191,22 +192,22 @@ class RsqlTargetFieldTest extends AbstractJpaIntegrationTest {
assertRSQLQuery(TargetFields.ATTRIBUTE.name() + ".test.null!=null2", 1); // value check
assertRSQLQuery(TargetFields.ATTRIBUTE.name() + ".test.n/a!=null", 0); // "null" check
if (RsqlUtility.getInstance().getRsqlToSpecBuilder() != LEGACY_G1) {
if (QLSupport.getInstance().getSpecBuilder() != LEGACY_G1) {
assertRSQLQuery(
TargetFields.ATTRIBUTE.name() + ".test.dot=not=value.dot",
RsqlUtility.getInstance().getRsqlToSpecBuilder() == LEGACY_G2 ? 5 : 0);
QLSupport.getInstance().getSpecBuilder() == LEGACY_G2 ? 5 : 0);
assertRSQLQuery(TargetFields.ATTRIBUTE.name() + ".test.null=not=null", 1); // null check
assertRSQLQuery(
TargetFields.ATTRIBUTE.name() + ".test.null=not=null2",
1 + (RsqlUtility.getInstance().getRsqlToSpecBuilder() == LEGACY_G2 ? 5 : 0)); // value check
1 + (QLSupport.getInstance().getSpecBuilder() == LEGACY_G2 ? 5 : 0)); // value check
assertRSQLQuery(TargetFields.ATTRIBUTE.name() + ".test.n/a=not=null", 0); // null check
assertRSQLQuery(
TargetFields.ATTRIBUTE.name() + ".test.dot=ne=value.dot",
RsqlUtility.getInstance().getRsqlToSpecBuilder() == LEGACY_G2 ? 5 : 0);
QLSupport.getInstance().getSpecBuilder() == LEGACY_G2 ? 5 : 0);
assertRSQLQuery(TargetFields.ATTRIBUTE.name() + ".test.null=ne=null", 1); // null check
assertRSQLQuery(
TargetFields.ATTRIBUTE.name() + ".test.null=ne=null2",
1 + (RsqlUtility.getInstance().getRsqlToSpecBuilder() == LEGACY_G2 ? 5 : 0)); // value check
1 + (QLSupport.getInstance().getSpecBuilder() == LEGACY_G2 ? 5 : 0)); // value check
assertRSQLQuery(TargetFields.ATTRIBUTE.name() + ".test.n/a=ne=null", 0); // null check
}
@@ -303,7 +304,7 @@ class RsqlTargetFieldTest extends AbstractJpaIntegrationTest {
assertRSQLQuery(TargetFields.METADATA.name() + ".metaKey==noExist*", 0);
assertRSQLQuery(TargetFields.METADATA.name() + ".notExist==metaValue", 0);
if (RsqlUtility.getInstance().getRsqlToSpecBuilder() != LEGACY_G1) {
if (QLSupport.getInstance().getSpecBuilder() != LEGACY_G1) {
assertRSQLQuery(TargetFields.METADATA.name() + ".metaKey=is=metaValue", 1);
assertRSQLQuery(TargetFields.METADATA.name() + ".metaKey=is=null", 4); // null check (1 of the initial five has)
assertRSQLQuery(TargetFields.METADATA.name() + ".metaKey=is=*v*", 2);
@@ -321,27 +322,27 @@ class RsqlTargetFieldTest extends AbstractJpaIntegrationTest {
assertRSQLQuery(TargetFields.METADATA.name() + ".notExist!=metaValue", 0);
assertRSQLQuery(TargetFields.METADATA.name() + ".metaKey!=notExist", 2);
if (RsqlUtility.getInstance().getRsqlToSpecBuilder() != LEGACY_G1) {
if (QLSupport.getInstance().getSpecBuilder() != LEGACY_G1) {
assertRSQLQuery(
TargetFields.METADATA.name() + ".metaKey=not=metaValue",
RsqlUtility.getInstance().getRsqlToSpecBuilder() == LEGACY_G2 ? 1 + 4 : 1);
QLSupport.getInstance().getSpecBuilder() == LEGACY_G2 ? 1 + 4 : 1);
assertRSQLQuery(TargetFields.METADATA.name() + ".metaKey=not=null", 2); // null check (2 of the initial five)
assertRSQLQuery(
TargetFields.METADATA.name() + ".notExist=not=metaValue",
RsqlUtility.getInstance().getRsqlToSpecBuilder() == LEGACY_G2 ? 1 + 5 : 0);
QLSupport.getInstance().getSpecBuilder() == LEGACY_G2 ? 1 + 5 : 0);
assertRSQLQuery(
TargetFields.METADATA.name() + ".metaKey=not=notExist",
RsqlUtility.getInstance().getRsqlToSpecBuilder() == LEGACY_G2 ? 2 + 4 : 2);
QLSupport.getInstance().getSpecBuilder() == LEGACY_G2 ? 2 + 4 : 2);
assertRSQLQuery(
TargetFields.METADATA.name() + ".metaKey=ne=metaValue",
RsqlUtility.getInstance().getRsqlToSpecBuilder() == LEGACY_G2 ? 1 + 4 : 1);
QLSupport.getInstance().getSpecBuilder() == LEGACY_G2 ? 1 + 4 : 1);
assertRSQLQuery(TargetFields.METADATA.name() + ".metaKey=ne=null", 2); // null check (2 of the initial five)
assertRSQLQuery(
TargetFields.METADATA.name() + ".notExist=ne=metaValue",
RsqlUtility.getInstance().getRsqlToSpecBuilder() == LEGACY_G2 ? 1 + 5 : 0);
QLSupport.getInstance().getSpecBuilder() == LEGACY_G2 ? 1 + 5 : 0);
assertRSQLQuery(
TargetFields.METADATA.name() + ".metaKey=ne=notExist",
RsqlUtility.getInstance().getRsqlToSpecBuilder() == LEGACY_G2 ? 2 + 4 : 2);
QLSupport.getInstance().getSpecBuilder() == LEGACY_G2 ? 2 + 4 : 2);
}
assertRSQLQuery(TargetFields.METADATA.name() + ".metaKey=in=(metaValue,notexist)", 1);
@@ -374,26 +375,26 @@ class RsqlTargetFieldTest extends AbstractJpaIntegrationTest {
*/
@Test
void rsqlValidTargetFields() {
RsqlUtility.getInstance().validateRsqlFor(
QLSupport.getInstance().validateQuery(
"ID == '0123' and NAME == abcd and DESCRIPTION == absd and CREATEDAT =lt= 0123 and LASTMODIFIEDAT =gt= 0123" +
" and CONTROLLERID == 0123 and UPDATESTATUS == PENDING and IPADDRESS == 0123 and LASTCONTROLLERREQUESTAT == 0123" +
" and tag == beta",
TargetFields.class, JpaTarget.class);
RsqlUtility.getInstance().validateRsqlFor(
QLSupport.getInstance().validateQuery(
"ASSIGNEDDS.name == abcd and ASSIGNEDDS.version == 0123 and INSTALLEDDS.name == abcd and INSTALLEDDS.version == 0123",
TargetFields.class, JpaTarget.class);
RsqlUtility.getInstance().validateRsqlFor(
QLSupport.getInstance().validateQuery(
"ATTRIBUTE.subkey1 == test and ATTRIBUTE.subkey2 == test and METADATA.metakey1 == abcd and METADATA.metavalue2 == asdfg",
TargetFields.class, JpaTarget.class);
RsqlUtility.getInstance().validateRsqlFor(
QLSupport.getInstance().validateQuery(
"CREATEDAT =lt= ${NOW_TS} and LASTMODIFIEDAT =ge= ${OVERDUE_TS}",
TargetFields.class, JpaTarget.class);
RsqlUtility.getInstance().validateRsqlFor(
QLSupport.getInstance().validateQuery(
"ATTRIBUTE.test.dot == test and ATTRIBUTE.subkey2 == test and METADATA.test.dot == abcd and METADATA.metavalue2 == asdfg",
TargetFields.class, JpaTarget.class);
assertThatExceptionOfType(RSQLParameterUnsupportedFieldException.class)
.isThrownBy(() -> RsqlUtility.getInstance().validateRsqlFor("wrongfield == abcd", TargetFields.class, JpaTarget.class));
.isThrownBy(() -> QLSupport.getInstance().validateQuery("wrongfield == abcd", TargetFields.class, JpaTarget.class));
}
/**
@@ -448,6 +449,6 @@ class RsqlTargetFieldTest extends AbstractJpaIntegrationTest {
private void assertRSQLQueryThrowsException(final String rsql) {
assertThatExceptionOfType(RSQLParameterUnsupportedFieldException.class)
.isThrownBy(() -> RsqlUtility.getInstance().validateRsqlFor(rsql, TargetFields.class, JpaTarget.class));
.isThrownBy(() -> QLSupport.getInstance().validateQuery(rsql, TargetFields.class, JpaTarget.class));
}
}

View File

@@ -9,15 +9,15 @@
*/
package org.eclipse.hawkbit.repository.jpa.rsql;
import static org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility.RsqlToSpecBuilder.LEGACY_G2;
import static org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility.RsqlToSpecBuilder.G3;
import static org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility.RsqlToSpecBuilder.LEGACY_G1;
import static org.eclipse.hawkbit.repository.jpa.ql.QLSupport.SpecBuilder.LEGACY_G2;
import static org.eclipse.hawkbit.repository.jpa.ql.QLSupport.SpecBuilder.G3;
import static org.eclipse.hawkbit.repository.jpa.ql.QLSupport.SpecBuilder.LEGACY_G1;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.NONE;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.eclipse.hawkbit.repository.RsqlQueryField;
import org.eclipse.hawkbit.repository.QueryField;
import org.eclipse.hawkbit.repository.SoftwareModuleFields;
import org.eclipse.hawkbit.repository.TargetFields;
import org.eclipse.hawkbit.repository.jpa.JpaRepositoryConfiguration;
@@ -130,7 +130,7 @@ class RsqlToSqlTest {
rsqlToSQL = new HawkbitQlToSql(entityManager);
}
private <T, A extends Enum<A> & RsqlQueryField> void print(final Class<T> domainClass, final Class<A> fieldsClass, final String rsql) {
private <T, A extends Enum<A> & QueryField> void print(final Class<T> domainClass, final Class<A> fieldsClass, final String rsql) {
System.out.println(rsql);
final String legacy = rsqlToSQL.toSQL(domainClass, fieldsClass, rsql, LEGACY_G1);
final String g2 = rsqlToSQL.toSQL(domainClass, fieldsClass, rsql, LEGACY_G2);
@@ -143,7 +143,7 @@ class RsqlToSqlTest {
}
}
private <T, A extends Enum<A> & RsqlQueryField> void printFrom(final Class<T> domainClass, final Class<A> fieldsClass, final String rsql) {
private <T, A extends Enum<A> & QueryField> void printFrom(final Class<T> domainClass, final Class<A> fieldsClass, final String rsql) {
System.out.println(rsql);
final String legacy = rsqlToSQL.toSQL(domainClass, fieldsClass, rsql, LEGACY_G1);
final String g2 = rsqlToSQL.toSQL(domainClass, fieldsClass, rsql, LEGACY_G2);