diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/JpaQueryRsqlVisitor.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/JpaQueryRsqlVisitor.java index 30269c6b4..7c46dd70d 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/JpaQueryRsqlVisitor.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/JpaQueryRsqlVisitor.java @@ -21,7 +21,6 @@ import java.util.Set; import java.util.function.BiFunction; import java.util.function.Function; import java.util.stream.Collectors; - import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Expression; @@ -35,6 +34,13 @@ import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import javax.persistence.criteria.Subquery; +import com.google.common.collect.Lists; +import cz.jirutka.rsql.parser.ast.AndNode; +import cz.jirutka.rsql.parser.ast.ComparisonNode; +import cz.jirutka.rsql.parser.ast.LogicalNode; +import cz.jirutka.rsql.parser.ast.Node; +import cz.jirutka.rsql.parser.ast.OrNode; +import cz.jirutka.rsql.parser.ast.RSQLVisitor; import org.apache.commons.lang3.math.NumberUtils; import org.eclipse.hawkbit.repository.FieldNameProvider; import org.eclipse.hawkbit.repository.FieldValueConverter; @@ -49,15 +55,6 @@ import org.springframework.orm.jpa.vendor.Database; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; -import com.google.common.collect.Lists; - -import cz.jirutka.rsql.parser.ast.AndNode; -import cz.jirutka.rsql.parser.ast.ComparisonNode; -import cz.jirutka.rsql.parser.ast.LogicalNode; -import cz.jirutka.rsql.parser.ast.Node; -import cz.jirutka.rsql.parser.ast.OrNode; -import cz.jirutka.rsql.parser.ast.RSQLVisitor; - /** * An implementation of the {@link RSQLVisitor} to visit the parsed tokens and * build JPA where clauses. @@ -76,6 +73,7 @@ public class JpaQueryRsqlVisitor & FieldNameProvider, T> exten public static final Character LIKE_WILDCARD = '*'; private static final char ESCAPE_CHAR = '\\'; private static final List NO_JOINS_OPERATOR = Lists.newArrayList("!=", "=out="); + private static final String ESCAPE_CHAR_WITH_ASTERISK = ESCAPE_CHAR +"*"; private final Map>> joinsInLevel = new HashMap<>(3); @@ -571,8 +569,18 @@ public class JpaQueryRsqlVisitor & FieldNameProvider, T> exten } else { escaped = transformedValue.replace("%", ESCAPE_CHAR + "%").replace("_", ESCAPE_CHAR + "_"); } + return replaceIfRequired(escaped); + } - return escaped.replace(LIKE_WILDCARD, '%').toUpperCase(); + private String replaceIfRequired(final String escapedValue) { + final String finalizedValue; + if (escapedValue.contains(ESCAPE_CHAR_WITH_ASTERISK)) { + finalizedValue = escapedValue.replace(ESCAPE_CHAR_WITH_ASTERISK, "$").replace(LIKE_WILDCARD, '%') + .replace("$", ESCAPE_CHAR_WITH_ASTERISK); + } else { + finalizedValue = escapedValue.replace(LIKE_WILDCARD, '%'); + } + return finalizedValue.toUpperCase(); } @SuppressWarnings("unchecked") diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLSoftwareModuleFieldTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLSoftwareModuleFieldTest.java index e137bf68a..808c5ccd7 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLSoftwareModuleFieldTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLSoftwareModuleFieldTest.java @@ -39,7 +39,10 @@ public class RSQLSoftwareModuleFieldTest extends AbstractJpaIntegrationTest { softwareModuleManagement.create(entityFactory.softwareModule().create().type(runtimeType).name("oracle-jre") .version("1.7.2").description("aa")); softwareModuleManagement.create( - entityFactory.softwareModule().create().type(osType).name("poky").version("3.0.2").description("aa")); + entityFactory.softwareModule().create().type(osType).name("poki").version("3.0.2").description("aa")); + softwareModuleManagement + .create(entityFactory.softwareModule().create().type(osType).name("*§$%&/&%ÄÜ*Ö@").version("1.0.0") + .description("wildcard testing")); softwareModuleManagement .create(entityFactory.softwareModule().create().type(osType).name("noDesc").version("noDesc")); @@ -59,62 +62,74 @@ public class RSQLSoftwareModuleFieldTest extends AbstractJpaIntegrationTest { @Description("Test filter software module by id") public void testFilterByParameterId() { assertRSQLQuery(SoftwareModuleFields.ID.name() + "==" + ah.getId(), 1); - assertRSQLQuery(SoftwareModuleFields.ID.name() + "!=" + ah.getId(), 4); + assertRSQLQuery(SoftwareModuleFields.ID.name() + "!=" + ah.getId(), 5); assertRSQLQuery(SoftwareModuleFields.ID.name() + "==" + -1, 0); - assertRSQLQuery(SoftwareModuleFields.ID.name() + "!=" + -1, 5); + assertRSQLQuery(SoftwareModuleFields.ID.name() + "!=" + -1, 6); // Not supported for numbers if (Database.POSTGRESQL.equals(getDatabase())) { return; } - assertRSQLQuery(SoftwareModuleFields.ID.name() + "==*", 5); + assertRSQLQuery(SoftwareModuleFields.ID.name() + "==*", 6); assertRSQLQuery(SoftwareModuleFields.ID.name() + "==noexist*", 0); assertRSQLQuery(SoftwareModuleFields.ID.name() + "=in=(" + ah.getId() + ",1000000)", 1); - assertRSQLQuery(SoftwareModuleFields.ID.name() + "=out=(" + ah.getId() + ",1000000)", 4); + assertRSQLQuery(SoftwareModuleFields.ID.name() + "=out=(" + ah.getId() + ",1000000)", 5); } @Test @Description("Test filter software module by name") public void testFilterByParameterName() { assertRSQLQuery(SoftwareModuleFields.NAME.name() + "==agent-hub", 1); - assertRSQLQuery(SoftwareModuleFields.NAME.name() + "!=agent-hub", 4); + assertRSQLQuery(SoftwareModuleFields.NAME.name() + "!=agent-hub", 5); assertRSQLQuery(SoftwareModuleFields.NAME.name() + "==agent-hub*", 2); - assertRSQLQuery(SoftwareModuleFields.NAME.name() + "!=agent-hub*", 3); + assertRSQLQuery(SoftwareModuleFields.NAME.name() + "!=agent-hub*", 4); assertRSQLQuery(SoftwareModuleFields.NAME.name() + "==noExist*", 0); assertRSQLQuery(SoftwareModuleFields.NAME.name() + "=in=(agent-hub,notexist)", 1); - assertRSQLQuery(SoftwareModuleFields.NAME.name() + "=out=(agent-hub,notexist)", 4); + assertRSQLQuery(SoftwareModuleFields.NAME.name() + "=out=(agent-hub,notexist)", 5); + + //wildcard entries + assertRSQLQuery(SoftwareModuleFields.NAME.name() + "==*$*", 1); + assertRSQLQuery(SoftwareModuleFields.NAME.name() + "==*§*", 1); + assertRSQLQuery(SoftwareModuleFields.NAME.name() + "==*Ö*", 1); + assertRSQLQuery(SoftwareModuleFields.NAME.name() + "==*Ä*", 1); + assertRSQLQuery(SoftwareModuleFields.NAME.name() + "==*Ü*", 1); + assertRSQLQuery(SoftwareModuleFields.NAME.name() + "==*@*", 1); + assertRSQLQuery(SoftwareModuleFields.NAME.name() + "==*/*", 1); + assertRSQLQuery(SoftwareModuleFields.NAME.name() + "==*&*", 1); + assertRSQLQuery(SoftwareModuleFields.NAME.name() + "==***", 6); + assertRSQLQuery(SoftwareModuleFields.NAME.name() + "==*\\**", 1); } @Test @Description("Test filter software module by description") public void testFilterByParameterDescription() { assertRSQLQuery(SoftwareModuleFields.DESCRIPTION.name() + "==''", 1); - assertRSQLQuery(SoftwareModuleFields.DESCRIPTION.name() + "!=''", 4); + assertRSQLQuery(SoftwareModuleFields.DESCRIPTION.name() + "!=''", 5); assertRSQLQuery(SoftwareModuleFields.DESCRIPTION.name() + "==agent-hub", 1); - assertRSQLQuery(SoftwareModuleFields.DESCRIPTION.name() + "!=agent-hub", 4); + assertRSQLQuery(SoftwareModuleFields.DESCRIPTION.name() + "!=agent-hub", 5); assertRSQLQuery(SoftwareModuleFields.DESCRIPTION.name() + "==noExist*", 0); assertRSQLQuery(SoftwareModuleFields.DESCRIPTION.name() + "=in=(agent-hub,notexist)", 1); - assertRSQLQuery(SoftwareModuleFields.DESCRIPTION.name() + "=out=(agent-hub,notexist)", 4); + assertRSQLQuery(SoftwareModuleFields.DESCRIPTION.name() + "=out=(agent-hub,notexist)", 5); } @Test @Description("Test filter software module by version") public void testFilterByParameterVersion() { assertRSQLQuery(SoftwareModuleFields.VERSION.name() + "==1.0.1", 2); - assertRSQLQuery(SoftwareModuleFields.VERSION.name() + "!=v1.0", 5); + assertRSQLQuery(SoftwareModuleFields.VERSION.name() + "!=v1.0", 6); assertRSQLQuery(SoftwareModuleFields.VERSION.name() + "=in=(1.0.1,1.0.2)", 2); - assertRSQLQuery(SoftwareModuleFields.VERSION.name() + "=out=(1.0.1)", 3); + assertRSQLQuery(SoftwareModuleFields.VERSION.name() + "=out=(1.0.1)", 4); } @Test @Description("Test filter software module by type key") public void testFilterByType() { assertRSQLQuery(SoftwareModuleFields.TYPE.name() + "==" + TestdataFactory.SM_TYPE_APP, 2); - assertRSQLQuery(SoftwareModuleFields.TYPE.name() + "!=" + TestdataFactory.SM_TYPE_APP, 3); + assertRSQLQuery(SoftwareModuleFields.TYPE.name() + "!=" + TestdataFactory.SM_TYPE_APP, 4); assertRSQLQuery(SoftwareModuleFields.TYPE.name() + "==noExist*", 0); assertRSQLQuery(SoftwareModuleFields.TYPE.name() + "=in=(" + TestdataFactory.SM_TYPE_APP + ")", 2); - assertRSQLQuery(SoftwareModuleFields.TYPE.name() + "=out=(" + TestdataFactory.SM_TYPE_APP + ")", 3); + assertRSQLQuery(SoftwareModuleFields.TYPE.name() + "=out=(" + TestdataFactory.SM_TYPE_APP + ")", 4); } @Test @@ -131,10 +146,10 @@ public class RSQLSoftwareModuleFieldTest extends AbstractJpaIntegrationTest { } - private void assertRSQLQuery(final String rsqlParam, final long excpectedEntity) { + private void assertRSQLQuery(final String rsqlParam, final long expectedEntity) { final Page find = softwareModuleManagement.findByRsql(PageRequest.of(0, 100), rsqlParam); final long countAll = find.getTotalElements(); assertThat(find).isNotNull(); - assertThat(countAll).isEqualTo(excpectedEntity); + assertThat(countAll).isEqualTo(expectedEntity); } }