Refactor RSQL serach fields related classes (#1834)

Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2024-09-09 17:39:45 +03:00
committed by GitHub
parent a31028ee19
commit 072809be6c
34 changed files with 291 additions and 285 deletions

View File

@@ -18,7 +18,7 @@ import java.util.List;
* Sort and search fields for actions.
*/
@Getter
public enum ActionFields implements FieldNameProvider, FieldValueConverter<ActionFields> {
public enum ActionFields implements RsqlQueryField, FieldValueConverter<ActionFields> {
ID("id"),
STATUS("active"),
@@ -26,29 +26,29 @@ public enum ActionFields implements FieldNameProvider, FieldValueConverter<Actio
LASTSTATUSCODE("lastActionStatusCode"),
WEIGHT("weight"),
TARGET("target",
TargetFields.ID.getFieldName(), TargetFields.NAME.getFieldName(),
TargetFields.UPDATESTATUS.getFieldName(), TargetFields.IPADDRESS.getFieldName()),
TargetFields.ID.getJpaEntityFieldName(), TargetFields.NAME.getJpaEntityFieldName(),
TargetFields.UPDATESTATUS.getJpaEntityFieldName(), TargetFields.IPADDRESS.getJpaEntityFieldName()),
DISTRIBUTIONSET("distributionSet",
DistributionSetFields.ID.getFieldName(),
DistributionSetFields.NAME.getFieldName(), DistributionSetFields.VERSION.getFieldName(),
DistributionSetFields.TYPE.getFieldName()),
ROLLOUT("rollout", RolloutFields.ID.getFieldName(), RolloutFields.NAME.getFieldName()),
ROLLOUTGROUP("rolloutGroup", RolloutGroupFields.ID.getFieldName(), RolloutGroupFields.NAME.getFieldName()),
DistributionSetFields.ID.getJpaEntityFieldName(),
DistributionSetFields.NAME.getJpaEntityFieldName(), DistributionSetFields.VERSION.getJpaEntityFieldName(),
DistributionSetFields.TYPE.getJpaEntityFieldName()),
ROLLOUT("rollout", RolloutFields.ID.getJpaEntityFieldName(), RolloutFields.NAME.getJpaEntityFieldName()),
ROLLOUTGROUP("rolloutGroup", RolloutGroupFields.ID.getJpaEntityFieldName(), RolloutGroupFields.NAME.getJpaEntityFieldName()),
EXTERNALREF("externalRef");
private static final String ACTIVE = "pending";
private static final String INACTIVE = "finished";
private final String fieldName;
private final String jpaEntityFieldName;
private final List<String> subEntityAttributes;
ActionFields(final String fieldName) {
this.fieldName = fieldName;
ActionFields(final String jpaEntityFieldName) {
this.jpaEntityFieldName = jpaEntityFieldName;
this.subEntityAttributes = Collections.emptyList();
}
ActionFields(final String fieldName, final String... subEntityAttributes) {
this.fieldName = fieldName;
ActionFields(final String jpaEntityFieldName, final String... subEntityAttributes) {
this.jpaEntityFieldName = jpaEntityFieldName;
this.subEntityAttributes = List.of(subEntityAttributes);
}

View File

@@ -15,14 +15,14 @@ import lombok.Getter;
* Sort and search fields for action status.
*/
@Getter
public enum ActionStatusFields implements FieldNameProvider {
public enum ActionStatusFields implements RsqlQueryField {
ID("id"),
REPORTEDAT("createdAt");
private final String fieldName;
private final String jpaEntityFieldName;
ActionStatusFields(final String fieldName) {
this.fieldName = fieldName;
ActionStatusFields(final String jpaEntityFieldName) {
this.jpaEntityFieldName = jpaEntityFieldName;
}
}

View File

@@ -22,7 +22,7 @@ import java.util.Optional;
* REST API e.g. for sorting etc.
*/
@Getter
public enum DistributionSetFields implements FieldNameProvider {
public enum DistributionSetFields implements RsqlQueryField {
ID("id"),
TYPE("type.key"),
@@ -32,29 +32,29 @@ public enum DistributionSetFields implements FieldNameProvider {
LASTMODIFIEDAT("lastModifiedAt"),
VERSION("version"),
COMPLETE("complete"),
MODULE("modules", SoftwareModuleFields.ID.getFieldName(), SoftwareModuleFields.NAME.getFieldName()),
MODULE("modules", SoftwareModuleFields.ID.getJpaEntityFieldName(), SoftwareModuleFields.NAME.getJpaEntityFieldName()),
TAG("tags.name"),
METADATA("metadata", new SimpleImmutableEntry<>("key", "value")),
VALID("valid");
private final String fieldName;
private final String jpaEntityFieldName;
private final Entry<String, String> subEntityMapTuple;
private final List<String> subEntityAttributes;
DistributionSetFields(final String fieldName) {
this(fieldName, null, Collections.emptyList());
DistributionSetFields(final String jpaEntityFieldName) {
this(jpaEntityFieldName, null, Collections.emptyList());
}
DistributionSetFields(final String fieldName, final String... subEntityAttributes) {
this(fieldName, null, List.of(subEntityAttributes));
DistributionSetFields(final String jpaEntityFieldName, final String... subEntityAttributes) {
this(jpaEntityFieldName, null, List.of(subEntityAttributes));
}
DistributionSetFields(final String fieldName, final Entry<String, String> subEntityMapTuple) {
this(fieldName, subEntityMapTuple, Collections.emptyList());
DistributionSetFields(final String jpaEntityFieldName, final Entry<String, String> subEntityMapTuple) {
this(jpaEntityFieldName, subEntityMapTuple, Collections.emptyList());
}
DistributionSetFields(final String fieldName, final Entry<String, String> subEntityMapTuple, List<String> subEntityAttributes) {
this.fieldName = fieldName;
DistributionSetFields(final String jpaEntityFieldName, final Entry<String, String> subEntityMapTuple, List<String> subEntityAttributes) {
this.jpaEntityFieldName = jpaEntityFieldName;
this.subEntityMapTuple = subEntityMapTuple;
this.subEntityAttributes = subEntityAttributes;
}

View File

@@ -15,19 +15,19 @@ import lombok.Getter;
* Sort fields for DistributionSetMetadata.
*/
@Getter
public enum DistributionSetMetadataFields implements FieldNameProvider {
public enum DistributionSetMetadataFields implements RsqlQueryField {
KEY("key"),
VALUE("value");
private final String fieldName;
private final String jpaEntityFieldName;
DistributionSetMetadataFields(final String fieldName) {
this.fieldName = fieldName;
DistributionSetMetadataFields(final String jpaEntityFieldName) {
this.jpaEntityFieldName = jpaEntityFieldName;
}
@Override
public String identifierFieldName() {
return KEY.getFieldName();
return KEY.getJpaEntityFieldName();
}
}

View File

@@ -21,25 +21,25 @@ import java.util.List;
* filtering over distribution set fields also.
*/
@Getter
public enum DistributionSetTagFields implements FieldNameProvider {
public enum DistributionSetTagFields implements RsqlQueryField {
ID(TagFields.ID.getFieldName()),
NAME(TagFields.NAME.getFieldName()),
DESCRIPTION(TagFields.DESCRIPTION.getFieldName()),
COLOUR(TagFields.COLOUR.getFieldName()),
ID(TagFields.ID.getJpaEntityFieldName()),
NAME(TagFields.NAME.getJpaEntityFieldName()),
DESCRIPTION(TagFields.DESCRIPTION.getJpaEntityFieldName()),
COLOUR(TagFields.COLOUR.getJpaEntityFieldName()),
DISTRIBUTIONSET("assignedToDistributionSet",
DistributionSetFields.ID.getFieldName(), DistributionSetFields.NAME.getFieldName());
DistributionSetFields.ID.getJpaEntityFieldName(), DistributionSetFields.NAME.getJpaEntityFieldName());
private final String fieldName;
private final String jpaEntityFieldName;
private final List<String> subEntityAttributes;
DistributionSetTagFields(final String fieldName) {
this.fieldName = fieldName;
DistributionSetTagFields(final String jpaEntityFieldName) {
this.jpaEntityFieldName = jpaEntityFieldName;
this.subEntityAttributes = Collections.emptyList();
}
DistributionSetTagFields(final String fieldName, final String... subEntityAttributes) {
this.fieldName = fieldName;
DistributionSetTagFields(final String jpaEntityFieldName, final String... subEntityAttributes) {
this.jpaEntityFieldName = jpaEntityFieldName;
this.subEntityAttributes = List.of(subEntityAttributes);
}
}

View File

@@ -16,16 +16,16 @@ import lombok.Getter;
* the REST API e.g. for sorting etc.
*/
@Getter
public enum DistributionSetTypeFields implements FieldNameProvider {
public enum DistributionSetTypeFields implements RsqlQueryField {
ID("id"),
KEY("key"),
NAME("name"),
DESCRIPTION("description");
private final String fieldName;
private final String jpaEntityFieldName;
DistributionSetTypeFields(final String fieldName) {
this.fieldName = fieldName;
DistributionSetTypeFields(final String jpaEntityFieldName) {
this.jpaEntityFieldName = jpaEntityFieldName;
}
}

View File

@@ -18,26 +18,26 @@ import java.util.List;
* 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 FieldNameProvider {
public enum RolloutFields implements RsqlQueryField {
ID("id"),
NAME("name"),
DESCRIPTION("description"),
STATUS("status"),
DISTRIBUTIONSET("distributionSet", DistributionSetFields.ID.getFieldName(),
DistributionSetFields.NAME.getFieldName(), DistributionSetFields.VERSION.getFieldName(),
DistributionSetFields.TYPE.getFieldName());
DISTRIBUTIONSET("distributionSet", DistributionSetFields.ID.getJpaEntityFieldName(),
DistributionSetFields.NAME.getJpaEntityFieldName(), DistributionSetFields.VERSION.getJpaEntityFieldName(),
DistributionSetFields.TYPE.getJpaEntityFieldName());
private final String fieldName;
private final String jpaEntityFieldName;
private final List<String> subEntityAttributes;
RolloutFields(final String fieldName) {
this.fieldName = fieldName;
RolloutFields(final String jpaEntityFieldName) {
this.jpaEntityFieldName = jpaEntityFieldName;
this.subEntityAttributes = Collections.emptyList();
}
RolloutFields(final String fieldName, final String... subEntityAttributes) {
this.fieldName = fieldName;
RolloutFields(final String jpaEntityFieldName, final String... subEntityAttributes) {
this.jpaEntityFieldName = jpaEntityFieldName;
this.subEntityAttributes = List.of(subEntityAttributes);
}
}

View File

@@ -15,15 +15,15 @@ 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 FieldNameProvider {
public enum RolloutGroupFields implements RsqlQueryField {
ID("id"),
NAME("name"),
DESCRIPTION("description");
private final String fieldName;
private final String jpaEntityFieldName;
RolloutGroupFields(final String fieldName) {
this.fieldName = fieldName;
RolloutGroupFields(final String jpaEntityFieldName) {
this.jpaEntityFieldName = jpaEntityFieldName;
}
}

View File

@@ -15,15 +15,13 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import jakarta.validation.constraints.NotNull;
import org.springframework.util.ObjectUtils;
/**
* An interface for declaring the name of the field described in the database
* which is used as string representation of the field, e.g. for sorting the
* fields over REST.
* An RSQL query field interface extended by all the fields that could be used in RSQL queries.
*/
@FunctionalInterface
public interface FieldNameProvider {
public interface RsqlQueryField {
/**
* Separator for the sub attributes
@@ -32,10 +30,10 @@ public interface FieldNameProvider {
String SUB_ATTRIBUTE_SPLIT_REGEX = "\\" + SUB_ATTRIBUTE_SEPARATOR;
/**
* @return the string representation of the underlying persistence field
* name e.g. in case of sorting. Never {@code null}.
* @return the string representation of the underlying persistence field name e.g. in case of sorting.
*/
String getFieldName();
@NotNull
String getJpaEntityFieldName();
/**
* Returns the sub attributes
@@ -48,7 +46,7 @@ public interface FieldNameProvider {
final String[] subAttributes = propertyFieldName.split(SUB_ATTRIBUTE_SPLIT_REGEX, 2);
// [0] field name | [1] key name (could miss, e.g. for target attributes)
final String mapKeyName = subAttributes.length == 2 ? subAttributes[1] : null;
return ObjectUtils.isEmpty(mapKeyName) ? new String[] { getFieldName() } : new String[] { getFieldName(), mapKeyName };
return ObjectUtils.isEmpty(mapKeyName) ? new String[] { getJpaEntityFieldName() } : new String[] { getJpaEntityFieldName(), mapKeyName };
} else {
return propertyFieldName.split(SUB_ATTRIBUTE_SPLIT_REGEX);
}

View File

@@ -19,7 +19,7 @@ import java.util.Optional;
* 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 FieldNameProvider {
public enum SoftwareModuleFields implements RsqlQueryField {
ID("id"),
TYPE("type.key"),
@@ -28,15 +28,15 @@ public enum SoftwareModuleFields implements FieldNameProvider {
VERSION("version"),
METADATA("metadata", new SimpleImmutableEntry<>("key", "value"));
private final String fieldName;
private final String jpaEntityFieldName;
private Entry<String, String> subEntityMapTuple;
SoftwareModuleFields(final String fieldName) {
this(fieldName, null);
SoftwareModuleFields(final String jpaEntityFieldName) {
this(jpaEntityFieldName, null);
}
SoftwareModuleFields(final String fieldName, final Entry<String, String> subEntityMapTuple) {
this.fieldName = fieldName;
SoftwareModuleFields(final String jpaEntityFieldName, final Entry<String, String> subEntityMapTuple) {
this.jpaEntityFieldName = jpaEntityFieldName;
this.subEntityMapTuple = subEntityMapTuple;
}

View File

@@ -15,20 +15,20 @@ import lombok.Getter;
* Sort fields for SoftwareModuleMetadata.
*/
@Getter
public enum SoftwareModuleMetadataFields implements FieldNameProvider {
public enum SoftwareModuleMetadataFields implements RsqlQueryField {
KEY("key"),
VALUE("value"),
TARGETVISIBLE("targetVisible");
private final String fieldName;
private final String jpaEntityFieldName;
SoftwareModuleMetadataFields(final String fieldName) {
this.fieldName = fieldName;
SoftwareModuleMetadataFields(final String jpaEntityFieldName) {
this.jpaEntityFieldName = jpaEntityFieldName;
}
@Override
public String identifierFieldName() {
return KEY.getFieldName();
return KEY.getJpaEntityFieldName();
}
}

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 FieldNameProvider {
public enum SoftwareModuleTypeFields implements RsqlQueryField {
ID("id"),
KEY("key"),
@@ -23,9 +23,9 @@ public enum SoftwareModuleTypeFields implements FieldNameProvider {
DESCRIPTION("description"),
MAXASSIGNMENTS("maxAssignments");
private final String fieldName;
private final String jpaEntityFieldName;
SoftwareModuleTypeFields(final String fieldName) {
this.fieldName = fieldName;
SoftwareModuleTypeFields(final String jpaEntityFieldName) {
this.jpaEntityFieldName = jpaEntityFieldName;
}
}

View File

@@ -15,16 +15,16 @@ 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 FieldNameProvider {
public enum TagFields implements RsqlQueryField {
ID("id"),
NAME("name"),
DESCRIPTION("description"),
COLOUR("colour");
private final String fieldName;
private final String jpaEntityFieldName;
TagFields(final String fieldName) {
this.fieldName = fieldName;
TagFields(final String jpaEntityFieldName) {
this.jpaEntityFieldName = jpaEntityFieldName;
}
}

View File

@@ -22,7 +22,7 @@ import java.util.Optional;
* e.g. for sorting etc.
*/
@Getter
public enum TargetFields implements FieldNameProvider {
public enum TargetFields implements RsqlQueryField {
ID("controllerId"),
NAME("name"),
@@ -38,26 +38,26 @@ public enum TargetFields implements FieldNameProvider {
TAG("tags.name"),
LASTCONTROLLERREQUESTAT("lastTargetQuery"),
METADATA("metadata", new SimpleImmutableEntry<>("key", "value")),
TARGETTYPE("targetType", TargetTypeFields.KEY.getFieldName(), TargetTypeFields.NAME.getFieldName());
TARGETTYPE("targetType", TargetTypeFields.KEY.getJpaEntityFieldName(), TargetTypeFields.NAME.getJpaEntityFieldName());
private final String fieldName;
private final String jpaEntityFieldName;
private final List<String> subEntityAttributes;
private final Entry<String, String> subEntityMapTuple;
TargetFields(final String fieldName) {
this(fieldName, Collections.emptyList(), null);
TargetFields(final String jpaEntityFieldName) {
this(jpaEntityFieldName, Collections.emptyList(), null);
}
TargetFields(final String fieldName, final String... subEntityAttributes) {
this(fieldName, List.of(subEntityAttributes), null);
TargetFields(final String jpaEntityFieldName, final String... subEntityAttributes) {
this(jpaEntityFieldName, List.of(subEntityAttributes), null);
}
TargetFields(final String fieldName, final Entry<String, String> subEntityMapTuple) {
this(fieldName, Collections.emptyList(), subEntityMapTuple);
TargetFields(final String jpaEntityFieldName, final Entry<String, String> subEntityMapTuple) {
this(jpaEntityFieldName, Collections.emptyList(), subEntityMapTuple);
}
TargetFields(final String fieldName, final List<String> subEntityAttributes, final Entry<String, String> subEntityMapTuple) {
this.fieldName = fieldName;
TargetFields(final String jpaEntityFieldName, final List<String> subEntityAttributes, final Entry<String, String> subEntityMapTuple) {
this.jpaEntityFieldName = jpaEntityFieldName;
this.subEntityAttributes = subEntityAttributes;
this.subEntityMapTuple = subEntityMapTuple;
}

View File

@@ -18,25 +18,25 @@ import java.util.List;
* 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 FieldNameProvider {
public enum TargetFilterQueryFields implements RsqlQueryField {
ID("id"),
NAME("name"),
AUTOASSIGNDISTRIBUTIONSET("autoAssignDistributionSet", "name", "version");
private final String fieldName;
private final String jpaEntityFieldName;
private List<String> subEntityAttributes;
TargetFilterQueryFields(final String fieldName) {
this(fieldName, Collections.emptyList());
TargetFilterQueryFields(final String jpaEntityFieldName) {
this(jpaEntityFieldName, Collections.emptyList());
}
TargetFilterQueryFields(final String fieldName, final String... subEntityAttribues) {
this(fieldName, List.of(subEntityAttribues));
TargetFilterQueryFields(final String jpaEntityFieldName, final String... subEntityAttribues) {
this(jpaEntityFieldName, List.of(subEntityAttribues));
}
TargetFilterQueryFields(final String fieldName, final List<String> subEntityAttribues) {
this.fieldName = fieldName;
TargetFilterQueryFields(final String jpaEntityFieldName, final List<String> subEntityAttribues) {
this.jpaEntityFieldName = jpaEntityFieldName;
this.subEntityAttributes = subEntityAttribues;
}
}

View File

@@ -15,19 +15,19 @@ import lombok.Getter;
* Sort fields for TargetMetadata.
*/
@Getter
public enum TargetMetadataFields implements FieldNameProvider {
public enum TargetMetadataFields implements RsqlQueryField {
KEY("key"),
VALUE("value");
private final String fieldName;
private final String jpaEntityFieldName;
TargetMetadataFields(final String fieldName) {
this.fieldName = fieldName;
TargetMetadataFields(final String jpaEntityFieldName) {
this.jpaEntityFieldName = jpaEntityFieldName;
}
@Override
public String identifierFieldName() {
return KEY.getFieldName();
return KEY.getJpaEntityFieldName();
}
}

View File

@@ -16,16 +16,16 @@ import lombok.Getter;
* Additionally, here were added fields for Target in order filtering over target fields also.
*/
@Getter
public enum TargetTagFields implements FieldNameProvider {
public enum TargetTagFields implements RsqlQueryField {
ID(TagFields.ID.getFieldName()),
NAME(TagFields.NAME.getFieldName()),
DESCRIPTION(TagFields.DESCRIPTION.getFieldName()),
COLOUR(TagFields.COLOUR.getFieldName());
ID(TagFields.ID.getJpaEntityFieldName()),
NAME(TagFields.NAME.getJpaEntityFieldName()),
DESCRIPTION(TagFields.DESCRIPTION.getJpaEntityFieldName()),
COLOUR(TagFields.COLOUR.getJpaEntityFieldName());
private final String fieldName;
private final String jpaEntityFieldName;
TargetTagFields(final String fieldName) {
this.fieldName = fieldName;
TargetTagFields(final String jpaEntityFieldName) {
this.jpaEntityFieldName = jpaEntityFieldName;
}
}

View File

@@ -15,16 +15,16 @@ import lombok.Getter;
* Describing the fields of the TargetType model which can be used in the REST API
*/
@Getter
public enum TargetTypeFields implements FieldNameProvider {
public enum TargetTypeFields implements RsqlQueryField {
ID("id"),
KEY("key"),
NAME("name"),
DESCRIPTION("description");
private final String fieldName;
private final String jpaEntityFieldName;
TargetTypeFields(final String fieldName) {
this.fieldName = fieldName;
TargetTypeFields(final String jpaEntityFieldName) {
this.jpaEntityFieldName = jpaEntityFieldName;
}
}

View File

@@ -27,16 +27,16 @@ public class FileNameFieldsTest {
public void repositoryManagementMethodsArePreAuthorizedAnnotated() {
final String packageName = getClass().getPackage().getName();
try (final ScanResult scanResult = new ClassGraph().acceptPackages(packageName).scan()) {
final List<? extends Class<? extends FieldNameProvider>> matchingClasses = scanResult.getAllClasses()
final List<? extends Class<? extends RsqlQueryField>> matchingClasses = scanResult.getAllClasses()
.stream()
.filter(classInPackage -> classInPackage.implementsInterface(FieldNameProvider.class))
.filter(classInPackage -> classInPackage.implementsInterface(RsqlQueryField.class))
.map(ClassInfo::loadClass)
.map(clazz -> (Class<? extends FieldNameProvider>) clazz)
.map(clazz -> (Class<? extends RsqlQueryField>) clazz)
.toList();
assertThat(matchingClasses).isNotEmpty();
matchingClasses.forEach(providerClass -> {
assertThat(providerClass.getEnumConstants()).isNotEmpty();
for (final FieldNameProvider provider : providerClass.getEnumConstants()) {
for (final RsqlQueryField provider : providerClass.getEnumConstants()) {
if (provider.isMap() && !provider.getSubEntityAttributes().isEmpty()) {
throw new UnsupportedOperationException(
"Currently sub-entity attributes for maps are not supported, alternatively you could use the key/value tuple, defined by SimpleImmutableEntry class");

View File

@@ -9,7 +9,7 @@
*/
package org.eclipse.hawkbit.repository.rsql;
import org.eclipse.hawkbit.repository.FieldNameProvider;
import org.eclipse.hawkbit.repository.RsqlQueryField;
import cz.jirutka.rsql.parser.ast.Node;
import cz.jirutka.rsql.parser.ast.RSQLVisitor;
@@ -23,16 +23,16 @@ public interface RsqlVisitorFactory {
/**
* Provides a {@link RSQLVisitor} instance for validating RSQL queries based
* on the given {@link FieldNameProvider}.
* on the given {@link RsqlQueryField}.
*
* @param <A>
* The type of the {@link FieldNameProvider}.
* The type of the {@link RsqlQueryField}.
* @param fieldNameProvider
* providing accessing to the relevant field names.
*
* @return An {@link RSQLVisitor} to validate the {@link Node}s of an RSQL
* query.
*/
<A extends Enum<A> & FieldNameProvider> RSQLVisitor<Void, String> validationRsqlVisitor(Class<A> fieldNameProvider);
<A extends Enum<A> & RsqlQueryField> RSQLVisitor<Void, String> validationRsqlVisitor(Class<A> fieldNameProvider);
}

View File

@@ -15,70 +15,83 @@ import java.util.stream.Collectors;
import jakarta.validation.constraints.NotNull;
import lombok.Value;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.FieldNameProvider;
import org.eclipse.hawkbit.repository.RsqlQueryField;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import cz.jirutka.rsql.parser.ast.ComparisonNode;
@Slf4j
public abstract class AbstractFieldNameRSQLVisitor<A extends Enum<A> & FieldNameProvider> {
public abstract class AbstractRSQLVisitor<A extends Enum<A> & RsqlQueryField> {
private final Class<A> fieldNameProvider;
protected AbstractFieldNameRSQLVisitor(final Class<A> fieldNameProvider) {
@Value
public class RsqlField {
A enumValue;
String[] subAttributes;
private RsqlField(final A enumValue, final String[] subAttributes) {
this.enumValue = enumValue;
this.subAttributes = subAttributes;
}
}
protected AbstractRSQLVisitor(final Class<A> fieldNameProvider) {
this.fieldNameProvider = fieldNameProvider;
}
protected A getFieldEnumByName(final ComparisonNode node) {
final String[] graph = node.getSelector().split(FieldNameProvider.SUB_ATTRIBUTE_SPLIT_REGEX);
protected RsqlField getRsqlField(final ComparisonNode node) {
final String[] graph = node.getSelector().split(RsqlQueryField.SUB_ATTRIBUTE_SPLIT_REGEX);
final String enumName = graph.length == 0 ? node.getSelector() : graph[0];
log.debug("get field identifier by name {} of enum type {}", enumName, fieldNameProvider);
try {
return Enum.valueOf(fieldNameProvider, enumName.toUpperCase());
final A enumValue = Enum.valueOf(fieldNameProvider, enumName.toUpperCase());
final String[] subAttributes = enumValue.getSubAttributes(node.getSelector());
// validate
if (enumValue.isMap()) {
// enum.key
if (subAttributes.length != 2) {
throw new RSQLParameterUnsupportedFieldException(
"The syntax of the given map search parameter field {" + node.getSelector() + "} is wrong. Syntax is: <enum name>.<key name>");
}
} else {
// sub entity need minimum 1 dot
if (!enumValue.getSubEntityAttributes().isEmpty() && subAttributes.length < 2) {
throw createRSQLParameterUnsupportedException(node, null);
}
}
// build property and validate sub attributes
final StringBuilder fieldNameBuilder = new StringBuilder(enumValue.getJpaEntityFieldName());
for (int i = 1; i < subAttributes.length; i++) {
final String propertyField = getFormattedSubEntityAttribute(enumValue, subAttributes[i]);
if (!enumValue.containsSubEntityAttribute(propertyField)) {
if (i != subAttributes.length - 1 || !enumValue.isMap()) {
throw createRSQLParameterUnsupportedException(node, null);
} // otherwise - the key of map is not in the sub entity attributes
}
fieldNameBuilder.append(RsqlQueryField.SUB_ATTRIBUTE_SEPARATOR).append(propertyField);
}
return new RsqlField(enumValue, enumValue.getSubAttributes(fieldNameBuilder.toString()));
} catch (final IllegalArgumentException e) {
throw createRSQLParameterUnsupportedException(node, e);
}
}
protected String getAndValidatePropertyFieldName(final A propertyEnum, final ComparisonNode node) {
final String[] subAttributes = propertyEnum.getSubAttributes(node.getSelector());
if (propertyEnum.isMap()) {
// enum.key
if (subAttributes.length != 2) {
throw new RSQLParameterUnsupportedFieldException(
"The syntax of the given map search parameter field {" + node.getSelector() + "} is wrong. Syntax is: <enum name>.<key name>");
}
} else {
// sub entity need minimum 1 dot
if (!propertyEnum.getSubEntityAttributes().isEmpty() && subAttributes.length < 2) {
throw createRSQLParameterUnsupportedException(node, null);
}
}
final StringBuilder fieldNameBuilder = new StringBuilder(propertyEnum.getFieldName());
for (int i = 1; i < subAttributes.length; i++) {
final String propertyField = getFormattedSubEntityAttribute(propertyEnum, subAttributes[i]);
if (!propertyEnum.containsSubEntityAttribute(propertyField)) {
if (i != subAttributes.length - 1 || !propertyEnum.isMap()) {
throw createRSQLParameterUnsupportedException(node, null);
} // otherwise - the key of map is not in the sub entity attributes
}
fieldNameBuilder.append(FieldNameProvider.SUB_ATTRIBUTE_SEPARATOR).append(propertyField);
}
return fieldNameBuilder.toString();
}
/**
* @param node current processing node
* @param rootException in case there is a cause otherwise {@code null}
* @return Exception with prepared message extracted from the comparison node.
*/
protected RSQLParameterUnsupportedFieldException createRSQLParameterUnsupportedException(
private RSQLParameterUnsupportedFieldException createRSQLParameterUnsupportedException(
@NotNull final ComparisonNode node,
final Exception rootException) {
return new RSQLParameterUnsupportedFieldException(String.format(
@@ -97,7 +110,7 @@ public abstract class AbstractFieldNameRSQLVisitor<A extends Enum<A> & FieldName
.filter(enumField -> enumField.getSubEntityAttributes().isEmpty()).map(enumField -> {
final String enumFieldName = enumField.name().toLowerCase();
if (enumField.isMap()) {
return enumFieldName + FieldNameProvider.SUB_ATTRIBUTE_SEPARATOR + "keyName";
return enumFieldName + RsqlQueryField.SUB_ATTRIBUTE_SEPARATOR + "keyName";
} else {
return enumFieldName;
}
@@ -107,7 +120,7 @@ public abstract class AbstractFieldNameRSQLVisitor<A extends Enum<A> & FieldName
.filter(enumField -> !enumField.getSubEntityAttributes().isEmpty()).flatMap(enumField -> {
final List<String> subEntity = enumField
.getSubEntityAttributes().stream().map(fieldName -> enumField.name().toLowerCase()
+ FieldNameProvider.SUB_ATTRIBUTE_SEPARATOR + fieldName)
+ RsqlQueryField.SUB_ATTRIBUTE_SEPARATOR + fieldName)
.toList();
return subEntity.stream();

View File

@@ -9,19 +9,19 @@
*/
package org.eclipse.hawkbit.repository.jpa.rsql;
import org.eclipse.hawkbit.repository.FieldNameProvider;
import org.eclipse.hawkbit.repository.RsqlQueryField;
import org.eclipse.hawkbit.repository.rsql.RsqlVisitorFactory;
import cz.jirutka.rsql.parser.ast.RSQLVisitor;
/**
* Factory providing {@link RSQLVisitor} instances which validate the nodes
* based on a given {@link FieldNameProvider}.
* based on a given {@link RsqlQueryField}.
*/
public class DefaultRsqlVisitorFactory implements RsqlVisitorFactory {
@Override
public <A extends Enum<A> & FieldNameProvider> RSQLVisitor<Void, String> validationRsqlVisitor(
public <A extends Enum<A> & RsqlQueryField> RSQLVisitor<Void, String> validationRsqlVisitor(
final Class<A> fieldNameProvider) {
return new FieldValidationRsqlVisitor<>(fieldNameProvider);
}

View File

@@ -9,7 +9,7 @@
*/
package org.eclipse.hawkbit.repository.jpa.rsql;
import org.eclipse.hawkbit.repository.FieldNameProvider;
import org.eclipse.hawkbit.repository.RsqlQueryField;
import cz.jirutka.rsql.parser.ast.AndNode;
import cz.jirutka.rsql.parser.ast.ComparisonNode;
@@ -19,19 +19,19 @@ import cz.jirutka.rsql.parser.ast.RSQLVisitor;
/**
* {@link RSQLVisitor} implementation which validates the nodes (fields) based
* on a given {@link FieldNameProvider} for a given entity type.
* on a given {@link RsqlQueryField} for a given entity type.
*
* @param <A>
* The type the {@link FieldNameProvider} refers to.
* The type the {@link RsqlQueryField} refers to.
*/
public class FieldValidationRsqlVisitor<A extends Enum<A> & FieldNameProvider> extends AbstractFieldNameRSQLVisitor<A>
public class FieldValidationRsqlVisitor<A extends Enum<A> & RsqlQueryField> extends AbstractRSQLVisitor<A>
implements RSQLVisitor<Void, String> {
/**
* Constructs the visitor and initializes it.
*
* @param fieldNameProvider
* The {@link FieldNameProvider} to use for validation.
* The {@link RsqlQueryField} to use for validation.
*/
public FieldValidationRsqlVisitor(final Class<A> fieldNameProvider) {
super(fieldNameProvider);
@@ -49,8 +49,8 @@ public class FieldValidationRsqlVisitor<A extends Enum<A> & FieldNameProvider> e
@Override
public Void visit(final ComparisonNode node, final String param) {
final A fieldName = getFieldEnumByName(node);
getAndValidatePropertyFieldName(fieldName, node);
// get AND validates
getRsqlField(node);
return null;
}

View File

@@ -43,7 +43,7 @@ import cz.jirutka.rsql.parser.ast.OrNode;
import cz.jirutka.rsql.parser.ast.RSQLVisitor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.math.NumberUtils;
import org.eclipse.hawkbit.repository.FieldNameProvider;
import org.eclipse.hawkbit.repository.RsqlQueryField;
import org.eclipse.hawkbit.repository.FieldValueConverter;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
@@ -66,7 +66,7 @@ import org.springframework.util.ObjectUtils;
*/
@Deprecated(forRemoval = true)
@Slf4j
public class JpaQueryRsqlVisitor<A extends Enum<A> & FieldNameProvider, T> extends AbstractFieldNameRSQLVisitor<A>
public class JpaQueryRsqlVisitor<A extends Enum<A> & RsqlQueryField, T> extends AbstractRSQLVisitor<A>
implements RSQLVisitor<List<Predicate>, String> {
public static final Character LIKE_WILDCARD = '*';
@@ -171,13 +171,12 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & FieldNameProvider, T> exten
* @param enumField
* field from a FieldNameProvider to resolve on the persistence
* layer
* @param finalProperty
* dot notated field path
* @param rsqlField RSQL field
* @return the Path for a field
*/
@SuppressWarnings("unchecked")
private Path<Object> getFieldPath(final A enumField, final String finalProperty) {
return (Path<Object>) getFieldPath(root, enumField.getSubAttributes(finalProperty), enumField.isMap(),
private Path<Object> getFieldPath(final A enumField, final RsqlField rsqlField) {
return (Path<Object>) getFieldPath(root, rsqlField.getSubAttributes(), enumField.isMap(),
this::getJoinFieldPath).orElseThrow(
() -> new RSQLParameterUnsupportedFieldException("RSQL field path cannot be empty", null));
}
@@ -218,20 +217,19 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & FieldNameProvider, T> exten
// https://jira.sonarsource.com/browse/SONARJAVA-1478
@SuppressWarnings({ "squid:S2095" })
public List<Predicate> visit(final ComparisonNode node, final String param) {
final A fieldName = getFieldEnumByName(node);
final String finalProperty = getAndValidatePropertyFieldName(fieldName, node);
final RsqlField rsqlField = getRsqlField(node);
final List<String> values = node.getArguments();
final List<Object> transformedValues = new ArrayList<>();
final Path<Object> fieldPath = getFieldPath(fieldName, finalProperty);
final Path<Object> fieldPath = getFieldPath(rsqlField.getEnumValue(), rsqlField);
for (final String value : values) {
transformedValues.add(convertValueIfNecessary(node, fieldName, value, fieldPath));
transformedValues.add(convertValueIfNecessary(node, rsqlField.getEnumValue(), value, fieldPath));
}
this.joinsNeeded = this.joinsNeeded || areJoinsNeeded(node);
return mapToPredicate(node, fieldPath, node.getArguments(), transformedValues, fieldName, finalProperty);
return mapToPredicate(node, fieldPath, node.getArguments(), transformedValues, rsqlField);
}
private static boolean areJoinsNeeded(final ComparisonNode node) {
@@ -307,8 +305,7 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & FieldNameProvider, T> exten
}
private List<Predicate> mapToPredicate(final ComparisonNode node, final Path<Object> fieldPath,
final List<String> values, final List<Object> transformedValues, final A enumField,
final String finalProperty) {
final List<String> values, final List<Object> transformedValues, final RsqlField rsqlField) {
String value = values.get(0);
// if lookup is available, replace macros ...
@@ -316,16 +313,16 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & FieldNameProvider, T> exten
value = virtualPropertyReplacer.replace(value);
}
final Predicate mapPredicate = mapToMapPredicate(node, fieldPath, enumField);
final Predicate mapPredicate = mapToMapPredicate(node, fieldPath, rsqlField.getEnumValue());
final Predicate valuePredicate = addOperatorPredicate(node, getMapValueFieldPath(enumField, fieldPath),
transformedValues, value, finalProperty, enumField);
final Predicate valuePredicate = addOperatorPredicate(node, getMapValueFieldPath(rsqlField.getEnumValue(), fieldPath),
transformedValues, value, rsqlField);
return toSingleList(mapPredicate != null ? cb.and(mapPredicate, valuePredicate) : valuePredicate);
}
private Predicate addOperatorPredicate(final ComparisonNode node, final Path<Object> fieldPath,
final List<Object> transformedValues, final String value, final String finalProperty, final A enumField) {
final List<Object> transformedValues, final String value, final RsqlField rsqlField) {
// only 'equal' and 'notEqual' can handle transformed value like
// enums. The JPA API cannot handle object types for greaterThan etc
@@ -337,7 +334,7 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & FieldNameProvider, T> exten
case "==":
return getEqualToPredicate(transformedValue, fieldPath);
case "!=":
return getNotEqualToPredicate(transformedValue, fieldPath, finalProperty, enumField);
return getNotEqualToPredicate(transformedValue, fieldPath, rsqlField);
case "=gt=":
return cb.greaterThan(pathOfString(fieldPath), value);
case "=ge=":
@@ -349,7 +346,7 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & FieldNameProvider, T> exten
case "=in=":
return in(pathOfString(fieldPath), transformedValues);
case "=out=":
return getOutPredicate(transformedValues, finalProperty, enumField, fieldPath);
return getOutPredicate(transformedValues, rsqlField, fieldPath);
default:
throw new RSQLParameterSyntaxException(
"operator symbol {" + operator + "} is either not supported or not implemented");
@@ -357,18 +354,18 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & FieldNameProvider, T> exten
}
private Predicate getOutPredicate(
final List<Object> transformedValues, final String finalProperty,
final A enumField, final Path<Object> fieldPath) {
final String[] fieldNames = enumField.getSubAttributes(finalProperty);
final List<Object> transformedValues,
final RsqlField rsqlField, final Path<Object> fieldPath) {
final String[] fieldNames = rsqlField.getSubAttributes();
if (isSimpleField(fieldNames, enumField.isMap())) {
if (isSimpleField(fieldNames, rsqlField.getEnumValue().isMap())) {
final Path<String> pathOfString = pathOfString(fieldPath);
return cb.or(cb.isNull(pathOfString), cb.not(in(pathOfString, transformedValues)));
}
clearOuterJoinsIfNotNeeded();
return toNotExistsSubQueryPredicate(fieldNames, enumField, expressionToCompare -> in(expressionToCompare, transformedValues));
return toNotExistsSubQueryPredicate(fieldNames, rsqlField.getEnumValue(), expressionToCompare -> in(expressionToCompare, transformedValues));
}
private Path<Object> getMapValueFieldPath(final A enumField, final Path<Object> fieldPath) {
@@ -422,7 +419,7 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & FieldNameProvider, T> exten
}
private Predicate getNotEqualToPredicate(final Object transformedValue, final Path<Object> fieldPath,
final String finalProperty, final A enumField) {
final RsqlField rsqlField) {
if (transformedValue == null) {
return cb.isNotNull(pathOfString(fieldPath));
@@ -433,9 +430,9 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & FieldNameProvider, T> exten
return cb.and(cb.isNotNull(pathOfString(fieldPath)), cb.notEqual(pathOfString(fieldPath), ""));
}
final String[] fieldNames = enumField.getSubAttributes(finalProperty);
final String[] fieldNames = rsqlField.getSubAttributes();
if (isSimpleField(fieldNames, enumField.isMap())) {
if (isSimpleField(fieldNames, rsqlField.getEnumValue().isMap())) {
if (isPattern(transformedValueStr)) { // a pattern, use like
return cb.or(cb.isNull(pathOfString(fieldPath)), notLike(pathOfString(fieldPath), toSQL(transformedValueStr)));
} else {
@@ -446,9 +443,9 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & FieldNameProvider, T> exten
clearOuterJoinsIfNotNeeded();
if (isPattern(transformedValueStr)) { // a pattern, use like
return toNotExistsSubQueryPredicate(fieldNames, enumField, expressionToCompare -> like(expressionToCompare, toSQL(transformedValueStr)));
return toNotExistsSubQueryPredicate(fieldNames, rsqlField.getEnumValue(), expressionToCompare -> like(expressionToCompare, toSQL(transformedValueStr)));
} else {
return toNotExistsSubQueryPredicate(fieldNames, enumField, expressionToCompare -> equal(expressionToCompare, transformedValueStr));
return toNotExistsSubQueryPredicate(fieldNames, rsqlField.getEnumValue(), expressionToCompare -> equal(expressionToCompare, transformedValueStr));
}
}

View File

@@ -39,7 +39,7 @@ import jakarta.persistence.metamodel.SingularAttribute;
import jakarta.persistence.metamodel.Type;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.math.NumberUtils;
import org.eclipse.hawkbit.repository.FieldNameProvider;
import org.eclipse.hawkbit.repository.RsqlQueryField;
import org.eclipse.hawkbit.repository.FieldValueConverter;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
@@ -57,8 +57,8 @@ import org.springframework.util.ObjectUtils;
* @param <T> the entity type referenced by the root
*/
@Slf4j
public class JpaQueryRsqlVisitorG2<A extends Enum<A> & FieldNameProvider, T>
extends AbstractFieldNameRSQLVisitor<A> implements RSQLVisitor<List<Predicate>, String> {
public class JpaQueryRsqlVisitorG2<A extends Enum<A> & RsqlQueryField, T>
extends AbstractRSQLVisitor<A> implements RSQLVisitor<List<Predicate>, String> {
public static final Character LIKE_WILDCARD = '*';
private static final char ESCAPE_CHAR = '\\';
@@ -110,31 +110,30 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & FieldNameProvider, T>
@Override
public List<Predicate> visit(final ComparisonNode node, final String param) {
final A fieldName = getFieldEnumByName(node);
final String finalProperty = getAndValidatePropertyFieldName(fieldName, node);
final RsqlField rsqlField = getRsqlField(node);
final List<String> values = node.getArguments();
final List<Object> transformedValues = new ArrayList<>();
final Path<Object> fieldPath = getFieldPath(root, fieldName.getSubAttributes(finalProperty), fieldName.isMap());
final Path<Object> fieldPath = getFieldPath(root, rsqlField);
for (final String value : values) {
transformedValues.add(convertValueIfNecessary(node, fieldName, fieldPath, value));
transformedValues.add(convertValueIfNecessary(node, rsqlField.getEnumValue(), fieldPath, value));
}
this.joinsNeeded = this.joinsNeeded || areJoinsNeeded(node);
return mapToPredicate(node, fieldName, finalProperty, fieldPath, node.getArguments(), transformedValues);
return mapToPredicate(node, rsqlField, fieldPath, node.getArguments(), transformedValues);
}
private List<Predicate> mapToPredicate(final ComparisonNode node, final A enumField, final String finalProperty,
private List<Predicate> mapToPredicate(final ComparisonNode node, final RsqlField rsqlField,
final Path<Object> fieldPath,
final List<String> values, final List<Object> transformedValues) {
// if lookup is available, replace macros ...
final String value = virtualPropertyReplacer == null ? values.get(0) : virtualPropertyReplacer.replace(values.get(0));
final Predicate mapPredicate = mapToMapPredicate(node, enumField, fieldPath);
final Predicate valuePredicate = addOperatorPredicate(node, enumField, finalProperty,
getValueFieldPath(enumField, fieldPath), transformedValues, value);
final Predicate mapPredicate = mapToMapPredicate(node, rsqlField.getEnumValue(), fieldPath);
final Predicate valuePredicate = addOperatorPredicate(node, rsqlField,
getValueFieldPath(rsqlField.getEnumValue(), fieldPath), transformedValues, value);
return Collections.singletonList(mapPredicate != null ? cb.and(mapPredicate, valuePredicate) : valuePredicate);
}
@@ -165,7 +164,7 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & FieldNameProvider, T>
"SimpleImmutableEntry are allowed. Neither of those could be found!"));
return equal(fieldPath.get(keyFieldName), keyValue);
}
private Predicate addOperatorPredicate(final ComparisonNode node, final A enumField, final String finalProperty,
private Predicate addOperatorPredicate(final ComparisonNode node, final RsqlField rsqlField,
final Path<Object> fieldPath, final List<Object> transformedValues, final String value) {
// only 'equal' and 'notEqual' can handle transformed value like enums.
// The JPA API cannot handle object types for greaterThan etc. methods.
@@ -173,13 +172,13 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & FieldNameProvider, T>
final String operator = node.getOperator().getSymbol();
return switch (operator) {
case "==" -> getEqualToPredicate(fieldPath, transformedValue);
case "!=" -> getNotEqualToPredicate(enumField, finalProperty, fieldPath, transformedValue);
case "!=" -> getNotEqualToPredicate(rsqlField, fieldPath, transformedValue);
case "=gt=" -> cb.greaterThan(pathOfString(fieldPath), value);
case "=ge=" -> cb.greaterThanOrEqualTo(pathOfString(fieldPath), value);
case "=lt=" -> cb.lessThan(pathOfString(fieldPath), value);
case "=le=" -> cb.lessThanOrEqualTo(pathOfString(fieldPath), value);
case "=in=" -> in(pathOfString(fieldPath), transformedValues);
case "=out=" -> getOutPredicate(enumField, finalProperty, fieldPath, transformedValues);
case "=out=" -> getOutPredicate(rsqlField, fieldPath, transformedValues);
default -> throw new RSQLParameterSyntaxException(
"Operator symbol {" + operator + "} is either not supported or not implemented");
};
@@ -204,7 +203,7 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & FieldNameProvider, T>
return cb.equal(fieldPath, transformedValue);
}
private Predicate getNotEqualToPredicate(final A enumField, final String finalProperty,
private Predicate getNotEqualToPredicate(final RsqlField rsqlField,
final Path<Object> fieldPath, final Object transformedValue) {
if (transformedValue == null) {
return cb.isNotNull(fieldPath);
@@ -215,9 +214,9 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & FieldNameProvider, T>
return cb.and(cb.isNotNull(fieldPath), cb.notEqual(pathOfString(fieldPath), ""));
}
final String[] fieldNames = enumField.getSubAttributes(finalProperty);
final String[] fieldNames = rsqlField.getSubAttributes();
if (isSimpleField(fieldNames, enumField.isMap())) {
if (isSimpleField(fieldNames, rsqlField.getEnumValue().isMap())) {
if (isPattern(transformedValueStr)) { // a pattern, use like
return cb.or(cb.isNull(fieldPath), notLike(pathOfString(fieldPath), toSQL(transformedValueStr)));
} else {
@@ -226,7 +225,7 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & FieldNameProvider, T>
}
clearJoinsIfNotNeeded();
return toNotExistsSubQueryPredicate(enumField, fieldNames, expressionToCompare ->
return toNotExistsSubQueryPredicate(rsqlField, expressionToCompare ->
isPattern(transformedValueStr) ? // a pattern, use like
like(expressionToCompare, toSQL(transformedValueStr)) :
equal(expressionToCompare, transformedValueStr));
@@ -235,23 +234,22 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & FieldNameProvider, T>
return toNullOrNotEqualPredicate(fieldPath, transformedValue);
}
private Predicate getOutPredicate(final A enumField, final String finalProperty, final Path<Object> fieldPath,
private Predicate getOutPredicate(final RsqlField rsqlField, final Path<Object> fieldPath,
final List<Object> transformedValues) {
final String[] fieldNames = enumField.getSubAttributes(finalProperty);
final String[] subAttributes = rsqlField.getSubAttributes();
if (isSimpleField(fieldNames, enumField.isMap())) {
if (isSimpleField(subAttributes, rsqlField.getEnumValue().isMap())) {
return cb.or(cb.isNull(fieldPath), cb.not(in(pathOfString(fieldPath), transformedValues)));
}
clearJoinsIfNotNeeded();
return toNotExistsSubQueryPredicate(enumField, fieldNames,
expressionToCompare -> in(expressionToCompare, transformedValues));
return toNotExistsSubQueryPredicate(rsqlField, expressionToCompare -> in(expressionToCompare, transformedValues));
}
private Path<Object> getFieldPath(
final Root<?> root, final String[] split, final boolean isMapKeyField) {
private Path<Object> getFieldPath(final Root<?> root, final RsqlField rsqlField) {
final String[] split = rsqlField.getSubAttributes();
Path<Object> fieldPath = null;
for (int i = 0, end = isMapKeyField ? split.length - 1 : split.length; i < end; i++) {
for (int i = 0, end = rsqlField.getEnumValue().isMap() ? split.length - 1 : split.length; i < end; i++) {
final String fieldNameSplit = split[i];
fieldPath = fieldPath == null ? getPath(root, fieldNameSplit) : fieldPath.get(fieldNameSplit);
}
@@ -360,14 +358,14 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & FieldNameProvider, T>
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private Predicate toNotExistsSubQueryPredicate(final A enumField, final String[] fieldNames, final Function<Expression<String>, Predicate> subQueryPredicateProvider) {
private Predicate toNotExistsSubQueryPredicate(final RsqlField rsqlField, final Function<Expression<String>, Predicate> subQueryPredicateProvider) {
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 Expression<String> expressionToCompare = getExpressionToCompare(enumField,
getFieldPath(subqueryRoot, fieldNames, enumField.isMap()));
final Predicate equalPredicate = cb.equal(root.get(rsqlField.getEnumValue().identifierFieldName()),
subqueryRoot.get(rsqlField.getEnumValue().identifierFieldName()));
final Expression<String> expressionToCompare = getExpressionToCompare(rsqlField.getEnumValue(),
getFieldPath(subqueryRoot, rsqlField));
final Predicate subQueryPredicate = subQueryPredicateProvider.apply(expressionToCompare);
subquery.select(subqueryRoot).where(cb.and(equalPredicate, subQueryPredicate));
return cb.not(cb.exists(subquery));

View File

@@ -22,7 +22,7 @@ import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.text.StrLookup;
import org.eclipse.hawkbit.repository.FieldNameProvider;
import org.eclipse.hawkbit.repository.RsqlQueryField;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.rsql.RsqlConfigHolder;
@@ -82,7 +82,7 @@ public final class RSQLUtility {
* given RSQL query.
*
* @param rsql the rsql query to be parsed
* @param fieldNameProvider the enum class type which implements the {@link FieldNameProvider}
* @param fieldNameProvider the enum class type which implements the {@link RsqlQueryField}
* @param virtualPropertyReplacer holds the logic how the known macros have to be resolved; may be <code>null</code>
* @param database database in use
*
@@ -91,7 +91,7 @@ public final class RSQLUtility {
* given {@code fieldNameProvider}
* @throws RSQLParameterSyntaxException if the RSQL syntax is wrong
*/
public static <A extends Enum<A> & FieldNameProvider, T> Specification<T> buildRsqlSpecification(
public static <A extends Enum<A> & RsqlQueryField, T> Specification<T> buildRsqlSpecification(
final String rsql, final Class<A> fieldNameProvider,
final VirtualPropertyReplacer virtualPropertyReplacer, final Database database) {
return new RSQLSpecification<>(rsql, fieldNameProvider, virtualPropertyReplacer, database);
@@ -106,7 +106,7 @@ public final class RSQLUtility {
* @throws RSQLParserException if RSQL syntax is invalid
* @throws RSQLParameterUnsupportedFieldException if RSQL key is not allowed
*/
public static <A extends Enum<A> & FieldNameProvider> void validateRsqlFor(
public static <A extends Enum<A> & RsqlQueryField> void validateRsqlFor(
final String rsql, final Class<A> fieldNameProvider) {
final RSQLVisitor<Void, String> visitor =
RsqlConfigHolder.getInstance().getRsqlVisitorFactory().validationRsqlVisitor(fieldNameProvider);
@@ -127,7 +127,7 @@ public final class RSQLUtility {
}
}
private static final class RSQLSpecification<A extends Enum<A> & FieldNameProvider, T> implements Specification<T> {
private static final class RSQLSpecification<A extends Enum<A> & RsqlQueryField, T> implements Specification<T> {
@Serial
private static final long serialVersionUID = 1L;

View File

@@ -1196,7 +1196,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
+ "from target/controller. Expected behaviour is that in case of OK finished update the target will go to "
+ "IN_SYNC status and installed DS is set to the assigned DS entry.")
void assignDistributionSetAndAddFinishedActionStatus() {
final PageRequest pageRequest = PageRequest.of(0, 100, Direction.ASC, ActionStatusFields.ID.getFieldName());
final PageRequest pageRequest = PageRequest.of(0, 100, Direction.ASC, ActionStatusFields.ID.getJpaEntityFieldName());
final DeploymentResult deployResWithDsA = prepareComplexRepo("undep-A-T", 2, "dep-A-T", 4, 1, "dsA");
final DeploymentResult deployResWithDsB = prepareComplexRepo("undep-B-T", 3, "dep-B-T", 5, 1, "dsB");

View File

@@ -19,7 +19,7 @@ import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import org.eclipse.hawkbit.repository.FieldNameProvider;
import org.eclipse.hawkbit.repository.RsqlQueryField;
import org.eclipse.hawkbit.repository.rsql.RsqlConfigHolder;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.persistence.config.PersistenceUnitProperties;
@@ -39,11 +39,11 @@ public class RSQLToSQL {
this.entityManager = entityManager;
}
public <T, A extends Enum<A> & FieldNameProvider> String toSQL(final Class<T> domainClass, final Class<A> fieldsClass, final String rsql, final boolean legacyRsqlVisitor) {
public <T, A extends Enum<A> & RsqlQueryField> String toSQL(final Class<T> domainClass, final Class<A> fieldsClass, final String rsql, final boolean legacyRsqlVisitor) {
return createDbQuery(domainClass, fieldsClass, rsql, legacyRsqlVisitor).getSQLString();
}
public <T, A extends Enum<A> & FieldNameProvider> DatabaseQuery createDbQuery(final Class<T> domainClass, final Class<A> fieldsClass, final String rsql, final boolean legacyRsqlVisitor) {
public <T, A extends Enum<A> & RsqlQueryField> DatabaseQuery createDbQuery(final Class<T> domainClass, final Class<A> fieldsClass, final String rsql, final boolean legacyRsqlVisitor) {
final CriteriaQuery<T> query = createQuery(domainClass, fieldsClass, rsql, legacyRsqlVisitor);
final TypedQuery<?> typedQuery = entityManager.createQuery(query);
// executes the query - otherwise the SQL string is not generated
@@ -52,7 +52,7 @@ public class RSQLToSQL {
return typedQuery.unwrap(JpaQuery.class).getDatabaseQuery();
}
private <T, A extends Enum<A> & FieldNameProvider> CriteriaQuery<T> createQuery(final Class<T> domainClass, final Class<A> fieldsClass, final String rsql, final boolean legacyRsqlVisitor) {
private <T, A extends Enum<A> & RsqlQueryField> CriteriaQuery<T> createQuery(final Class<T> domainClass, final Class<A> fieldsClass, final String rsql, final boolean legacyRsqlVisitor) {
final CriteriaQuery<T> query = entityManager.getCriteriaBuilder().createQuery(domainClass);
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
return query.where(
@@ -65,7 +65,7 @@ public class RSQLToSQL {
);
}
private <T, A extends Enum<A> & FieldNameProvider> Predicate toPredicate(
private <T, A extends Enum<A> & RsqlQueryField> Predicate toPredicate(
final String rsql,
final Class<A> fieldsClass, final VirtualPropertyReplacer virtualPropertyReplacer,
final Root<T> root, final CriteriaQuery<?> query, final CriteriaBuilder cb,

View File

@@ -11,7 +11,7 @@ package org.eclipse.hawkbit.repository.jpa.rsql;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.eclipse.hawkbit.repository.FieldNameProvider;
import org.eclipse.hawkbit.repository.RsqlQueryField;
import org.eclipse.hawkbit.repository.TargetFields;
import org.eclipse.hawkbit.repository.jpa.RepositoryApplicationConfiguration;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
@@ -52,7 +52,7 @@ public class RSQLToSQLTest {
print(JpaTarget.class, TargetFields.class, "(tag!=TAG1 or tag !=TAG2)");
}
private <T, A extends Enum<A> & FieldNameProvider> void print(final Class<T> domainClass, final Class<A> fieldsClass, final String rsql) {
private <T, A extends Enum<A> & RsqlQueryField> void print(final Class<T> domainClass, final Class<A> fieldsClass, final String rsql) {
System.out.println(rsql);
System.out.println("\tlegacy:\n" +
"\t\t" + rsqlToSQL.toSQL(domainClass, fieldsClass, rsql, true));

View File

@@ -38,7 +38,7 @@ import jakarta.persistence.metamodel.EntityType;
import jakarta.persistence.metamodel.SingularAttribute;
import jakarta.persistence.metamodel.Type;
import org.eclipse.hawkbit.repository.DistributionSetFields;
import org.eclipse.hawkbit.repository.FieldNameProvider;
import org.eclipse.hawkbit.repository.RsqlQueryField;
import org.eclipse.hawkbit.repository.SoftwareModuleFields;
import org.eclipse.hawkbit.repository.TargetFields;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
@@ -554,7 +554,7 @@ public class RSQLUtilityTest {
return (Path<Y>) path;
}
private enum TestFieldEnum implements FieldNameProvider {
private enum TestFieldEnum implements RsqlQueryField {
TESTFIELD("testfield"), TESTFIELD_WITH_SUB_ENTITIES("testfieldWithSubEntities", "subentity11", "subentity22");
private final String fieldName;
@@ -570,7 +570,7 @@ public class RSQLUtilityTest {
}
@Override
public String getFieldName() {
public String getJpaEntityFieldName() {
return this.fieldName;
}

View File

@@ -82,7 +82,7 @@ public final class MgmtTargetMapper {
.withRel(MgmtRestConstants.TARGET_V1_ATTRIBUTES).expand());
response.add(linkTo(methodOn(MgmtTargetRestApi.class).getActionHistory(response.getControllerId(), 0,
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE,
ActionFields.ID.getFieldName() + ":" + SortDirection.DESC, null))
ActionFields.ID.getJpaEntityFieldName() + ":" + SortDirection.DESC, null))
.withRel(MgmtRestConstants.TARGET_V1_ACTIONS).expand());
response.add(linkTo(methodOn(MgmtTargetRestApi.class).getMetadata(response.getControllerId(),
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET_VALUE,
@@ -316,7 +316,7 @@ public final class MgmtTargetMapper {
result.add(linkTo(methodOn(MgmtTargetRestApi.class).getActionStatusList(controllerId, action.getId(), 0,
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE,
ActionStatusFields.ID.getFieldName() + ":" + SortDirection.DESC))
ActionStatusFields.ID.getJpaEntityFieldName() + ":" + SortDirection.DESC))
.withRel(MgmtRestConstants.TARGET_V1_ACTION_STATUS).expand());
final Rollout rollout = action.getRollout();

View File

@@ -58,7 +58,7 @@ public final class PagingUtility {
static Sort sanitizeTargetSortParam(final String sortParam) {
if (sortParam == null) {
// default
return Sort.by(Direction.ASC, TargetFields.CONTROLLERID.getFieldName());
return Sort.by(Direction.ASC, TargetFields.CONTROLLERID.getJpaEntityFieldName());
}
return Sort.by(SortUtility.parse(TargetFields.class, sortParam));
}
@@ -66,7 +66,7 @@ public final class PagingUtility {
static Sort sanitizeTargetTypeSortParam(final String sortParam) {
if (sortParam == null) {
// default
return Sort.by(Direction.ASC, TargetTypeFields.ID.getFieldName());
return Sort.by(Direction.ASC, TargetTypeFields.ID.getJpaEntityFieldName());
}
return Sort.by(SortUtility.parse(TargetTypeFields.class, sortParam));
}
@@ -74,7 +74,7 @@ public final class PagingUtility {
static Sort sanitizeTagSortParam(final String sortParam) {
if (sortParam == null) {
// default
return Sort.by(Direction.ASC, TagFields.ID.getFieldName());
return Sort.by(Direction.ASC, TagFields.ID.getJpaEntityFieldName());
}
return Sort.by(SortUtility.parse(TagFields.class, sortParam));
}
@@ -82,7 +82,7 @@ public final class PagingUtility {
static Sort sanitizeTargetFilterQuerySortParam(final String sortParam) {
if (sortParam == null) {
// default
return Sort.by(Direction.ASC, TargetFilterQueryFields.ID.getFieldName());
return Sort.by(Direction.ASC, TargetFilterQueryFields.ID.getJpaEntityFieldName());
}
return Sort.by(SortUtility.parse(TargetFilterQueryFields.class, sortParam));
}
@@ -90,7 +90,7 @@ public final class PagingUtility {
static Sort sanitizeSoftwareModuleSortParam(final String sortParam) {
if (sortParam == null) {
// default
return Sort.by(Direction.ASC, SoftwareModuleFields.ID.getFieldName());
return Sort.by(Direction.ASC, SoftwareModuleFields.ID.getJpaEntityFieldName());
}
return Sort.by(SortUtility.parse(SoftwareModuleFields.class, sortParam));
}
@@ -98,7 +98,7 @@ public final class PagingUtility {
static Sort sanitizeSoftwareModuleTypeSortParam(final String sortParam) {
if (sortParam == null) {
// default
return Sort.by(Direction.ASC, SoftwareModuleTypeFields.ID.getFieldName());
return Sort.by(Direction.ASC, SoftwareModuleTypeFields.ID.getJpaEntityFieldName());
}
return Sort.by(SortUtility.parse(SoftwareModuleTypeFields.class, sortParam));
}
@@ -106,7 +106,7 @@ public final class PagingUtility {
static Sort sanitizeDistributionSetSortParam(final String sortParam) {
if (sortParam == null) {
// default
return Sort.by(Direction.ASC, DistributionSetFields.ID.getFieldName());
return Sort.by(Direction.ASC, DistributionSetFields.ID.getJpaEntityFieldName());
}
return Sort.by(SortUtility.parse(DistributionSetFields.class, sortParam));
}
@@ -114,7 +114,7 @@ public final class PagingUtility {
static Sort sanitizeDistributionSetTypeSortParam(final String sortParam) {
if (sortParam == null) {
// default
return Sort.by(Direction.ASC, DistributionSetTypeFields.ID.getFieldName());
return Sort.by(Direction.ASC, DistributionSetTypeFields.ID.getJpaEntityFieldName());
}
return Sort.by(SortUtility.parse(DistributionSetTypeFields.class, sortParam));
}
@@ -123,7 +123,7 @@ public final class PagingUtility {
if (sortParam == null) {
// default sort is DESC in case of action to match behavior
// of management UI (last entry on top)
return Sort.by(Direction.DESC, ActionFields.ID.getFieldName());
return Sort.by(Direction.DESC, ActionFields.ID.getJpaEntityFieldName());
}
return Sort.by(SortUtility.parse(ActionFields.class, sortParam));
}
@@ -132,7 +132,7 @@ public final class PagingUtility {
if (sortParam == null) {
// default sort is DESC in case of action status to match behavior
// of management UI (last entry on top)
return Sort.by(Direction.DESC, ActionStatusFields.ID.getFieldName());
return Sort.by(Direction.DESC, ActionStatusFields.ID.getJpaEntityFieldName());
}
return Sort.by(SortUtility.parse(ActionStatusFields.class, sortParam));
}
@@ -140,7 +140,7 @@ public final class PagingUtility {
static Sort sanitizeDistributionSetMetadataSortParam(final String sortParam) {
if (sortParam == null) {
// default
return Sort.by(Direction.ASC, DistributionSetMetadataFields.KEY.getFieldName());
return Sort.by(Direction.ASC, DistributionSetMetadataFields.KEY.getJpaEntityFieldName());
}
return Sort.by(SortUtility.parse(DistributionSetMetadataFields.class, sortParam));
}
@@ -148,7 +148,7 @@ public final class PagingUtility {
static Sort sanitizeSoftwareModuleMetadataSortParam(final String sortParam) {
if (sortParam == null) {
// default
return Sort.by(Direction.ASC, SoftwareModuleMetadataFields.KEY.getFieldName());
return Sort.by(Direction.ASC, SoftwareModuleMetadataFields.KEY.getJpaEntityFieldName());
}
return Sort.by(SortUtility.parse(SoftwareModuleMetadataFields.class, sortParam));
}
@@ -156,7 +156,7 @@ public final class PagingUtility {
static Sort sanitizeRolloutSortParam(final String sortParam) {
if (sortParam == null) {
// default
return Sort.by(Direction.ASC, RolloutFields.ID.getFieldName());
return Sort.by(Direction.ASC, RolloutFields.ID.getJpaEntityFieldName());
}
return Sort.by(SortUtility.parse(RolloutFields.class, sortParam));
}
@@ -164,7 +164,7 @@ public final class PagingUtility {
static Sort sanitizeRolloutGroupSortParam(final String sortParam) {
if (sortParam == null) {
// default
return Sort.by(Direction.ASC, RolloutGroupFields.ID.getFieldName());
return Sort.by(Direction.ASC, RolloutGroupFields.ID.getJpaEntityFieldName());
}
return Sort.by(SortUtility.parse(RolloutGroupFields.class, sortParam));
}

View File

@@ -155,7 +155,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
assertThat(actions).hasSize(2);
updateActionStatus(actions.get(0), Status.FINISHED, null, "test");
final PageRequest pageRequest = PageRequest.of(0, 1000, Direction.ASC, ActionFields.ID.getFieldName());
final PageRequest pageRequest = PageRequest.of(0, 1000, Direction.ASC, ActionFields.ID.getJpaEntityFieldName());
final Action action = deploymentManagement.findActionsByTarget(knownTargetId, pageRequest).getContent().get(0);
final ActionStatus status = deploymentManagement.findActionStatusByAction(PAGE, action.getId()).getContent()

View File

@@ -13,7 +13,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import org.eclipse.hawkbit.repository.FieldNameProvider;
import org.eclipse.hawkbit.repository.RsqlQueryField;
import org.eclipse.hawkbit.rest.exception.SortParameterSyntaxErrorException;
import org.eclipse.hawkbit.rest.exception.SortParameterUnsupportedDirectionException;
import org.eclipse.hawkbit.rest.exception.SortParameterUnsupportedFieldException;
@@ -53,11 +53,11 @@ public final class SortUtility {
* should be related to.
* @param <T>
* the type of the enumeration which must be derived from
* {@link FieldNameProvider}
* {@link RsqlQueryField}
* @param sortString
* the string representation of the query parameters. Might be
* {@code null} or an empty string.
* @return a list which holds the {@link FieldNameProvider} and the specific
* @return a list which holds the {@link RsqlQueryField} 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
@@ -67,7 +67,7 @@ public final class SortUtility {
* @throws SortParameterUnsupportedDirectionException
* if the given direction is not "ASC" or "DESC"
*/
public static <T extends Enum<T> & FieldNameProvider> List<Order> parse(final Class<T> enumType,
public static <T extends Enum<T> & RsqlQueryField> List<Order> parse(final Class<T> enumType,
final String sortString) throws SortParameterSyntaxErrorException {
final List<Order> parsedSortings = new ArrayList<>();
// scan the sort tuples e.g. field:direction
@@ -84,7 +84,7 @@ public final class SortUtility {
final T identifier = getAttributeIdentifierByName(enumType, fieldName);
final Direction sortDirection = getDirection(sortDirectionStr);
parsedSortings.add(new Order(sortDirection, identifier.getFieldName()));
parsedSortings.add(new Order(sortDirection, identifier.getJpaEntityFieldName()));
} else {
throw new SortParameterSyntaxErrorException();
}
@@ -103,12 +103,12 @@ public final class SortUtility {
* the name of the enum
* @param <T>
* the type of the enumeration which must be derived from
* {@link FieldNameProvider}
* {@link RsqlQueryField}
* @return the corresponding enum
* @throws SortParameterUnsupportedFieldException
* if there is no matching enum for the specified name
*/
private static <T extends Enum<T> & FieldNameProvider> T getAttributeIdentifierByName(final Class<T> enumType,
private static <T extends Enum<T> & RsqlQueryField> T getAttributeIdentifierByName(final Class<T> enumType,
final String name) {
try {
return Enum.valueOf(enumType, name.toUpperCase());