Improve building of SQL from an RSQL query (#1766)

* Improve building of SQL from an RSQL query

* ignore case behavior could be disabled
* like is used only when needed

Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>

* Inlining of some methods and unified IN build + fix case

Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>

* Implement more flexible ignore case configuration

Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>

---------

Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2024-07-15 13:04:47 +03:00
committed by GitHub
parent beeb2523e2
commit ff6d7a29f6
7 changed files with 253 additions and 226 deletions

View File

@@ -21,6 +21,7 @@ import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.data.domain.PageRequest;
@@ -43,34 +44,30 @@ public class RSQLActionFieldsTest extends AbstractJpaIntegrationTest {
final DistributionSet dsA = testdataFactory.createDistributionSet("daA");
target = (JpaTarget) targetManagement
.create(entityFactory.target().create().controllerId("targetId123").description("targetId123"));
action = new JpaAction();
action.setActionType(ActionType.SOFT);
action.setDistributionSet(dsA);
action.setTarget(target);
action.setStatus(Status.RUNNING);
action.setWeight(45);
action.setInitiatedBy(tenantAware.getCurrentUsername());
action = newJpaAction(dsA, false, null);
for (int i = 0; i < 10; i++) {
newJpaAction(dsA, i % 2 == 0, i % 2 == 0 ? "extRef" : "extRef2");
}
}
private @NotNull JpaAction newJpaAction(final DistributionSet dsA, final boolean active, final String extRef) {
final JpaAction newAction = new JpaAction();
newAction.setActionType(ActionType.SOFT);
newAction.setDistributionSet(dsA);
newAction.setActive(active);
newAction.setStatus(Status.RUNNING);
newAction.setTarget(target);
newAction.setWeight(45);
newAction.setInitiatedBy(tenantAware.getCurrentUsername());
if (extRef != null) {
newAction.setExternalRef(extRef);
}
actionRepository.save(newAction);
target.addAction(action);
actionRepository.save(action);
for (int i = 0; i < 10; i++) {
final JpaAction newAction = new JpaAction();
newAction.setActionType(ActionType.SOFT);
newAction.setDistributionSet(dsA);
newAction.setActive((i % 2) == 0);
newAction.setStatus(Status.RUNNING);
newAction.setTarget(target);
newAction.setWeight(45);
newAction.setInitiatedBy(tenantAware.getCurrentUsername());
if ((i % 2) == 0) {
newAction.setExternalRef("extRef");
} else {
newAction.setExternalRef("extRef2");
}
actionRepository.save(newAction);
target.addAction(newAction);
}
return newAction;
}
@Test
@@ -116,11 +113,11 @@ public class RSQLActionFieldsTest extends AbstractJpaIntegrationTest {
}
private void assertRSQLQuery(final String rsqlParam, final long expectedEntities) {
final Slice<Action> findEnitity = deploymentManagement.findActionsByTarget(rsqlParam, target.getControllerId(),
final Slice<Action> findEntity = deploymentManagement.findActionsByTarget(rsqlParam, target.getControllerId(),
PageRequest.of(0, 100));
final long countAllEntities = deploymentManagement.countActionsByTarget(rsqlParam, target.getControllerId());
assertThat(findEnitity).isNotNull();
assertThat(findEntity).isNotNull();
assertThat(findEntity.getContent().size()).isEqualTo(expectedEntities);
assertThat(countAllEntities).isEqualTo(expectedEntities);
}
}
}

View File

