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

View File

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

View File

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

View File

@@ -15,19 +15,19 @@ import lombok.Getter;
* Sort fields for DistributionSetMetadata. * Sort fields for DistributionSetMetadata.
*/ */
@Getter @Getter
public enum DistributionSetMetadataFields implements FieldNameProvider { public enum DistributionSetMetadataFields implements RsqlQueryField {
KEY("key"), KEY("key"),
VALUE("value"); VALUE("value");
private final String fieldName; private final String jpaEntityFieldName;
DistributionSetMetadataFields(final String fieldName) { DistributionSetMetadataFields(final String jpaEntityFieldName) {
this.fieldName = fieldName; this.jpaEntityFieldName = jpaEntityFieldName;
} }
@Override @Override
public String identifierFieldName() { 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. * filtering over distribution set fields also.
*/ */
@Getter @Getter
public enum DistributionSetTagFields implements FieldNameProvider { public enum DistributionSetTagFields implements RsqlQueryField {
ID(TagFields.ID.getFieldName()), ID(TagFields.ID.getJpaEntityFieldName()),
NAME(TagFields.NAME.getFieldName()), NAME(TagFields.NAME.getJpaEntityFieldName()),
DESCRIPTION(TagFields.DESCRIPTION.getFieldName()), DESCRIPTION(TagFields.DESCRIPTION.getJpaEntityFieldName()),
COLOUR(TagFields.COLOUR.getFieldName()), COLOUR(TagFields.COLOUR.getJpaEntityFieldName()),
DISTRIBUTIONSET("assignedToDistributionSet", 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; private final List<String> subEntityAttributes;
DistributionSetTagFields(final String fieldName) { DistributionSetTagFields(final String jpaEntityFieldName) {
this.fieldName = fieldName; this.jpaEntityFieldName = jpaEntityFieldName;
this.subEntityAttributes = Collections.emptyList(); this.subEntityAttributes = Collections.emptyList();
} }
DistributionSetTagFields(final String fieldName, final String... subEntityAttributes) { DistributionSetTagFields(final String jpaEntityFieldName, final String... subEntityAttributes) {
this.fieldName = fieldName; this.jpaEntityFieldName = jpaEntityFieldName;
this.subEntityAttributes = List.of(subEntityAttributes); this.subEntityAttributes = List.of(subEntityAttributes);
} }
} }

View File

@@ -16,16 +16,16 @@ import lombok.Getter;
* the REST API e.g. for sorting etc. * the REST API e.g. for sorting etc.
*/ */
@Getter @Getter
public enum DistributionSetTypeFields implements FieldNameProvider { public enum DistributionSetTypeFields implements RsqlQueryField {
ID("id"), ID("id"),
KEY("key"), KEY("key"),
NAME("name"), NAME("name"),
DESCRIPTION("description"); DESCRIPTION("description");
private final String fieldName; private final String jpaEntityFieldName;
DistributionSetTypeFields(final String fieldName) { DistributionSetTypeFields(final String jpaEntityFieldName) {
this.fieldName = fieldName; 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. * Describing the fields of the Rollout model which can be used in the REST API e.g. for sorting etc.
*/ */
@Getter @Getter
public enum RolloutFields implements FieldNameProvider { public enum RolloutFields implements RsqlQueryField {
ID("id"), ID("id"),
NAME("name"), NAME("name"),
DESCRIPTION("description"), DESCRIPTION("description"),
STATUS("status"), STATUS("status"),
DISTRIBUTIONSET("distributionSet", DistributionSetFields.ID.getFieldName(), DISTRIBUTIONSET("distributionSet", DistributionSetFields.ID.getJpaEntityFieldName(),
DistributionSetFields.NAME.getFieldName(), DistributionSetFields.VERSION.getFieldName(), DistributionSetFields.NAME.getJpaEntityFieldName(), DistributionSetFields.VERSION.getJpaEntityFieldName(),
DistributionSetFields.TYPE.getFieldName()); DistributionSetFields.TYPE.getJpaEntityFieldName());
private final String fieldName; private final String jpaEntityFieldName;
private final List<String> subEntityAttributes; private final List<String> subEntityAttributes;
RolloutFields(final String fieldName) { RolloutFields(final String jpaEntityFieldName) {
this.fieldName = fieldName; this.jpaEntityFieldName = jpaEntityFieldName;
this.subEntityAttributes = Collections.emptyList(); this.subEntityAttributes = Collections.emptyList();
} }
RolloutFields(final String fieldName, final String... subEntityAttributes) { RolloutFields(final String jpaEntityFieldName, final String... subEntityAttributes) {
this.fieldName = fieldName; this.jpaEntityFieldName = jpaEntityFieldName;
this.subEntityAttributes = List.of(subEntityAttributes); 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. * Describing the fields of the RolloutGroup model which can be used in the REST API e.g. for sorting etc.
*/ */
@Getter @Getter
public enum RolloutGroupFields implements FieldNameProvider { public enum RolloutGroupFields implements RsqlQueryField {
ID("id"), ID("id"),
NAME("name"), NAME("name"),
DESCRIPTION("description"); DESCRIPTION("description");
private final String fieldName; private final String jpaEntityFieldName;
RolloutGroupFields(final String fieldName) { RolloutGroupFields(final String jpaEntityFieldName) {
this.fieldName = fieldName; this.jpaEntityFieldName = jpaEntityFieldName;
} }
} }

View File

@@ -15,15 +15,13 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Optional; import java.util.Optional;
import jakarta.validation.constraints.NotNull;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
/** /**
* An interface for declaring the name of the field described in the database * An RSQL query field interface extended by all the fields that could be used in RSQL queries.
* which is used as string representation of the field, e.g. for sorting the
* fields over REST.
*/ */
@FunctionalInterface public interface RsqlQueryField {
public interface FieldNameProvider {
/** /**
* Separator for the sub attributes * Separator for the sub attributes
@@ -32,10 +30,10 @@ public interface FieldNameProvider {
String SUB_ATTRIBUTE_SPLIT_REGEX = "\\" + SUB_ATTRIBUTE_SEPARATOR; String SUB_ATTRIBUTE_SPLIT_REGEX = "\\" + SUB_ATTRIBUTE_SEPARATOR;
/** /**
* @return the string representation of the underlying persistence field * @return the string representation of the underlying persistence field name e.g. in case of sorting.
* name e.g. in case of sorting. Never {@code null}.
*/ */
String getFieldName(); @NotNull
String getJpaEntityFieldName();
/** /**
* Returns the sub attributes * Returns the sub attributes
@@ -48,7 +46,7 @@ public interface FieldNameProvider {
final String[] subAttributes = propertyFieldName.split(SUB_ATTRIBUTE_SPLIT_REGEX, 2); final String[] subAttributes = propertyFieldName.split(SUB_ATTRIBUTE_SPLIT_REGEX, 2);
// [0] field name | [1] key name (could miss, e.g. for target attributes) // [0] field name | [1] key name (could miss, e.g. for target attributes)
final String mapKeyName = subAttributes.length == 2 ? subAttributes[1] : null; 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 { } else {
return propertyFieldName.split(SUB_ATTRIBUTE_SPLIT_REGEX); 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. * Describing the fields of the SoftwareModule model which can be used in the REST API e.g. for sorting etc.
*/ */
@Getter @Getter
public enum SoftwareModuleFields implements FieldNameProvider { public enum SoftwareModuleFields implements RsqlQueryField {
ID("id"), ID("id"),
TYPE("type.key"), TYPE("type.key"),
@@ -28,15 +28,15 @@ public enum SoftwareModuleFields implements FieldNameProvider {
VERSION("version"), VERSION("version"),
METADATA("metadata", new SimpleImmutableEntry<>("key", "value")); METADATA("metadata", new SimpleImmutableEntry<>("key", "value"));
private final String fieldName; private final String jpaEntityFieldName;
private Entry<String, String> subEntityMapTuple; private Entry<String, String> subEntityMapTuple;
SoftwareModuleFields(final String fieldName) { SoftwareModuleFields(final String jpaEntityFieldName) {
this(fieldName, null); this(jpaEntityFieldName, null);
} }
SoftwareModuleFields(final String fieldName, final Entry<String, String> subEntityMapTuple) { SoftwareModuleFields(final String jpaEntityFieldName, final Entry<String, String> subEntityMapTuple) {
this.fieldName = fieldName; this.jpaEntityFieldName = jpaEntityFieldName;
this.subEntityMapTuple = subEntityMapTuple; this.subEntityMapTuple = subEntityMapTuple;
} }

View File

@@ -15,20 +15,20 @@ import lombok.Getter;
* Sort fields for SoftwareModuleMetadata. * Sort fields for SoftwareModuleMetadata.
*/ */
@Getter @Getter
public enum SoftwareModuleMetadataFields implements FieldNameProvider { public enum SoftwareModuleMetadataFields implements RsqlQueryField {
KEY("key"), KEY("key"),
VALUE("value"), VALUE("value"),
TARGETVISIBLE("targetVisible"); TARGETVISIBLE("targetVisible");
private final String fieldName; private final String jpaEntityFieldName;
SoftwareModuleMetadataFields(final String fieldName) { SoftwareModuleMetadataFields(final String jpaEntityFieldName) {
this.fieldName = fieldName; this.jpaEntityFieldName = jpaEntityFieldName;
} }
@Override @Override
public String identifierFieldName() { 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. * Describing the fields of the SoftwareModuleType model which can be used in the REST API e.g. for sorting etc.
*/ */
@Getter @Getter
public enum SoftwareModuleTypeFields implements FieldNameProvider { public enum SoftwareModuleTypeFields implements RsqlQueryField {
ID("id"), ID("id"),
KEY("key"), KEY("key"),
@@ -23,9 +23,9 @@ public enum SoftwareModuleTypeFields implements FieldNameProvider {
DESCRIPTION("description"), DESCRIPTION("description"),
MAXASSIGNMENTS("maxAssignments"); MAXASSIGNMENTS("maxAssignments");
private final String fieldName; private final String jpaEntityFieldName;
SoftwareModuleTypeFields(final String fieldName) { SoftwareModuleTypeFields(final String jpaEntityFieldName) {
this.fieldName = fieldName; 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. * Describing the fields of the Tag model which can be used in the REST API e.g. for sorting etc.
*/ */
@Getter @Getter
public enum TagFields implements FieldNameProvider { public enum TagFields implements RsqlQueryField {
ID("id"), ID("id"),
NAME("name"), NAME("name"),
DESCRIPTION("description"), DESCRIPTION("description"),
COLOUR("colour"); COLOUR("colour");
private final String fieldName; private final String jpaEntityFieldName;
TagFields(final String fieldName) { TagFields(final String jpaEntityFieldName) {
this.fieldName = fieldName; this.jpaEntityFieldName = jpaEntityFieldName;
} }
} }

View File

@@ -22,7 +22,7 @@ import java.util.Optional;
* e.g. for sorting etc. * e.g. for sorting etc.
*/ */
@Getter @Getter
public enum TargetFields implements FieldNameProvider { public enum TargetFields implements RsqlQueryField {
ID("controllerId"), ID("controllerId"),
NAME("name"), NAME("name"),
@@ -38,26 +38,26 @@ public enum TargetFields implements FieldNameProvider {
TAG("tags.name"), TAG("tags.name"),
LASTCONTROLLERREQUESTAT("lastTargetQuery"), LASTCONTROLLERREQUESTAT("lastTargetQuery"),
METADATA("metadata", new SimpleImmutableEntry<>("key", "value")), 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 List<String> subEntityAttributes;
private final Entry<String, String> subEntityMapTuple; private final Entry<String, String> subEntityMapTuple;
TargetFields(final String fieldName) { TargetFields(final String jpaEntityFieldName) {
this(fieldName, Collections.emptyList(), null); this(jpaEntityFieldName, Collections.emptyList(), null);
} }
TargetFields(final String fieldName, final String... subEntityAttributes) { TargetFields(final String jpaEntityFieldName, final String... subEntityAttributes) {
this(fieldName, List.of(subEntityAttributes), null); this(jpaEntityFieldName, List.of(subEntityAttributes), null);
} }
TargetFields(final String fieldName, final Entry<String, String> subEntityMapTuple) { TargetFields(final String jpaEntityFieldName, final Entry<String, String> subEntityMapTuple) {
this(fieldName, Collections.emptyList(), subEntityMapTuple); this(jpaEntityFieldName, Collections.emptyList(), subEntityMapTuple);
} }
TargetFields(final String fieldName, final List<String> subEntityAttributes, final Entry<String, String> subEntityMapTuple) { TargetFields(final String jpaEntityFieldName, final List<String> subEntityAttributes, final Entry<String, String> subEntityMapTuple) {
this.fieldName = fieldName; this.jpaEntityFieldName = jpaEntityFieldName;
this.subEntityAttributes = subEntityAttributes; this.subEntityAttributes = subEntityAttributes;
this.subEntityMapTuple = subEntityMapTuple; 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. * Describing the fields of the Target model which can be used in the REST API e.g. for sorting etc.
*/ */
@Getter @Getter
public enum TargetFilterQueryFields implements FieldNameProvider { public enum TargetFilterQueryFields implements RsqlQueryField {
ID("id"), ID("id"),
NAME("name"), NAME("name"),
AUTOASSIGNDISTRIBUTIONSET("autoAssignDistributionSet", "name", "version"); AUTOASSIGNDISTRIBUTIONSET("autoAssignDistributionSet", "name", "version");
private final String fieldName; private final String jpaEntityFieldName;
private List<String> subEntityAttributes; private List<String> subEntityAttributes;
TargetFilterQueryFields(final String fieldName) { TargetFilterQueryFields(final String jpaEntityFieldName) {
this(fieldName, Collections.emptyList()); this(jpaEntityFieldName, Collections.emptyList());
} }
TargetFilterQueryFields(final String fieldName, final String... subEntityAttribues) { TargetFilterQueryFields(final String jpaEntityFieldName, final String... subEntityAttribues) {
this(fieldName, List.of(subEntityAttribues)); this(jpaEntityFieldName, List.of(subEntityAttribues));
} }
TargetFilterQueryFields(final String fieldName, final List<String> subEntityAttribues) { TargetFilterQueryFields(final String jpaEntityFieldName, final List<String> subEntityAttribues) {
this.fieldName = fieldName; this.jpaEntityFieldName = jpaEntityFieldName;
this.subEntityAttributes = subEntityAttribues; this.subEntityAttributes = subEntityAttribues;
} }
} }

View File

@@ -15,19 +15,19 @@ import lombok.Getter;
* Sort fields for TargetMetadata. * Sort fields for TargetMetadata.
*/ */
@Getter @Getter
public enum TargetMetadataFields implements FieldNameProvider { public enum TargetMetadataFields implements RsqlQueryField {
KEY("key"), KEY("key"),
VALUE("value"); VALUE("value");
private final String fieldName; private final String jpaEntityFieldName;
TargetMetadataFields(final String fieldName) { TargetMetadataFields(final String jpaEntityFieldName) {
this.fieldName = fieldName; this.jpaEntityFieldName = jpaEntityFieldName;
} }
@Override @Override
public String identifierFieldName() { 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. * Additionally, here were added fields for Target in order filtering over target fields also.
*/ */
@Getter @Getter
public enum TargetTagFields implements FieldNameProvider { public enum TargetTagFields implements RsqlQueryField {
ID(TagFields.ID.getFieldName()), ID(TagFields.ID.getJpaEntityFieldName()),
NAME(TagFields.NAME.getFieldName()), NAME(TagFields.NAME.getJpaEntityFieldName()),
DESCRIPTION(TagFields.DESCRIPTION.getFieldName()), DESCRIPTION(TagFields.DESCRIPTION.getJpaEntityFieldName()),
COLOUR(TagFields.COLOUR.getFieldName()); COLOUR(TagFields.COLOUR.getJpaEntityFieldName());
private final String fieldName; private final String jpaEntityFieldName;
TargetTagFields(final String fieldName) { TargetTagFields(final String jpaEntityFieldName) {
this.fieldName = fieldName; 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 * Describing the fields of the TargetType model which can be used in the REST API
*/ */
@Getter @Getter
public enum TargetTypeFields implements FieldNameProvider { public enum TargetTypeFields implements RsqlQueryField {
ID("id"), ID("id"),
KEY("key"), KEY("key"),
NAME("name"), NAME("name"),
DESCRIPTION("description"); DESCRIPTION("description");
private final String fieldName; private final String jpaEntityFieldName;
TargetTypeFields(final String fieldName) { TargetTypeFields(final String jpaEntityFieldName) {
this.fieldName = fieldName; this.jpaEntityFieldName = jpaEntityFieldName;
} }
} }

View File

@@ -27,16 +27,16 @@ public class FileNameFieldsTest {
public void repositoryManagementMethodsArePreAuthorizedAnnotated() { public void repositoryManagementMethodsArePreAuthorizedAnnotated() {
final String packageName = getClass().getPackage().getName(); final String packageName = getClass().getPackage().getName();
try (final ScanResult scanResult = new ClassGraph().acceptPackages(packageName).scan()) { 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() .stream()
.filter(classInPackage -> classInPackage.implementsInterface(FieldNameProvider.class)) .filter(classInPackage -> classInPackage.implementsInterface(RsqlQueryField.class))
.map(ClassInfo::loadClass) .map(ClassInfo::loadClass)
.map(clazz -> (Class<? extends FieldNameProvider>) clazz) .map(clazz -> (Class<? extends RsqlQueryField>) clazz)
.toList(); .toList();
assertThat(matchingClasses).isNotEmpty(); assertThat(matchingClasses).isNotEmpty();
matchingClasses.forEach(providerClass -> { matchingClasses.forEach(providerClass -> {
assertThat(providerClass.getEnumConstants()).isNotEmpty(); assertThat(providerClass.getEnumConstants()).isNotEmpty();
for (final FieldNameProvider provider : providerClass.getEnumConstants()) { for (final RsqlQueryField provider : providerClass.getEnumConstants()) {
if (provider.isMap() && !provider.getSubEntityAttributes().isEmpty()) { if (provider.isMap() && !provider.getSubEntityAttributes().isEmpty()) {
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
"Currently sub-entity attributes for maps are not supported, alternatively you could use the key/value tuple, defined by SimpleImmutableEntry class"); "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; 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.Node;
import cz.jirutka.rsql.parser.ast.RSQLVisitor; import cz.jirutka.rsql.parser.ast.RSQLVisitor;
@@ -23,16 +23,16 @@ public interface RsqlVisitorFactory {
/** /**
* Provides a {@link RSQLVisitor} instance for validating RSQL queries based * Provides a {@link RSQLVisitor} instance for validating RSQL queries based
* on the given {@link FieldNameProvider}. * on the given {@link RsqlQueryField}.
* *
* @param <A> * @param <A>
* The type of the {@link FieldNameProvider}. * The type of the {@link RsqlQueryField}.
* @param fieldNameProvider * @param fieldNameProvider
* providing accessing to the relevant field names. * providing accessing to the relevant field names.
* *
* @return An {@link RSQLVisitor} to validate the {@link Node}s of an RSQL * @return An {@link RSQLVisitor} to validate the {@link Node}s of an RSQL
* query. * 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 jakarta.validation.constraints.NotNull;
import lombok.Value;
import lombok.extern.slf4j.Slf4j; 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 org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import cz.jirutka.rsql.parser.ast.ComparisonNode; import cz.jirutka.rsql.parser.ast.ComparisonNode;
@Slf4j @Slf4j
public abstract class AbstractFieldNameRSQLVisitor<A extends Enum<A> & FieldNameProvider> { public abstract class AbstractRSQLVisitor<A extends Enum<A> & RsqlQueryField> {
private final Class<A> fieldNameProvider; 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; this.fieldNameProvider = fieldNameProvider;
} }
protected A getFieldEnumByName(final ComparisonNode node) { protected RsqlField getRsqlField(final ComparisonNode node) {
final String[] graph = node.getSelector().split(FieldNameProvider.SUB_ATTRIBUTE_SPLIT_REGEX); final String[] graph = node.getSelector().split(RsqlQueryField.SUB_ATTRIBUTE_SPLIT_REGEX);
final String enumName = graph.length == 0 ? node.getSelector() : graph[0]; final String enumName = graph.length == 0 ? node.getSelector() : graph[0];
log.debug("get field identifier by name {} of enum type {}", enumName, fieldNameProvider); log.debug("get field identifier by name {} of enum type {}", enumName, fieldNameProvider);
try { 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) { } catch (final IllegalArgumentException e) {
throw createRSQLParameterUnsupportedException(node, 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 node current processing node
* @param rootException in case there is a cause otherwise {@code null} * @param rootException in case there is a cause otherwise {@code null}
* @return Exception with prepared message extracted from the comparison node. * @return Exception with prepared message extracted from the comparison node.
*/ */
protected RSQLParameterUnsupportedFieldException createRSQLParameterUnsupportedException( private RSQLParameterUnsupportedFieldException createRSQLParameterUnsupportedException(
@NotNull final ComparisonNode node, @NotNull final ComparisonNode node,
final Exception rootException) { final Exception rootException) {
return new RSQLParameterUnsupportedFieldException(String.format( 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 -> { .filter(enumField -> enumField.getSubEntityAttributes().isEmpty()).map(enumField -> {
final String enumFieldName = enumField.name().toLowerCase(); final String enumFieldName = enumField.name().toLowerCase();
if (enumField.isMap()) { if (enumField.isMap()) {
return enumFieldName + FieldNameProvider.SUB_ATTRIBUTE_SEPARATOR + "keyName"; return enumFieldName + RsqlQueryField.SUB_ATTRIBUTE_SEPARATOR + "keyName";
} else { } else {
return enumFieldName; return enumFieldName;
} }
@@ -107,7 +120,7 @@ public abstract class AbstractFieldNameRSQLVisitor<A extends Enum<A> & FieldName
.filter(enumField -> !enumField.getSubEntityAttributes().isEmpty()).flatMap(enumField -> { .filter(enumField -> !enumField.getSubEntityAttributes().isEmpty()).flatMap(enumField -> {
final List<String> subEntity = enumField final List<String> subEntity = enumField
.getSubEntityAttributes().stream().map(fieldName -> enumField.name().toLowerCase() .getSubEntityAttributes().stream().map(fieldName -> enumField.name().toLowerCase()
+ FieldNameProvider.SUB_ATTRIBUTE_SEPARATOR + fieldName) + RsqlQueryField.SUB_ATTRIBUTE_SEPARATOR + fieldName)
.toList(); .toList();
return subEntity.stream(); return subEntity.stream();

View File

@@ -9,19 +9,19 @@
*/ */
package org.eclipse.hawkbit.repository.jpa.rsql; 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 org.eclipse.hawkbit.repository.rsql.RsqlVisitorFactory;
import cz.jirutka.rsql.parser.ast.RSQLVisitor; import cz.jirutka.rsql.parser.ast.RSQLVisitor;
/** /**
* Factory providing {@link RSQLVisitor} instances which validate the nodes * 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 { public class DefaultRsqlVisitorFactory implements RsqlVisitorFactory {
@Override @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) { final Class<A> fieldNameProvider) {
return new FieldValidationRsqlVisitor<>(fieldNameProvider); return new FieldValidationRsqlVisitor<>(fieldNameProvider);
} }

View File

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

View File

@@ -43,7 +43,7 @@ import cz.jirutka.rsql.parser.ast.OrNode;
import cz.jirutka.rsql.parser.ast.RSQLVisitor; import cz.jirutka.rsql.parser.ast.RSQLVisitor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.math.NumberUtils; 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.FieldValueConverter;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException; import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException; import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
@@ -66,7 +66,7 @@ import org.springframework.util.ObjectUtils;
*/ */
@Deprecated(forRemoval = true) @Deprecated(forRemoval = true)
@Slf4j @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> { implements RSQLVisitor<List<Predicate>, String> {
public static final Character LIKE_WILDCARD = '*'; public static final Character LIKE_WILDCARD = '*';
@@ -171,13 +171,12 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & FieldNameProvider, T> exten
* @param enumField * @param enumField
* field from a FieldNameProvider to resolve on the persistence * field from a FieldNameProvider to resolve on the persistence
* layer * layer
* @param finalProperty * @param rsqlField RSQL field
* dot notated field path
* @return the Path for a field * @return the Path for a field
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private Path<Object> getFieldPath(final A enumField, final String finalProperty) { private Path<Object> getFieldPath(final A enumField, final RsqlField rsqlField) {
return (Path<Object>) getFieldPath(root, enumField.getSubAttributes(finalProperty), enumField.isMap(), return (Path<Object>) getFieldPath(root, rsqlField.getSubAttributes(), enumField.isMap(),
this::getJoinFieldPath).orElseThrow( this::getJoinFieldPath).orElseThrow(
() -> new RSQLParameterUnsupportedFieldException("RSQL field path cannot be empty", null)); () -> 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 // https://jira.sonarsource.com/browse/SONARJAVA-1478
@SuppressWarnings({ "squid:S2095" }) @SuppressWarnings({ "squid:S2095" })
public List<Predicate> visit(final ComparisonNode node, final String param) { public List<Predicate> visit(final ComparisonNode node, final String param) {
final A fieldName = getFieldEnumByName(node); final RsqlField rsqlField = getRsqlField(node);
final String finalProperty = getAndValidatePropertyFieldName(fieldName, node);
final List<String> values = node.getArguments(); final List<String> values = node.getArguments();
final List<Object> transformedValues = new ArrayList<>(); 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) { 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); 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) { 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, private List<Predicate> mapToPredicate(final ComparisonNode node, final Path<Object> fieldPath,
final List<String> values, final List<Object> transformedValues, final A enumField, final List<String> values, final List<Object> transformedValues, final RsqlField rsqlField) {
final String finalProperty) {
String value = values.get(0); String value = values.get(0);
// if lookup is available, replace macros ... // if lookup is available, replace macros ...
@@ -316,16 +313,16 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & FieldNameProvider, T> exten
value = virtualPropertyReplacer.replace(value); 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), final Predicate valuePredicate = addOperatorPredicate(node, getMapValueFieldPath(rsqlField.getEnumValue(), fieldPath),
transformedValues, value, finalProperty, enumField); transformedValues, value, rsqlField);
return toSingleList(mapPredicate != null ? cb.and(mapPredicate, valuePredicate) : valuePredicate); return toSingleList(mapPredicate != null ? cb.and(mapPredicate, valuePredicate) : valuePredicate);
} }
private Predicate addOperatorPredicate(final ComparisonNode node, final Path<Object> fieldPath, 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 // only 'equal' and 'notEqual' can handle transformed value like
// enums. The JPA API cannot handle object types for greaterThan etc // 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 "==": case "==":
return getEqualToPredicate(transformedValue, fieldPath); return getEqualToPredicate(transformedValue, fieldPath);
case "!=": case "!=":
return getNotEqualToPredicate(transformedValue, fieldPath, finalProperty, enumField); return getNotEqualToPredicate(transformedValue, fieldPath, rsqlField);
case "=gt=": case "=gt=":
return cb.greaterThan(pathOfString(fieldPath), value); return cb.greaterThan(pathOfString(fieldPath), value);
case "=ge=": case "=ge=":
@@ -349,7 +346,7 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & FieldNameProvider, T> exten
case "=in=": case "=in=":
return in(pathOfString(fieldPath), transformedValues); return in(pathOfString(fieldPath), transformedValues);
case "=out=": case "=out=":
return getOutPredicate(transformedValues, finalProperty, enumField, fieldPath); return getOutPredicate(transformedValues, rsqlField, fieldPath);
default: default:
throw new RSQLParameterSyntaxException( throw new RSQLParameterSyntaxException(
"operator symbol {" + operator + "} is either not supported or not implemented"); "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( private Predicate getOutPredicate(
final List<Object> transformedValues, final String finalProperty, final List<Object> transformedValues,
final A enumField, final Path<Object> fieldPath) { final RsqlField rsqlField, final Path<Object> fieldPath) {
final String[] fieldNames = enumField.getSubAttributes(finalProperty); final String[] fieldNames = rsqlField.getSubAttributes();
if (isSimpleField(fieldNames, enumField.isMap())) { if (isSimpleField(fieldNames, rsqlField.getEnumValue().isMap())) {
final Path<String> pathOfString = pathOfString(fieldPath); final Path<String> pathOfString = pathOfString(fieldPath);
return cb.or(cb.isNull(pathOfString), cb.not(in(pathOfString, transformedValues))); return cb.or(cb.isNull(pathOfString), cb.not(in(pathOfString, transformedValues)));
} }
clearOuterJoinsIfNotNeeded(); 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) { 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, private Predicate getNotEqualToPredicate(final Object transformedValue, final Path<Object> fieldPath,
final String finalProperty, final A enumField) { final RsqlField rsqlField) {
if (transformedValue == null) { if (transformedValue == null) {
return cb.isNotNull(pathOfString(fieldPath)); 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), "")); 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 if (isPattern(transformedValueStr)) { // a pattern, use like
return cb.or(cb.isNull(pathOfString(fieldPath)), notLike(pathOfString(fieldPath), toSQL(transformedValueStr))); return cb.or(cb.isNull(pathOfString(fieldPath)), notLike(pathOfString(fieldPath), toSQL(transformedValueStr)));
} else { } else {
@@ -446,9 +443,9 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & FieldNameProvider, T> exten
clearOuterJoinsIfNotNeeded(); clearOuterJoinsIfNotNeeded();
if (isPattern(transformedValueStr)) { // a pattern, use like 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 { } 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 jakarta.persistence.metamodel.Type;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.math.NumberUtils; 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.FieldValueConverter;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException; import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException; 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 * @param <T> the entity type referenced by the root
*/ */
@Slf4j @Slf4j
public class JpaQueryRsqlVisitorG2<A extends Enum<A> & FieldNameProvider, T> public class JpaQueryRsqlVisitorG2<A extends Enum<A> & RsqlQueryField, T>
extends AbstractFieldNameRSQLVisitor<A> implements RSQLVisitor<List<Predicate>, String> { extends AbstractRSQLVisitor<A> implements RSQLVisitor<List<Predicate>, String> {
public static final Character LIKE_WILDCARD = '*'; public static final Character LIKE_WILDCARD = '*';
private static final char ESCAPE_CHAR = '\\'; private static final char ESCAPE_CHAR = '\\';
@@ -110,31 +110,30 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & FieldNameProvider, T>
@Override @Override
public List<Predicate> visit(final ComparisonNode node, final String param) { public List<Predicate> visit(final ComparisonNode node, final String param) {
final A fieldName = getFieldEnumByName(node); final RsqlField rsqlField = getRsqlField(node);
final String finalProperty = getAndValidatePropertyFieldName(fieldName, node);
final List<String> values = node.getArguments(); final List<String> values = node.getArguments();
final List<Object> transformedValues = new ArrayList<>(); 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) { 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); 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 Path<Object> fieldPath,
final List<String> values, final List<Object> transformedValues) { final List<String> values, final List<Object> transformedValues) {
// if lookup is available, replace macros ... // if lookup is available, replace macros ...
final String value = virtualPropertyReplacer == null ? values.get(0) : virtualPropertyReplacer.replace(values.get(0)); final String value = virtualPropertyReplacer == null ? values.get(0) : virtualPropertyReplacer.replace(values.get(0));
final Predicate mapPredicate = mapToMapPredicate(node, enumField, fieldPath); final Predicate mapPredicate = mapToMapPredicate(node, rsqlField.getEnumValue(), fieldPath);
final Predicate valuePredicate = addOperatorPredicate(node, enumField, finalProperty, final Predicate valuePredicate = addOperatorPredicate(node, rsqlField,
getValueFieldPath(enumField, fieldPath), transformedValues, value); getValueFieldPath(rsqlField.getEnumValue(), fieldPath), transformedValues, value);
return Collections.singletonList(mapPredicate != null ? cb.and(mapPredicate, valuePredicate) : valuePredicate); 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!")); "SimpleImmutableEntry are allowed. Neither of those could be found!"));
return equal(fieldPath.get(keyFieldName), keyValue); 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) { final Path<Object> fieldPath, final List<Object> transformedValues, final String value) {
// only 'equal' and 'notEqual' can handle transformed value like enums. // only 'equal' and 'notEqual' can handle transformed value like enums.
// The JPA API cannot handle object types for greaterThan etc. methods. // 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(); final String operator = node.getOperator().getSymbol();
return switch (operator) { return switch (operator) {
case "==" -> getEqualToPredicate(fieldPath, transformedValue); case "==" -> getEqualToPredicate(fieldPath, transformedValue);
case "!=" -> getNotEqualToPredicate(enumField, finalProperty, fieldPath, transformedValue); case "!=" -> getNotEqualToPredicate(rsqlField, fieldPath, transformedValue);
case "=gt=" -> cb.greaterThan(pathOfString(fieldPath), value); case "=gt=" -> cb.greaterThan(pathOfString(fieldPath), value);
case "=ge=" -> cb.greaterThanOrEqualTo(pathOfString(fieldPath), value); case "=ge=" -> cb.greaterThanOrEqualTo(pathOfString(fieldPath), value);
case "=lt=" -> cb.lessThan(pathOfString(fieldPath), value); case "=lt=" -> cb.lessThan(pathOfString(fieldPath), value);
case "=le=" -> cb.lessThanOrEqualTo(pathOfString(fieldPath), value); case "=le=" -> cb.lessThanOrEqualTo(pathOfString(fieldPath), value);
case "=in=" -> in(pathOfString(fieldPath), transformedValues); case "=in=" -> in(pathOfString(fieldPath), transformedValues);
case "=out=" -> getOutPredicate(enumField, finalProperty, fieldPath, transformedValues); case "=out=" -> getOutPredicate(rsqlField, fieldPath, transformedValues);
default -> throw new RSQLParameterSyntaxException( default -> throw new RSQLParameterSyntaxException(
"Operator symbol {" + operator + "} is either not supported or not implemented"); "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); 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) { final Path<Object> fieldPath, final Object transformedValue) {
if (transformedValue == null) { if (transformedValue == null) {
return cb.isNotNull(fieldPath); 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), "")); 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 if (isPattern(transformedValueStr)) { // a pattern, use like
return cb.or(cb.isNull(fieldPath), notLike(pathOfString(fieldPath), toSQL(transformedValueStr))); return cb.or(cb.isNull(fieldPath), notLike(pathOfString(fieldPath), toSQL(transformedValueStr)));
} else { } else {
@@ -226,7 +225,7 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & FieldNameProvider, T>
} }
clearJoinsIfNotNeeded(); clearJoinsIfNotNeeded();
return toNotExistsSubQueryPredicate(enumField, fieldNames, expressionToCompare -> return toNotExistsSubQueryPredicate(rsqlField, expressionToCompare ->
isPattern(transformedValueStr) ? // a pattern, use like isPattern(transformedValueStr) ? // a pattern, use like
like(expressionToCompare, toSQL(transformedValueStr)) : like(expressionToCompare, toSQL(transformedValueStr)) :
equal(expressionToCompare, transformedValueStr)); equal(expressionToCompare, transformedValueStr));
@@ -235,23 +234,22 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & FieldNameProvider, T>
return toNullOrNotEqualPredicate(fieldPath, transformedValue); 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 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))); return cb.or(cb.isNull(fieldPath), cb.not(in(pathOfString(fieldPath), transformedValues)));
} }
clearJoinsIfNotNeeded(); clearJoinsIfNotNeeded();
return toNotExistsSubQueryPredicate(enumField, fieldNames, return toNotExistsSubQueryPredicate(rsqlField, expressionToCompare -> in(expressionToCompare, transformedValues));
expressionToCompare -> in(expressionToCompare, transformedValues));
} }
private Path<Object> getFieldPath( private Path<Object> getFieldPath(final Root<?> root, final RsqlField rsqlField) {
final Root<?> root, final String[] split, final boolean isMapKeyField) { final String[] split = rsqlField.getSubAttributes();
Path<Object> fieldPath = null; 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]; final String fieldNameSplit = split[i];
fieldPath = fieldPath == null ? getPath(root, fieldNameSplit) : fieldPath.get(fieldNameSplit); 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" }) @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 Class<?> javaType = root.getJavaType();
final Subquery<?> subquery = query.subquery(javaType); final Subquery<?> subquery = query.subquery(javaType);
final Root subqueryRoot = subquery.from(javaType); final Root subqueryRoot = subquery.from(javaType);
final Predicate equalPredicate = cb.equal(root.get(enumField.identifierFieldName()), final Predicate equalPredicate = cb.equal(root.get(rsqlField.getEnumValue().identifierFieldName()),
subqueryRoot.get(enumField.identifierFieldName())); subqueryRoot.get(rsqlField.getEnumValue().identifierFieldName()));
final Expression<String> expressionToCompare = getExpressionToCompare(enumField, final Expression<String> expressionToCompare = getExpressionToCompare(rsqlField.getEnumValue(),
getFieldPath(subqueryRoot, fieldNames, enumField.isMap())); getFieldPath(subqueryRoot, rsqlField));
final Predicate subQueryPredicate = subQueryPredicateProvider.apply(expressionToCompare); final Predicate subQueryPredicate = subQueryPredicateProvider.apply(expressionToCompare);
subquery.select(subqueryRoot).where(cb.and(equalPredicate, subQueryPredicate)); subquery.select(subqueryRoot).where(cb.and(equalPredicate, subQueryPredicate));
return cb.not(cb.exists(subquery)); return cb.not(cb.exists(subquery));

View File

@@ -22,7 +22,7 @@ import lombok.AccessLevel;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.text.StrLookup; 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.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException; import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.rsql.RsqlConfigHolder; import org.eclipse.hawkbit.repository.rsql.RsqlConfigHolder;
@@ -82,7 +82,7 @@ public final class RSQLUtility {
* given RSQL query. * given RSQL query.
* *
* @param rsql the rsql query to be parsed * @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 virtualPropertyReplacer holds the logic how the known macros have to be resolved; may be <code>null</code>
* @param database database in use * @param database database in use
* *
@@ -91,7 +91,7 @@ public final class RSQLUtility {
* given {@code fieldNameProvider} * given {@code fieldNameProvider}
* @throws RSQLParameterSyntaxException if the RSQL syntax is wrong * @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 String rsql, final Class<A> fieldNameProvider,
final VirtualPropertyReplacer virtualPropertyReplacer, final Database database) { final VirtualPropertyReplacer virtualPropertyReplacer, final Database database) {
return new RSQLSpecification<>(rsql, fieldNameProvider, virtualPropertyReplacer, database); return new RSQLSpecification<>(rsql, fieldNameProvider, virtualPropertyReplacer, database);
@@ -106,7 +106,7 @@ public final class RSQLUtility {
* @throws RSQLParserException if RSQL syntax is invalid * @throws RSQLParserException if RSQL syntax is invalid
* @throws RSQLParameterUnsupportedFieldException if RSQL key is not allowed * @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 String rsql, final Class<A> fieldNameProvider) {
final RSQLVisitor<Void, String> visitor = final RSQLVisitor<Void, String> visitor =
RsqlConfigHolder.getInstance().getRsqlVisitorFactory().validationRsqlVisitor(fieldNameProvider); 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 @Serial
private static final long serialVersionUID = 1L; 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 " + "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.") + "IN_SYNC status and installed DS is set to the assigned DS entry.")
void assignDistributionSetAndAddFinishedActionStatus() { 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 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"); 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.CriteriaQuery;
import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root; 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.RsqlConfigHolder;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer; import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.persistence.config.PersistenceUnitProperties; import org.eclipse.persistence.config.PersistenceUnitProperties;
@@ -39,11 +39,11 @@ public class RSQLToSQL {
this.entityManager = entityManager; 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(); 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 CriteriaQuery<T> query = createQuery(domainClass, fieldsClass, rsql, legacyRsqlVisitor);
final TypedQuery<?> typedQuery = entityManager.createQuery(query); final TypedQuery<?> typedQuery = entityManager.createQuery(query);
// executes the query - otherwise the SQL string is not generated // executes the query - otherwise the SQL string is not generated
@@ -52,7 +52,7 @@ public class RSQLToSQL {
return typedQuery.unwrap(JpaQuery.class).getDatabaseQuery(); 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 CriteriaQuery<T> query = entityManager.getCriteriaBuilder().createQuery(domainClass);
final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
return query.where( 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 String rsql,
final Class<A> fieldsClass, final VirtualPropertyReplacer virtualPropertyReplacer, final Class<A> fieldsClass, final VirtualPropertyReplacer virtualPropertyReplacer,
final Root<T> root, final CriteriaQuery<?> query, final CriteriaBuilder cb, 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.EntityManager;
import jakarta.persistence.PersistenceContext; 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.TargetFields;
import org.eclipse.hawkbit.repository.jpa.RepositoryApplicationConfiguration; import org.eclipse.hawkbit.repository.jpa.RepositoryApplicationConfiguration;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; 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)"); 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(rsql);
System.out.println("\tlegacy:\n" + System.out.println("\tlegacy:\n" +
"\t\t" + rsqlToSQL.toSQL(domainClass, fieldsClass, rsql, true)); "\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.SingularAttribute;
import jakarta.persistence.metamodel.Type; import jakarta.persistence.metamodel.Type;
import org.eclipse.hawkbit.repository.DistributionSetFields; 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.SoftwareModuleFields;
import org.eclipse.hawkbit.repository.TargetFields; import org.eclipse.hawkbit.repository.TargetFields;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
@@ -554,7 +554,7 @@ public class RSQLUtilityTest {
return (Path<Y>) path; return (Path<Y>) path;
} }
private enum TestFieldEnum implements FieldNameProvider { private enum TestFieldEnum implements RsqlQueryField {
TESTFIELD("testfield"), TESTFIELD_WITH_SUB_ENTITIES("testfieldWithSubEntities", "subentity11", "subentity22"); TESTFIELD("testfield"), TESTFIELD_WITH_SUB_ENTITIES("testfieldWithSubEntities", "subentity11", "subentity22");
private final String fieldName; private final String fieldName;
@@ -570,7 +570,7 @@ public class RSQLUtilityTest {
} }
@Override @Override
public String getFieldName() { public String getJpaEntityFieldName() {
return this.fieldName; return this.fieldName;
} }

View File

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

View File

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

View File

@@ -155,7 +155,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
assertThat(actions).hasSize(2); assertThat(actions).hasSize(2);
updateActionStatus(actions.get(0), Status.FINISHED, null, "test"); 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 Action action = deploymentManagement.findActionsByTarget(knownTargetId, pageRequest).getContent().get(0);
final ActionStatus status = deploymentManagement.findActionStatusByAction(PAGE, action.getId()).getContent() 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.List;
import java.util.StringTokenizer; 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.SortParameterSyntaxErrorException;
import org.eclipse.hawkbit.rest.exception.SortParameterUnsupportedDirectionException; import org.eclipse.hawkbit.rest.exception.SortParameterUnsupportedDirectionException;
import org.eclipse.hawkbit.rest.exception.SortParameterUnsupportedFieldException; import org.eclipse.hawkbit.rest.exception.SortParameterUnsupportedFieldException;
@@ -53,11 +53,11 @@ public final class SortUtility {
* should be related to. * should be related to.
* @param <T> * @param <T>
* the type of the enumeration which must be derived from * the type of the enumeration which must be derived from
* {@link FieldNameProvider} * {@link RsqlQueryField}
* @param sortString * @param sortString
* the string representation of the query parameters. Might be * the string representation of the query parameters. Might be
* {@code null} or an empty string. * {@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 * {@link Direction} for them as a tuple. Never {@code null}. In
* case of no sorting parameters an empty map will be returned. * case of no sorting parameters an empty map will be returned.
* @throws SortParameterSyntaxErrorException * @throws SortParameterSyntaxErrorException
@@ -67,7 +67,7 @@ public final class SortUtility {
* @throws SortParameterUnsupportedDirectionException * @throws SortParameterUnsupportedDirectionException
* if the given direction is not "ASC" or "DESC" * 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 String sortString) throws SortParameterSyntaxErrorException {
final List<Order> parsedSortings = new ArrayList<>(); final List<Order> parsedSortings = new ArrayList<>();
// scan the sort tuples e.g. field:direction // scan the sort tuples e.g. field:direction
@@ -84,7 +84,7 @@ public final class SortUtility {
final T identifier = getAttributeIdentifierByName(enumType, fieldName); final T identifier = getAttributeIdentifierByName(enumType, fieldName);
final Direction sortDirection = getDirection(sortDirectionStr); final Direction sortDirection = getDirection(sortDirectionStr);
parsedSortings.add(new Order(sortDirection, identifier.getFieldName())); parsedSortings.add(new Order(sortDirection, identifier.getJpaEntityFieldName()));
} else { } else {
throw new SortParameterSyntaxErrorException(); throw new SortParameterSyntaxErrorException();
} }
@@ -103,12 +103,12 @@ public final class SortUtility {
* the name of the enum * the name of the enum
* @param <T> * @param <T>
* the type of the enumeration which must be derived from * the type of the enumeration which must be derived from
* {@link FieldNameProvider} * {@link RsqlQueryField}
* @return the corresponding enum * @return the corresponding enum
* @throws SortParameterUnsupportedFieldException * @throws SortParameterUnsupportedFieldException
* if there is no matching enum for the specified name * 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) { final String name) {
try { try {
return Enum.valueOf(enumType, name.toUpperCase()); return Enum.valueOf(enumType, name.toUpperCase());