First level suppor for RsqlQueryFields shortcut support (#2686)
* now it is possible to have a showrtcut for a sub attributes (i.e. calling it directly with enum name, e.g. type -> type.key) with directly specifying the defaultSubEntityAttribute * no need to have single sub attribute in order to have a default sub attribute * added TYPE search field for TargetFields (sinonim of targettype) * targettype is deprecated - to be decided if and when to be removed * returned back "type" direct search (with meaning type.key) for DistributionSet and SoftwareModule as non-depricated * add serche with "type" as type.key for Target Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -46,6 +46,11 @@ public enum DistributionSetFields implements RsqlQueryField {
|
||||
this.subEntityAttributes = List.of(subEntityAttributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultSubEntityAttribute() {
|
||||
return this == TYPE ? DistributionSetTypeFields.KEY.getJpaEntityFieldName() : RsqlQueryField.super.getDefaultSubEntityAttribute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMap() {
|
||||
return this == METADATA;
|
||||
|
||||
@@ -20,7 +20,7 @@ package org.eclipse.hawkbit.repository;
|
||||
public interface FieldValueConverter<T extends Enum<T>> {
|
||||
|
||||
/**
|
||||
* Converts the given {@code value} into the representation to build ageneric query.
|
||||
* Converts the given {@code value} into the representation to build a generic query.
|
||||
*
|
||||
* @param enumValue the enum value to build the value for
|
||||
* @param value the value in string representation
|
||||
|
||||
@@ -39,12 +39,22 @@ public interface RsqlQueryField {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default sub entity attribute if available. This allows to skip that sub-attribute and call with "shortcut" - the enum name
|
||||
*
|
||||
* @return the default sub-attribute or <code>null</code>> if no default is available
|
||||
*/
|
||||
default String getDefaultSubEntityAttribute() {
|
||||
final List<String> subAttributes = getSubEntityAttributes();
|
||||
return subAttributes.size() == 1 ? subAttributes.get(0) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the field, that identifies the entity.
|
||||
*
|
||||
* @return the name of the identifier, by default 'id'
|
||||
*/
|
||||
default String identifierFieldName() {
|
||||
default String getIdentifierFieldName() {
|
||||
return "id";
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,12 @@ public enum SoftwareModuleFields implements RsqlQueryField {
|
||||
this.subEntityAttributes = List.of(subEntityAttributes);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getDefaultSubEntityAttribute() {
|
||||
return this == TYPE ? SoftwareModuleTypeFields.KEY.getJpaEntityFieldName() : RsqlQueryField.super.getDefaultSubEntityAttribute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMap() {
|
||||
return this == METADATA;
|
||||
|
||||
@@ -39,6 +39,14 @@ public enum TargetFields implements RsqlQueryField {
|
||||
TAG("tags", TagFields.NAME.getJpaEntityFieldName()),
|
||||
LASTCONTROLLERREQUESTAT("lastTargetQuery"),
|
||||
METADATA("metadata"),
|
||||
TYPE("targetType",
|
||||
TargetTypeFields.ID.getJpaEntityFieldName(),
|
||||
TargetTypeFields.KEY.getJpaEntityFieldName(),
|
||||
TargetTypeFields.NAME.getJpaEntityFieldName()),
|
||||
// kept just for backward compatibility for backward compatibility
|
||||
// could be removed only if in the systems there are no active auto assignments or rollouts (dynamic or starting) with that condition
|
||||
// to be reconsidered if and when to be removed
|
||||
@Deprecated(forRemoval = true, since = "0.10.0")
|
||||
TARGETTYPE("targetType",
|
||||
TargetTypeFields.ID.getJpaEntityFieldName(),
|
||||
TargetTypeFields.KEY.getJpaEntityFieldName(),
|
||||
@@ -52,6 +60,11 @@ public enum TargetFields implements RsqlQueryField {
|
||||
this.subEntityAttributes = List.of(subEntityAttributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultSubEntityAttribute() {
|
||||
return this == TYPE ? TargetTypeFields.KEY.getJpaEntityFieldName() : RsqlQueryField.super.getDefaultSubEntityAttribute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMap() {
|
||||
return this == ATTRIBUTE || this == METADATA;
|
||||
|
||||
@@ -24,7 +24,6 @@ import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.NOT
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.UnaryOperator;
|
||||
@@ -42,10 +41,8 @@ import cz.jirutka.rsql.parser.ast.RSQLVisitor;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetFields;
|
||||
import org.eclipse.hawkbit.repository.FieldValueConverter;
|
||||
import org.eclipse.hawkbit.repository.RsqlQueryField;
|
||||
import org.eclipse.hawkbit.repository.SoftwareModuleFields;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
||||
import org.eclipse.hawkbit.repository.jpa.ql.Node;
|
||||
@@ -58,7 +55,7 @@ import org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison;
|
||||
* <li>check for * and convert EQ/NEQ to LIKE/NOT_LIKE</li>
|
||||
* <li>replace the rsql fields (enum values) with the JPA entity field names</li>
|
||||
* <li>checks sub-attributes (if allowed)</li>
|
||||
* <li>append the default sub-attributes if needed)</li>
|
||||
* <li>append the default sub-attributes if needed</li>
|
||||
* <li>apply value conversion for implementing with FieldValueConverter</li>
|
||||
* </ul>
|
||||
*/
|
||||
@@ -121,20 +118,17 @@ public class RsqlParser {
|
||||
// just enum name for a complex type (with sub-attributes), should have single (default!) sub-attribute
|
||||
if (enumValue.isMap()) {
|
||||
throw new RSQLParameterUnsupportedFieldException("No key specified for a map type " + enumValue);
|
||||
} else if (enumValue.getSubEntityAttributes().size() == 1) {
|
||||
// single sub attribute - so, treat it as a default
|
||||
attribute = enumValue.getJpaEntityFieldName() + SUB_ATTRIBUTE_SEPARATOR + enumValue.getSubEntityAttributes().get(0);
|
||||
} else if (RsqlUtility.SM_DS_SEARCH_BY_TYPE_BACKWARD_COMPATIBILITY &&
|
||||
"type".equalsIgnoreCase(enumValue.getJpaEntityFieldName()) &&
|
||||
(Objects.equals(rsqlQueryFieldType, SoftwareModuleFields.class) ||
|
||||
Objects.equals(rsqlQueryFieldType, DistributionSetFields.class))) {
|
||||
// backward compatibility - type for SoftwareModuleTypeFields && DistributionSetFields means type.key
|
||||
attribute = enumValue.getJpaEntityFieldName() + SUB_ATTRIBUTE_SEPARATOR + "key";
|
||||
} else {
|
||||
throw new RSQLParameterUnsupportedFieldException(
|
||||
String.format(
|
||||
"The given search parameter field {%s} requires one of the following sub-attributes %s",
|
||||
key, enumValue.getSubEntityAttributes()));
|
||||
final String defaultSubEntityAttribute = enumValue.getDefaultSubEntityAttribute();
|
||||
if (defaultSubEntityAttribute != null) {
|
||||
// single sub attribute - so, treat it as a default
|
||||
attribute = enumValue.getJpaEntityFieldName() + SUB_ATTRIBUTE_SEPARATOR + defaultSubEntityAttribute;
|
||||
} else {
|
||||
throw new RSQLParameterUnsupportedFieldException(
|
||||
String.format(
|
||||
"The given search parameter field {%s} requires one of the following sub-attributes %s",
|
||||
key, enumValue.getSubEntityAttributes()));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { // field name with sub-attribute
|
||||
|
||||
@@ -71,11 +71,6 @@ import org.springframework.orm.jpa.vendor.Database;
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class RsqlUtility {
|
||||
|
||||
// to be removed in future releases, use type.key instead of type for software module and distribution set RSQL queries
|
||||
@Deprecated(forRemoval = true, since = "0.10.0")
|
||||
public static final boolean SM_DS_SEARCH_BY_TYPE_BACKWARD_COMPATIBILITY =
|
||||
"true".equalsIgnoreCase(System.getProperty("hawkbit.rsql.sm-ds-search-by-type.backward-compatibility", "true"));
|
||||
|
||||
private static final RsqlUtility SINGLETON = new RsqlUtility();
|
||||
|
||||
public enum RsqlToSpecBuilder {
|
||||
|
||||
@@ -13,7 +13,6 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -24,12 +23,9 @@ 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.DistributionSetFields;
|
||||
import org.eclipse.hawkbit.repository.RsqlQueryField;
|
||||
import org.eclipse.hawkbit.repository.SoftwareModuleFields;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
||||
import org.eclipse.hawkbit.repository.jpa.ql.SpecificationBuilder;
|
||||
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
@@ -74,14 +70,9 @@ public abstract class AbstractRSQLVisitor<A extends Enum<A> & RsqlQueryField> {
|
||||
if (!enumValue.isMap()) {
|
||||
// sub entity need minimum 1 dot
|
||||
if (!enumValue.getSubEntityAttributes().isEmpty() && split.length < 2) {
|
||||
if (enumValue.getSubEntityAttributes().size() == 1) { // single sub attribute - so add is as a default
|
||||
split = new String[] { split[0], enumValue.getSubEntityAttributes().get(0) };
|
||||
} else if (RsqlUtility.SM_DS_SEARCH_BY_TYPE_BACKWARD_COMPATIBILITY &&
|
||||
"type".equals(node.getSelector()) &&
|
||||
(Objects.equals(rsqlQueryFieldType, SoftwareModuleFields.class) ||
|
||||
Objects.equals(rsqlQueryFieldType, DistributionSetFields.class))) {
|
||||
// backward compatibility - type for DistributionSetFields means type.key
|
||||
split = new String[] { split[0], "key" };
|
||||
final String defaultSubEntityAttribute = enumValue.getDefaultSubEntityAttribute();
|
||||
if (defaultSubEntityAttribute != null) { // single sub attribute - so add is as a default
|
||||
split = new String[] { split[0], defaultSubEntityAttribute };
|
||||
} else {
|
||||
throw createRSQLParameterUnsupportedException(node, null);
|
||||
}
|
||||
|
||||
@@ -493,8 +493,8 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & RsqlQueryField, T> extends
|
||||
final Class<?> javaType = root.getJavaType();
|
||||
final Subquery<?> subquery = query.subquery(javaType);
|
||||
final Root subqueryRoot = subquery.from(javaType);
|
||||
final Predicate equalPredicate = cb.equal(root.get(enumField.identifierFieldName()),
|
||||
subqueryRoot.get(enumField.identifierFieldName()));
|
||||
final Predicate equalPredicate = cb.equal(root.get(enumField.getIdentifierFieldName()),
|
||||
subqueryRoot.get(enumField.getIdentifierFieldName()));
|
||||
final Path innerFieldPath = getInnerFieldPath(subqueryRoot, fieldNames, enumField.isMap());
|
||||
final Expression<String> expressionToCompare = getExpressionToCompare(innerFieldPath, enumField);
|
||||
final Predicate subQueryPredicate = subQueryPredicateProvider.apply(expressionToCompare);
|
||||
|
||||
@@ -341,8 +341,8 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & RsqlQueryField, T>
|
||||
subquery.select(subqueryRoot)
|
||||
.where(cb.and(
|
||||
cb.equal(
|
||||
root.get(queryPath.getEnumValue().identifierFieldName()),
|
||||
subqueryRoot.get(queryPath.getEnumValue().identifierFieldName())),
|
||||
root.get(queryPath.getEnumValue().getIdentifierFieldName()),
|
||||
subqueryRoot.get(queryPath.getEnumValue().getIdentifierFieldName())),
|
||||
subQueryPredicateProvider.apply(
|
||||
getExpressionToCompare(queryPath.getEnumValue(), getFieldPath(subqueryRoot, queryPath)))))));
|
||||
}
|
||||
|
||||
@@ -143,8 +143,7 @@ class RsqlSoftwareModuleFieldTest extends AbstractJpaIntegrationTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFilterByTypeBackwardCompatibility() {
|
||||
assertThat(RsqlUtility.SM_DS_SEARCH_BY_TYPE_BACKWARD_COMPATIBILITY).isTrue();
|
||||
void testFilterByTypeShortcut() {
|
||||
assertRSQLQuery(SoftwareModuleFields.TYPE.name() + "==" + TestdataFactory.SM_TYPE_APP, 2);
|
||||
assertRSQLQuery(SoftwareModuleFields.TYPE.name() + "!=" + TestdataFactory.SM_TYPE_APP, 4);
|
||||
assertRSQLQuery(SoftwareModuleFields.TYPE.name() + "==noExist*", 0);
|
||||
|
||||
Reference in New Issue
Block a user