@@ -46,7 +46,7 @@ import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
import org.eclipse.hawkbit.repository.model.helper.SystemSecurityContextHolder;
import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagementHolder;
import org.eclipse.hawkbit.repository.rsql.RsqlVisitorFactory;
import org.eclipse.hawkbit.repository.rsql.RsqlVisitorFactoryHolder;
import org.eclipse.hawkbit.repository.rsql.RsqlConfigHolder;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyResolver;
import org.eclipse.hawkbit.security.SystemSecurityContext;
@@ -116,8 +116,8 @@ public class RSQLUtilityTest {
}
@Bean
RsqlVisitorFactoryHolder rsqlVisitorFactoryHolder() {
return RsqlVisitorFactoryHolder.getInstance();
RsqlConfigHolder rsqlVisitorFactoryHolder() {
return RsqlConfigHolder.getInstance();
}
}
@@ -284,12 +284,36 @@ public class RSQLUtilityTest {
}
@Test
public void correctRsqlBuildsSimpleNotLikePredicate() {
public void correctRsqlBuildsSimpleNotEqualPredicate() {
reset(baseSoftwareModuleRootMock, criteriaQueryMock, criteriaBuilderMock);
final String correctRsql = "name!=abc";
when(baseSoftwareModuleRootMock.get("name")).thenReturn(baseSoftwareModuleRootMock);
when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class);
when(criteriaBuilderMock.isNull(any(Expression.class))).thenReturn(mock(Predicate.class));
when(criteriaBuilderMock.notEqual(any(Expression.class), anyString()))
.thenReturn(mock(Predicate.class));
when(criteriaBuilderMock.upper(eq(pathOfString(baseSoftwareModuleRootMock))))
.thenReturn(pathOfString(baseSoftwareModuleRootMock));
// test
RSQLUtility.buildRsqlSpecification(correctRsql, SoftwareModuleFields.class, null, testDb)
.toPredicate(baseSoftwareModuleRootMock, criteriaQueryMock, criteriaBuilderMock);
// verification
verify(criteriaBuilderMock, times(1)).or(any(Predicate.class), any(Predicate.class));
verify(criteriaBuilderMock, times(1)).isNull(eq(pathOfString(baseSoftwareModuleRootMock)));
verify(criteriaBuilderMock, times(1)).notEqual(eq(pathOfString(baseSoftwareModuleRootMock)),
eq("abc".toUpperCase()));
}
@Test
public void correctRsqlBuildsSimpleNotLikePredicate() {
reset(baseSoftwareModuleRootMock, criteriaQueryMock, criteriaBuilderMock);
final String correctRsql = "name!=abc*";
when(baseSoftwareModuleRootMock.get("name")).thenReturn(baseSoftwareModuleRootMock);
when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class);
when(criteriaBuilderMock.isNull(any(Expression.class))).thenReturn(mock(Predicate.class));
when(criteriaBuilderMock.notLike(any(Expression.class), anyString(), eq('\\')))
.thenReturn(mock(Predicate.class));
@@ -304,7 +328,7 @@ public class RSQLUtilityTest {
verify(criteriaBuilderMock, times(1)).or(any(Predicate.class), any(Predicate.class));
verify(criteriaBuilderMock, times(1)).isNull(eq(pathOfString(baseSoftwareModuleRootMock)));
verify(criteriaBuilderMock, times(1)).notLike(eq(pathOfString(baseSoftwareModuleRootMock)),
eq("abc".toUpperCase()), eq('\\'));
eq("abc%".toUpperCase()), eq('\\'));
}
@Test
@@ -335,11 +359,32 @@ public class RSQLUtilityTest {
}
@Test
public void correctRsqlBuildsLikePredicateWithPercentage() {
public void correctRsqlBuildsEqualPredicateWithPercentage() {
reset(baseSoftwareModuleRootMock, criteriaQueryMock, criteriaBuilderMock);
final String correctRsql = "name==a%";
when(baseSoftwareModuleRootMock.get("name")).thenReturn(baseSoftwareModuleRootMock);
when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class);
when(criteriaBuilderMock.equal(any(Expression.class), anyString())).thenReturn(mock(Predicate.class));
when(criteriaBuilderMock.<String> greaterThanOrEqualTo(any(Expression.class), any(String.class)))
.thenReturn(mock(Predicate.class));
when(criteriaBuilderMock.upper(eq(pathOfString(baseSoftwareModuleRootMock))))
.thenReturn(pathOfString(baseSoftwareModuleRootMock));
// test
RSQLUtility.buildRsqlSpecification(correctRsql, SoftwareModuleFields.class, null, Database.H2)
.toPredicate(baseSoftwareModuleRootMock, criteriaQueryMock, criteriaBuilderMock);
// verification
verify(criteriaBuilderMock, times(1)).and(any(Predicate.class));
verify(criteriaBuilderMock, times(1)).equal(eq(pathOfString(baseSoftwareModuleRootMock)),
eq("a%".toUpperCase()));
}
@Test
public void correctRsqlBuildsLikePredicateWithPercentage() {
reset(baseSoftwareModuleRootMock, criteriaQueryMock, criteriaBuilderMock);
final String correctRsql = "name==a%*";
when(baseSoftwareModuleRootMock.get("name")).thenReturn(baseSoftwareModuleRootMock);
when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class);
when(criteriaBuilderMock.like(any(Expression.class), anyString(), eq('\\'))).thenReturn(mock(Predicate.class));
when(criteriaBuilderMock.<String> greaterThanOrEqualTo(any(Expression.class), any(String.class)))
.thenReturn(mock(Predicate.class));
@@ -352,13 +397,13 @@ public class RSQLUtilityTest {
// verification
verify(criteriaBuilderMock, times(1)).and(any(Predicate.class));
verify(criteriaBuilderMock, times(1)).like(eq(pathOfString(baseSoftwareModuleRootMock)),
eq("a\\%".toUpperCase()), eq('\\'));
eq("a\\%%".toUpperCase()), eq('\\'));
}
@Test
public void correctRsqlBuildsLikePredicateWithPercentageSQLServer() {
reset(baseSoftwareModuleRootMock, criteriaQueryMock, criteriaBuilderMock);
final String correctRsql = "name==a%";
final String correctRsql = "name==a%*";
when(baseSoftwareModuleRootMock.get("name")).thenReturn(baseSoftwareModuleRootMock);
when(baseSoftwareModuleRootMock.getJavaType()).thenReturn((Class) SoftwareModule.class);
when(criteriaBuilderMock.upper(eq(pathOfString(baseSoftwareModuleRootMock))))
@@ -374,7 +419,7 @@ public class RSQLUtilityTest {
// verification
verify(criteriaBuilderMock, times(1)).and(any(Predicate.class));
verify(criteriaBuilderMock, times(1)).like(eq(pathOfString(baseSoftwareModuleRootMock)),
eq("a[%]".toUpperCase()), eq('\\'));
eq("a[%]%".toUpperCase()), eq('\\'));
}
@Test