Add some id based searches and software module search by type (#2681)
Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -21,7 +21,10 @@ import lombok.Getter;
|
|||||||
public enum DistributionSetFields implements RsqlQueryField {
|
public enum DistributionSetFields implements RsqlQueryField {
|
||||||
|
|
||||||
ID("id"),
|
ID("id"),
|
||||||
TYPE("type", "key", "name"),
|
TYPE("type",
|
||||||
|
DistributionSetTypeFields.ID.getJpaEntityFieldName(),
|
||||||
|
DistributionSetTypeFields.KEY.getJpaEntityFieldName(),
|
||||||
|
DistributionSetTypeFields.NAME.getJpaEntityFieldName()),
|
||||||
NAME("name"),
|
NAME("name"),
|
||||||
DESCRIPTION("description"),
|
DESCRIPTION("description"),
|
||||||
CREATEDAT("createdAt"),
|
CREATEDAT("createdAt"),
|
||||||
|
|||||||
@@ -20,7 +20,10 @@ import lombok.Getter;
|
|||||||
public enum SoftwareModuleFields implements RsqlQueryField {
|
public enum SoftwareModuleFields implements RsqlQueryField {
|
||||||
|
|
||||||
ID("id"),
|
ID("id"),
|
||||||
TYPE("type", "key"),
|
TYPE("type",
|
||||||
|
SoftwareModuleTypeFields.ID.getJpaEntityFieldName(),
|
||||||
|
SoftwareModuleTypeFields.KEY.getJpaEntityFieldName(),
|
||||||
|
SoftwareModuleTypeFields.NAME.getJpaEntityFieldName()),
|
||||||
NAME("name"),
|
NAME("name"),
|
||||||
DESCRIPTION("description"),
|
DESCRIPTION("description"),
|
||||||
VERSION("version"),
|
VERSION("version"),
|
||||||
|
|||||||
@@ -32,16 +32,17 @@ public enum TargetFields implements RsqlQueryField {
|
|||||||
IPADDRESS("address"),
|
IPADDRESS("address"),
|
||||||
ATTRIBUTE("controllerAttributes"),
|
ATTRIBUTE("controllerAttributes"),
|
||||||
GROUP("group"),
|
GROUP("group"),
|
||||||
ASSIGNEDDS(
|
ASSIGNEDDS("assignedDistributionSet",
|
||||||
"assignedDistributionSet",
|
|
||||||
DistributionSetFields.NAME.getJpaEntityFieldName(), DistributionSetFields.VERSION.getJpaEntityFieldName()),
|
DistributionSetFields.NAME.getJpaEntityFieldName(), DistributionSetFields.VERSION.getJpaEntityFieldName()),
|
||||||
INSTALLEDDS(
|
INSTALLEDDS("installedDistributionSet",
|
||||||
"installedDistributionSet",
|
|
||||||
DistributionSetFields.NAME.getJpaEntityFieldName(), DistributionSetFields.VERSION.getJpaEntityFieldName()),
|
DistributionSetFields.NAME.getJpaEntityFieldName(), DistributionSetFields.VERSION.getJpaEntityFieldName()),
|
||||||
TAG("tags", TagFields.NAME.getJpaEntityFieldName()),
|
TAG("tags", TagFields.NAME.getJpaEntityFieldName()),
|
||||||
LASTCONTROLLERREQUESTAT("lastTargetQuery"),
|
LASTCONTROLLERREQUESTAT("lastTargetQuery"),
|
||||||
METADATA("metadata"),
|
METADATA("metadata"),
|
||||||
TARGETTYPE("targetType", TargetTypeFields.KEY.getJpaEntityFieldName(), TargetTypeFields.NAME.getJpaEntityFieldName());
|
TARGETTYPE("targetType",
|
||||||
|
TargetTypeFields.ID.getJpaEntityFieldName(),
|
||||||
|
TargetTypeFields.KEY.getJpaEntityFieldName(),
|
||||||
|
TargetTypeFields.NAME.getJpaEntityFieldName());
|
||||||
|
|
||||||
private final String jpaEntityFieldName;
|
private final String jpaEntityFieldName;
|
||||||
private final List<String> subEntityAttributes;
|
private final List<String> subEntityAttributes;
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.NOT
|
|||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.UnaryOperator;
|
import java.util.function.UnaryOperator;
|
||||||
@@ -41,8 +42,10 @@ import cz.jirutka.rsql.parser.ast.RSQLVisitor;
|
|||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.eclipse.hawkbit.repository.DistributionSetFields;
|
||||||
import org.eclipse.hawkbit.repository.FieldValueConverter;
|
import org.eclipse.hawkbit.repository.FieldValueConverter;
|
||||||
import org.eclipse.hawkbit.repository.RsqlQueryField;
|
import org.eclipse.hawkbit.repository.RsqlQueryField;
|
||||||
|
import org.eclipse.hawkbit.repository.SoftwareModuleFields;
|
||||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
||||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
||||||
import org.eclipse.hawkbit.repository.jpa.ql.Node;
|
import org.eclipse.hawkbit.repository.jpa.ql.Node;
|
||||||
@@ -68,14 +71,14 @@ public class RsqlParser {
|
|||||||
public static final ComparisonOperator IS = new ComparisonOperator("=is=", "=eq=");
|
public static final ComparisonOperator IS = new ComparisonOperator("=is=", "=eq=");
|
||||||
public static final ComparisonOperator NOT = new ComparisonOperator("=not=", "=ne=");
|
public static final ComparisonOperator NOT = new ComparisonOperator("=not=", "=ne=");
|
||||||
|
|
||||||
private static final RSQLParser PARSER;
|
private static final RSQLParser RSQL_PARSER;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
final Set<ComparisonOperator> operators = new HashSet<>(RSQLOperators.defaultOperators());
|
final Set<ComparisonOperator> operators = new HashSet<>(RSQLOperators.defaultOperators());
|
||||||
// == and != alternatives just treating "null" string as null not as a "null"
|
// == and != alternatives just treating "null" string as null not as a "null"
|
||||||
operators.add(IS);
|
operators.add(IS);
|
||||||
operators.add(NOT);
|
operators.add(NOT);
|
||||||
PARSER = new RSQLParser(operators);
|
RSQL_PARSER = new RSQLParser(operators);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Node parse(final String rsql) {
|
public static Node parse(final String rsql) {
|
||||||
@@ -88,7 +91,7 @@ public class RsqlParser {
|
|||||||
|
|
||||||
private static Node parse(final String rsql, final Function<String, Key> keyResolver) {
|
private static Node parse(final String rsql, final Function<String, Key> keyResolver) {
|
||||||
try {
|
try {
|
||||||
return PARSER
|
return RSQL_PARSER
|
||||||
.parse(rsql)
|
.parse(rsql)
|
||||||
.accept(new RsqlVisitor(keyResolver));
|
.accept(new RsqlVisitor(keyResolver));
|
||||||
} catch (final RSQLParserException e) {
|
} catch (final RSQLParserException e) {
|
||||||
@@ -121,6 +124,12 @@ public class RsqlParser {
|
|||||||
} else if (enumValue.getSubEntityAttributes().size() == 1) {
|
} else if (enumValue.getSubEntityAttributes().size() == 1) {
|
||||||
// single sub attribute - so, treat it as a default
|
// single sub attribute - so, treat it as a default
|
||||||
attribute = enumValue.getJpaEntityFieldName() + SUB_ATTRIBUTE_SEPARATOR + enumValue.getSubEntityAttributes().get(0);
|
attribute = enumValue.getJpaEntityFieldName() + SUB_ATTRIBUTE_SEPARATOR + enumValue.getSubEntityAttributes().get(0);
|
||||||
|
} else if (RsqlUtility.SM_DS_SEARCH_BY_TYPE_BACKWARD_COMPATIBILITY &&
|
||||||
|
"type".equalsIgnoreCase(enumValue.getJpaEntityFieldName()) &&
|
||||||
|
(Objects.equals(rsqlQueryFieldType, SoftwareModuleFields.class) ||
|
||||||
|
Objects.equals(rsqlQueryFieldType, DistributionSetFields.class))) {
|
||||||
|
// backward compatibility - type for SoftwareModuleTypeFields && DistributionSetFields means type.key
|
||||||
|
attribute = enumValue.getJpaEntityFieldName() + SUB_ATTRIBUTE_SEPARATOR + "key";
|
||||||
} else {
|
} else {
|
||||||
throw new RSQLParameterUnsupportedFieldException(
|
throw new RSQLParameterUnsupportedFieldException(
|
||||||
String.format(
|
String.format(
|
||||||
@@ -148,7 +157,9 @@ public class RsqlParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Key(attribute, RsqlVisitor.valueConverter(enumValue));
|
return new
|
||||||
|
|
||||||
|
Key(attribute, RsqlVisitor.valueConverter(enumValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
private record Key(String path, UnaryOperator<Object> converter) {}
|
private record Key(String path, UnaryOperator<Object> converter) {}
|
||||||
|
|||||||
@@ -71,6 +71,11 @@ import org.springframework.orm.jpa.vendor.Database;
|
|||||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
public class RsqlUtility {
|
public class RsqlUtility {
|
||||||
|
|
||||||
|
// to be removed in future releases, use type.key instead of type for software module and distribution set RSQL queries
|
||||||
|
@Deprecated(forRemoval = true, since = "0.10.0")
|
||||||
|
public static final boolean SM_DS_SEARCH_BY_TYPE_BACKWARD_COMPATIBILITY =
|
||||||
|
"true".equalsIgnoreCase(System.getProperty("hawkbit.rsql.sm-ds-search-by-type.backward-compatibility", "true"));
|
||||||
|
|
||||||
private static final RsqlUtility SINGLETON = new RsqlUtility();
|
private static final RsqlUtility SINGLETON = new RsqlUtility();
|
||||||
|
|
||||||
public enum RsqlToSpecBuilder {
|
public enum RsqlToSpecBuilder {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import java.util.Arrays;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@@ -23,9 +24,12 @@ import cz.jirutka.rsql.parser.ast.ComparisonOperator;
|
|||||||
import cz.jirutka.rsql.parser.ast.RSQLOperators;
|
import cz.jirutka.rsql.parser.ast.RSQLOperators;
|
||||||
import lombok.Value;
|
import lombok.Value;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.eclipse.hawkbit.repository.DistributionSetFields;
|
||||||
import org.eclipse.hawkbit.repository.RsqlQueryField;
|
import org.eclipse.hawkbit.repository.RsqlQueryField;
|
||||||
|
import org.eclipse.hawkbit.repository.SoftwareModuleFields;
|
||||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
||||||
import org.eclipse.hawkbit.repository.jpa.ql.SpecificationBuilder;
|
import org.eclipse.hawkbit.repository.jpa.ql.SpecificationBuilder;
|
||||||
|
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlUtility;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -72,6 +76,12 @@ public abstract class AbstractRSQLVisitor<A extends Enum<A> & RsqlQueryField> {
|
|||||||
if (!enumValue.getSubEntityAttributes().isEmpty() && split.length < 2) {
|
if (!enumValue.getSubEntityAttributes().isEmpty() && split.length < 2) {
|
||||||
if (enumValue.getSubEntityAttributes().size() == 1) { // single sub attribute - so add is as a default
|
if (enumValue.getSubEntityAttributes().size() == 1) { // single sub attribute - so add is as a default
|
||||||
split = new String[] { split[0], enumValue.getSubEntityAttributes().get(0) };
|
split = new String[] { split[0], enumValue.getSubEntityAttributes().get(0) };
|
||||||
|
} else if (RsqlUtility.SM_DS_SEARCH_BY_TYPE_BACKWARD_COMPATIBILITY &&
|
||||||
|
"type".equals(node.getSelector()) &&
|
||||||
|
(Objects.equals(rsqlQueryFieldType, SoftwareModuleFields.class) ||
|
||||||
|
Objects.equals(rsqlQueryFieldType, DistributionSetFields.class))) {
|
||||||
|
// backward compatibility - type for DistributionSetFields means type.key
|
||||||
|
split = new String[] { split[0], "key" };
|
||||||
} else {
|
} else {
|
||||||
throw createRSQLParameterUnsupportedException(node, null);
|
throw createRSQLParameterUnsupportedException(node, null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,6 +135,16 @@ class RsqlSoftwareModuleFieldTest extends AbstractJpaIntegrationTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void testFilterByType() {
|
void testFilterByType() {
|
||||||
|
assertRSQLQuery(SoftwareModuleFields.TYPE.name() + ".key==" + TestdataFactory.SM_TYPE_APP, 2);
|
||||||
|
assertRSQLQuery(SoftwareModuleFields.TYPE.name() + ".key!=" + TestdataFactory.SM_TYPE_APP, 4);
|
||||||
|
assertRSQLQuery(SoftwareModuleFields.TYPE.name() + ".key==noExist*", 0);
|
||||||
|
assertRSQLQuery(SoftwareModuleFields.TYPE.name() + ".key=in=(" + TestdataFactory.SM_TYPE_APP + ")", 2);
|
||||||
|
assertRSQLQuery(SoftwareModuleFields.TYPE.name() + ".key=out=(" + TestdataFactory.SM_TYPE_APP + ")", 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFilterByTypeBackwardCompatibility() {
|
||||||
|
assertThat(RsqlUtility.SM_DS_SEARCH_BY_TYPE_BACKWARD_COMPATIBILITY).isTrue();
|
||||||
assertRSQLQuery(SoftwareModuleFields.TYPE.name() + "==" + TestdataFactory.SM_TYPE_APP, 2);
|
assertRSQLQuery(SoftwareModuleFields.TYPE.name() + "==" + TestdataFactory.SM_TYPE_APP, 2);
|
||||||
assertRSQLQuery(SoftwareModuleFields.TYPE.name() + "!=" + TestdataFactory.SM_TYPE_APP, 4);
|
assertRSQLQuery(SoftwareModuleFields.TYPE.name() + "!=" + TestdataFactory.SM_TYPE_APP, 4);
|
||||||
assertRSQLQuery(SoftwareModuleFields.TYPE.name() + "==noExist*", 0);
|
assertRSQLQuery(SoftwareModuleFields.TYPE.name() + "==noExist*", 0);
|
||||||
@@ -142,6 +152,18 @@ class RsqlSoftwareModuleFieldTest extends AbstractJpaIntegrationTest {
|
|||||||
assertRSQLQuery(SoftwareModuleFields.TYPE.name() + "=out=(" + TestdataFactory.SM_TYPE_APP + ")", 4);
|
assertRSQLQuery(SoftwareModuleFields.TYPE.name() + "=out=(" + TestdataFactory.SM_TYPE_APP + ")", 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test filter software module by type key
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void testFilterByName() {
|
||||||
|
assertRSQLQuery(SoftwareModuleFields.TYPE.name() + ".name==" + appType.getName(), 2);
|
||||||
|
assertRSQLQuery(SoftwareModuleFields.TYPE.name() + ".name!=" + appType.getName(), 4);
|
||||||
|
assertRSQLQuery(SoftwareModuleFields.TYPE.name() + ".name==noExist*", 0);
|
||||||
|
assertRSQLQuery(SoftwareModuleFields.TYPE.name() + ".name=in=(" + appType.getName() + ")", 2);
|
||||||
|
assertRSQLQuery(SoftwareModuleFields.TYPE.name() + ".name=out=(" + appType.getName() + ")", 4);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test filter software module by metadata
|
* Test filter software module by metadata
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -396,6 +396,16 @@ class RsqlTargetFieldTest extends AbstractJpaIntegrationTest {
|
|||||||
.isThrownBy(() -> RsqlUtility.getInstance().validateRsqlFor("wrongfield == abcd", TargetFields.class, JpaTarget.class));
|
.isThrownBy(() -> RsqlUtility.getInstance().validateRsqlFor("wrongfield == abcd", TargetFields.class, JpaTarget.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test filter by target type key
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void shouldFilterTargetsByTypeId() {
|
||||||
|
assertRSQLQuery("targettype." + TargetTypeFields.ID.name() + "==" + targetType1.getId(), 1);
|
||||||
|
assertRSQLQuery("targettype." + TargetTypeFields.ID.name() + "!=" + targetType2.getId(), 4);
|
||||||
|
assertRSQLQuery("targettype." + TargetTypeFields.ID.name() + "==-1", 0);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test filter by target type key
|
* Test filter by target type key
|
||||||
*/
|
*/
|
||||||
@@ -424,7 +434,7 @@ class RsqlTargetFieldTest extends AbstractJpaIntegrationTest {
|
|||||||
@Test
|
@Test
|
||||||
void shouldFilterTargetsByTypeIdAndDescription() {
|
void shouldFilterTargetsByTypeIdAndDescription() {
|
||||||
assertThatExceptionOfType(RSQLParameterUnsupportedFieldException.class)
|
assertThatExceptionOfType(RSQLParameterUnsupportedFieldException.class)
|
||||||
.isThrownBy(() -> assertRSQLQuery("targettype.ID==1", 0));
|
.isThrownBy(() -> assertRSQLQuery("targettype.IDD==1", 0));
|
||||||
assertThatExceptionOfType(RSQLParameterUnsupportedFieldException.class)
|
assertThatExceptionOfType(RSQLParameterUnsupportedFieldException.class)
|
||||||
.isThrownBy(() -> assertRSQLQuery("targettype.description==Description", 0));
|
.isThrownBy(() -> assertRSQLQuery("targettype.description==Description", 0));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user