From ad505ffd8bb057b425064a0f580fbed25e683502 Mon Sep 17 00:00:00 2001 From: Benjamin Campomenosi <109955405+benjamin-campomenosi-sonarsource@users.noreply.github.com> Date: Thu, 11 Aug 2022 09:12:25 +0200 Subject: [PATCH] SONAR-17150 fix SSF-39 --- .../org/sonar/db/rule/RuleRepositoryDao.java | 19 ++++--- .../sonar/db/rule/RuleRepositoryMapper.java | 3 +- .../sonar/db/rule/RuleRepositoryMapper.xml | 9 +++- .../sonar/db/rule/RuleRepositoryDaoTest.java | 53 +++++++++++++++---- .../server/rule/ws/RepositoriesAction.java | 17 ++---- .../rule/ws/RepositoriesActionTest.java | 13 +++++ 6 files changed, 82 insertions(+), 32 deletions(-) diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleRepositoryDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleRepositoryDao.java index e08d821f929..2c6136a94ac 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleRepositoryDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleRepositoryDao.java @@ -21,16 +21,21 @@ package org.sonar.db.rule; import java.util.Collection; import java.util.List; +import java.util.Locale; import java.util.Set; +import javax.annotation.Nullable; import org.sonar.api.utils.System2; import org.sonar.db.Dao; import org.sonar.db.DatabaseUtils; import org.sonar.db.DbSession; import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.commons.lang.StringUtils.isBlank; public class RuleRepositoryDao implements Dao { + private static final String PERCENT_SIGN = "%"; + private final System2 system2; public RuleRepositoryDao(System2 system2) { @@ -49,12 +54,9 @@ public class RuleRepositoryDao implements Dao { return dbSession.getMapper(RuleRepositoryMapper.class).selectAllKeys(); } - /** - * @return a non-null list ordered by key (as implemented by database, order may - * depend on case sensitivity) - */ - public List selectByLanguage(DbSession dbSession, String language) { - return dbSession.getMapper(RuleRepositoryMapper.class).selectByLanguage(language); + public List selectByQueryAndLanguage(DbSession dbSession, @Nullable String query, @Nullable String language){ + String queryUpgraded = toLowerCaseAndSurroundWithPercentSigns(query); + return dbSession.getMapper(RuleRepositoryMapper.class).selectByQueryAndLanguage(queryUpgraded,language); } public void insert(DbSession dbSession, Collection dtos) { @@ -76,4 +78,9 @@ public class RuleRepositoryDao implements Dao { checkArgument(keys.size() < DatabaseUtils.PARTITION_SIZE_FOR_ORACLE, "too many rule repositories: %s", keys.size()); dbSession.getMapper(RuleRepositoryMapper.class).deleteIfKeyNotIn(keys); } + + private static String toLowerCaseAndSurroundWithPercentSigns(@Nullable String query) { + return isBlank(query) ? PERCENT_SIGN : (PERCENT_SIGN + query.toLowerCase(Locale.ENGLISH) + PERCENT_SIGN); + } + } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleRepositoryMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleRepositoryMapper.java index 2f0aeb989b1..6a2c9d2c119 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleRepositoryMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleRepositoryMapper.java @@ -23,6 +23,7 @@ import java.util.Collection; import java.util.List; import java.util.Set; import javax.annotation.CheckForNull; +import javax.annotation.Nullable; import org.apache.ibatis.annotations.Param; public interface RuleRepositoryMapper { @@ -31,7 +32,7 @@ public interface RuleRepositoryMapper { Set selectAllKeys(); - List selectByLanguage(@Param("language") String language); + List selectByQueryAndLanguage(@Param("query") String query,@Param("language") @Nullable String language); @CheckForNull RuleRepositoryDto selectByKey(@Param("key") String key); diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/rule/RuleRepositoryMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/rule/RuleRepositoryMapper.xml index 962dd8a1a1a..da15614c902 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/rule/RuleRepositoryMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/rule/RuleRepositoryMapper.xml @@ -18,10 +18,15 @@ from rule_repositories - select from rule_repositories - where language = #{language} + where + ( lower(kee) like #{query} + or lower(name) like #{query} ) + + and language = #{language} + order by kee diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleRepositoryDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleRepositoryDaoTest.java index b75e26632cc..66b76b8d32d 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleRepositoryDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleRepositoryDaoTest.java @@ -123,18 +123,34 @@ public class RuleRepositoryDaoTest { } @Test - public void selectByLanguage() { + public void selectByQueryAndLanguage_shouldMatchOnlyOnKeeOrName(){ DbSession dbSession = dbTester.getSession(); - RuleRepositoryDto dto1 = new RuleRepositoryDto("findbugs", "java", "Findbugs"); - RuleRepositoryDto dto2 = new RuleRepositoryDto("java", "java", "Java"); + RuleRepositoryDto dto1 = new RuleRepositoryDto("a_findbugs", "java", "Findbugs"); + RuleRepositoryDto dto2 = new RuleRepositoryDto("jdk", "java", "Java"); RuleRepositoryDto dto3 = new RuleRepositoryDto("cobol-lint", "cobol", "Cobol Lint"); - underTest.insert(dbSession, asList(dto1, dto2, dto3)); + underTest.insert(dbSession, asList(dto1,dto2,dto3)); - assertThat(underTest.selectByLanguage(dbSession, "java")).extracting(RuleRepositoryDto::getKey) - // ordered by key - .containsExactly("findbugs", "java"); + List ruleRepositoryDtos = underTest.selectByQueryAndLanguage(dbSession, "%a%",null); + + assertThat(ruleRepositoryDtos).extracting(RuleRepositoryDto::getName) + .containsExactlyInAnyOrder("Java","Findbugs"); } + @Test + public void selectByQueryAndLanguage_whenSeveralRepoMatchingForDifferentLanguages_matchOnlyTheRepoOfTheChosenLanguage(){ + DbSession dbSession = dbTester.getSession(); + RuleRepositoryDto dto1 = new RuleRepositoryDto("findbugs", "java", "Findbugsa"); + RuleRepositoryDto dto2 = new RuleRepositoryDto("java", "java", "Java"); + RuleRepositoryDto dto3 = new RuleRepositoryDto("cobol-bug", "cobol", "Cobol Lint"); + underTest.insert(dbSession, asList(dto1,dto2,dto3)); + + List ruleRepositoryDtos = underTest.selectByQueryAndLanguage(dbSession, "%bug%", "java"); + + assertThat(ruleRepositoryDtos).extracting(RuleRepositoryDto::getKey) + .containsExactly("findbugs"); + } + + @Test public void selectAllKeys() { DbSession dbSession = dbTester.getSession(); @@ -147,14 +163,33 @@ public class RuleRepositoryDaoTest { } @Test - public void selectByLanguage_returns_empty_list_if_no_results() { + public void selectByQueryAndLanguage_returnsEmptyList_when_thereIsNoResults() { DbSession dbSession = dbTester.getSession(); RuleRepositoryDto dto1 = new RuleRepositoryDto("findbugs", "java", "Findbugs"); underTest.insert(dbSession, asList(dto1)); - assertThat(underTest.selectByLanguage(dbSession, "missing")).isEmpty(); + assertThat(underTest.selectByQueryAndLanguage(dbSession, "missing", null)).isEmpty(); } + @Test + public void selectByQueryAndLanguage_shouldBeCaseInsensitive(){ + DbSession dbSession = dbTester.getSession(); + RuleRepositoryDto dto1 = new RuleRepositoryDto("FINDBUGS", "java", "repoFB"); + RuleRepositoryDto dto2 = new RuleRepositoryDto("cobol-lint", "cobol", "Cobol Lint"); + RuleRepositoryDto dto3 = new RuleRepositoryDto("openjdk", "java", "JaVa"); + underTest.insert(dbSession, asList(dto1,dto2,dto3)); + + assertThat(underTest.selectByQueryAndLanguage(dbSession,"bug", null)) + .extracting(RuleRepositoryDto::getKey).contains("FINDBUGS"); + assertThat(underTest.selectByQueryAndLanguage(dbSession,"COBOL", null)) + .extracting(RuleRepositoryDto::getKey).contains("cobol-lint"); + assertThat(underTest.selectByQueryAndLanguage(dbSession,"jAvA", null)) + .extracting(RuleRepositoryDto::getKey).contains("openjdk"); + + } + + + private long selectCreatedAtByKey(DbSession dbSession, String key) { return (long) dbTester.selectFirst(dbSession, "select created_at as \"created_at\" from rule_repositories where kee='" + key + "'") .get("created_at"); diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/rule/ws/RepositoriesAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/rule/ws/RepositoriesAction.java index 77df6a5bacf..9318866b87e 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/rule/ws/RepositoriesAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/rule/ws/RepositoriesAction.java @@ -21,7 +21,6 @@ package org.sonar.server.rule.ws; import com.google.common.io.Resources; import java.util.Collection; -import java.util.regex.Pattern; import javax.annotation.Nullable; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; @@ -29,12 +28,10 @@ import org.sonar.api.server.ws.WebService; import org.sonar.api.server.ws.WebService.NewAction; import org.sonar.api.server.ws.WebService.Param; import org.sonar.api.utils.text.JsonWriter; -import org.sonar.core.util.stream.MoreCollectors; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.rule.RuleRepositoryDto; -import static org.apache.commons.lang.StringUtils.isEmpty; /** * @since 5.1 @@ -42,7 +39,6 @@ import static org.apache.commons.lang.StringUtils.isEmpty; public class RepositoriesAction implements RulesWsAction { private static final String LANGUAGE = "language"; - private static final String MATCH_ALL = ".*"; private final DbClient dbClient; @@ -86,19 +82,12 @@ public class RepositoriesAction implements RulesWsAction { } private Collection listMatchingRepositories(@Nullable String query, @Nullable String languageKey) { - Pattern pattern = Pattern.compile(query == null ? MATCH_ALL : MATCH_ALL + Pattern.quote(query) + MATCH_ALL, Pattern.CASE_INSENSITIVE); - - return selectFromDb(languageKey).stream() - .filter(r -> pattern.matcher(r.getKey()).matches() || pattern.matcher(r.getName()).matches()) - .collect(MoreCollectors.toList()); + return selectFromDb(languageKey, query); } - private Collection selectFromDb(@Nullable String language) { + private Collection selectFromDb(@Nullable String language, @Nullable String query) { try (DbSession dbSession = dbClient.openSession(false)) { - if (isEmpty(language)) { - return dbClient.ruleRepositoryDao().selectAll(dbSession); - } - return dbClient.ruleRepositoryDao().selectByLanguage(dbSession, language); + return dbClient.ruleRepositoryDao().selectByQueryAndLanguage(dbSession, query, language); } } } diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/ws/RepositoriesActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/ws/RepositoriesActionTest.java index 5ca37024fd4..5c55915196e 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/ws/RepositoriesActionTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/ws/RepositoriesActionTest.java @@ -65,6 +65,19 @@ public class RepositoriesActionTest { newRequest().setParam("q", "sonar").execute().assertJson(this.getClass(), "repositories_sonar.json"); } + @Test + public void filter_repository_by_query(){ + newRequest().setParam("q", "xo").execute().assertJson(this.getClass(),"repositories_xoo.json"); + newRequest().setParam("q","w").execute().assertJson(this.getClass(),"repositories_common.json"); + } + + + @Test + public void filter_repository_by_query_and_language(){ + newRequest().setParam("q","ava").setParam("language","ws").execute().assertJson(this.getClass(),"repositories_java.json"); + newRequest().setParam("q","w").setParam("language","ws").execute().assertJson(this.getClass(),"repositories_common.json"); + } + @Test public void do_not_consider_query_as_regexp_when_filtering_repositories_by_name() { // invalid regexp : do not fail. Query is not a regexp. -- 2.39.5