From: Eric Hartmann Date: Tue, 30 Jan 2018 11:34:11 +0000 (+0100) Subject: SONAR-10313 Keep the order of issues on Search X-Git-Tag: 7.5~1700 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=4a0df7b8f2c14211ee282b8d1335efd09375eba1;p=sonarqube.git SONAR-10313 Keep the order of issues on Search --- diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDao.java index 5570e5c8494..482857eecfb 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDao.java @@ -19,15 +19,10 @@ */ package org.sonar.db.issue; -import com.google.common.base.Function; -import com.google.common.base.Predicates; import java.util.Collection; -import java.util.HashMap; import java.util.List; -import java.util.Map; +import java.util.Optional; import java.util.Set; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import org.apache.ibatis.session.ResultHandler; import org.sonar.db.Dao; import org.sonar.db.DbSession; @@ -35,18 +30,17 @@ import org.sonar.db.RowNotFoundException; import org.sonar.db.WildcardPosition; import org.sonar.db.component.ComponentDto; -import static com.google.common.collect.FluentIterable.from; import static org.sonar.db.DaoDatabaseUtils.buildLikeValue; import static org.sonar.db.DatabaseUtils.executeLargeInputs; public class IssueDao implements Dao { - public java.util.Optional selectByKey(DbSession session, String key) { - return java.util.Optional.ofNullable(mapper(session).selectByKey(key)); + public Optional selectByKey(DbSession session, String key) { + return Optional.ofNullable(mapper(session).selectByKey(key)); } public IssueDto selectOrFailByKey(DbSession session, String key) { - java.util.Optional issue = selectByKey(session, key); + Optional issue = selectByKey(session, key); if (!issue.isPresent()) { throw new RowNotFoundException(String.format("Issue with key '%s' does not exist", key)); } @@ -57,39 +51,12 @@ public class IssueDao implements Dao { * Gets a list issues by their keys. The result does NOT contain {@code null} values for issues not found, so * the size of result may be less than the number of keys. A single issue is returned * if input keys contain multiple occurrences of a key. - *

Results may be in a different order as input keys (see {@link #selectByOrderedKeys(DbSession, List)}).

+ *

Results may be in a different order as input keys.

*/ public List selectByKeys(final DbSession session, Collection keys) { return executeLargeInputs(keys, mapper(session)::selectByKeys); } - /** - * Gets a list issues by their keys. The result does NOT contain {@code null} values for issues not found, so - * the size of result may be less than the number of keys. A single issue is returned - * if input keys contain multiple occurrences of a key. - *

Contrary to {@link #selectByKeys(DbSession, Collection)}, results are in the same order as input keys.

- */ - public List selectByOrderedKeys(DbSession session, List keys) { - List unordered = selectByKeys(session, keys); - return from(keys).transform(new KeyToIssue(unordered)).filter(Predicates.notNull()).toList(); - } - - private static class KeyToIssue implements Function { - private final Map map = new HashMap<>(); - - private KeyToIssue(Collection unordered) { - for (IssueDto dto : unordered) { - map.put(dto.getKey(), dto); - } - } - - @Nullable - @Override - public IssueDto apply(@Nonnull String issueKey) { - return map.get(issueKey); - } - } - public Set selectComponentUuidsOfOpenIssuesForProjectUuid(DbSession session, String projectUuid) { return mapper(session).selectComponentUuidsOfOpenIssuesForProjectUuid(projectUuid); } diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java index c32c54e366a..9791628fc34 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java @@ -122,18 +122,6 @@ public class IssueDaoTest { assertThat(issues).extracting("key").containsOnly("I1", "I2"); } - @Test - public void selectByOrderedKeys() { - // contains I1 and I2 - prepareTables(); - - Iterable issues = underTest.selectByOrderedKeys(db.getSession(), asList("I1", "I2", "I3")); - assertThat(issues).extracting("key").containsExactly("I1", "I2"); - - issues = underTest.selectByOrderedKeys(db.getSession(), asList("I2", "I3", "I1")); - assertThat(issues).extracting("key").containsExactly("I2", "I1"); - } - @Test public void scrollNonClosedByComponentUuid() { RuleDefinitionDto rule = db.rules().insert(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseLoader.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseLoader.java index 7e03b90f08c..9dc3608d4d5 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseLoader.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseLoader.java @@ -19,15 +19,19 @@ */ package org.sonar.server.issue.ws; +import com.google.common.base.Function; import com.google.common.collect.ImmutableSet; import com.google.common.collect.MultimapBuilder; import com.google.common.collect.SetMultimap; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; import javax.annotation.Nullable; import org.sonar.core.issue.DefaultIssue; import org.sonar.core.util.stream.MoreCollectors; @@ -80,8 +84,7 @@ public class SearchResponseLoader { /** * The issue keys are given by the multi-criteria search in Elasticsearch index. *

- * Same as {@link #load(SearchResponseData, Collector, Facets)} but will only retrieve from DB data which is not - * already provided by the specified preloaded {@link SearchResponseData}.
+ * It will only retrieve from DB data which is not already provided by the specified preloaded {@link SearchResponseData}.
* The returned {@link SearchResponseData} is not the one specified as argument. *

*/ @@ -106,14 +109,23 @@ public class SearchResponseLoader { private List loadIssues(SearchResponseData preloadedResponseData, Collector collector, DbSession dbSession) { List preloadedIssues = preloadedResponseData.getIssues(); Set preloadedIssueKeys = preloadedIssues.stream().map(IssueDto::getKey).collect(MoreCollectors.toSet(preloadedIssues.size())); - Set issueKeysToLoad = copyOf(difference(ImmutableSet.copyOf(collector.getIssueKeys()), preloadedIssueKeys)); + + ImmutableSet issueKeys = ImmutableSet.copyOf(collector.getIssueKeys()); + Set issueKeysToLoad = copyOf(difference(issueKeys, preloadedIssueKeys)); + if (issueKeysToLoad.isEmpty()) { - return preloadedIssues; + return issueKeys.stream() + .map(new KeyToIssueFunction(preloadedIssues)::apply).filter(Objects::nonNull) + .collect(Collectors.toList()); } List loadedIssues = dbClient.issueDao().selectByKeys(dbSession, issueKeysToLoad); - return concat(preloadedIssues.stream(), loadedIssues.stream()) + List unorderedIssues = concat(preloadedIssues.stream(), loadedIssues.stream()) .collect(toList(preloadedIssues.size() + loadedIssues.size())); + + return issueKeys.stream() + .map(new KeyToIssueFunction(unorderedIssues)::apply).filter(Objects::nonNull) + .collect(Collectors.toList()); } private void loadUsers(SearchResponseData preloadedResponseData, Collector collector, DbSession dbSession, SearchResponseData result) { @@ -338,4 +350,20 @@ public class SearchResponseLoader { return projectUuids; } } + + private static class KeyToIssueFunction implements Function { + private final Map map = new HashMap<>(); + + private KeyToIssueFunction(Collection unordered) { + for (IssueDto dto : unordered) { + map.put(dto.getKey(), dto); + } + } + + @Nullable + @Override + public IssueDto apply(String issueKey) { + return map.get(issueKey); + } + } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java index cf8cc73604a..df3f8b2056a 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java @@ -19,6 +19,8 @@ */ package org.sonar.server.issue.ws; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; import java.time.Clock; import org.junit.Before; import org.junit.Rule; @@ -627,11 +629,16 @@ public class SearchActionTest { session.commit(); indexIssues(); - ws.newRequest() + TestResponse response = ws.newRequest() .setParam("sort", IssueQuery.SORT_BY_UPDATE_DATE) .setParam("asc", "false") - .execute() - .assertJson(this.getClass(), "sort_by_updated_at.json"); + .execute(); + + JsonElement parse = new JsonParser().parse(response.getInput()); + + assertThat(parse.getAsJsonObject().get("issues").getAsJsonArray()) + .extracting(o -> o.getAsJsonObject().get("key").getAsString()) + .containsExactly("82fd47d4-b650-4037-80bc-7b112bd4eac3", "82fd47d4-b650-4037-80bc-7b112bd4eac1", "82fd47d4-b650-4037-80bc-7b112bd4eac2"); } @Test diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionTest/sort_by_updated_at.json b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionTest/sort_by_updated_at.json deleted file mode 100644 index faac653a9f8..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionTest/sort_by_updated_at.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "issues": [ - { - "organization": "my-org-2", - "key": "82fd47d4-b650-4037-80bc-7b112bd4eac2", - "component": "FILE_KEY", - "project": "PROJECT_KEY", - "rule": "xoo:x1", - "updateDate": "2014-11-01T00:00:00+0100" - }, - { - "organization": "my-org-2", - "key": "82fd47d4-b650-4037-80bc-7b112bd4eac1", - "component": "FILE_KEY", - "project": "PROJECT_KEY", - "rule": "xoo:x1", - "updateDate": "2014-11-02T00:00:00+0100" - }, - { - "organization": "my-org-2", - "key": "82fd47d4-b650-4037-80bc-7b112bd4eac3", - "component": "FILE_KEY", - "project": "PROJECT_KEY", - "rule": "xoo:x1", - "updateDate": "2014-11-03T00:00:00+0100" - } - ] -} diff --git a/tests/src/test/java/org/sonarqube/tests/issue/IssueCreationDatePluginChangedTest.java b/tests/src/test/java/org/sonarqube/tests/issue/IssueCreationDatePluginChangedTest.java index 6ad7158a0f6..af2fbd1e1a6 100644 --- a/tests/src/test/java/org/sonarqube/tests/issue/IssueCreationDatePluginChangedTest.java +++ b/tests/src/test/java/org/sonarqube/tests/issue/IssueCreationDatePluginChangedTest.java @@ -23,12 +23,10 @@ import com.google.common.collect.ImmutableList; import com.sonar.orchestrator.Orchestrator; import com.sonar.orchestrator.build.SonarScanner; import java.io.File; -import java.io.IOException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; -import javax.mail.MessagingException; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; @@ -150,7 +148,9 @@ public class IssueCreationDatePluginChangedTest { // New analysis that should raise 2 new issues that will be backdated ORCHESTRATOR.executeBuild(scanner); - issues = getIssues(issueQuery().components("creation-date-sample:src/main/xoo/sample/Sample.xoo")); + issues = getIssues(issueQuery() + .components("creation-date-sample:src/main/xoo/sample/Sample.xoo") + .sort("FILE_LINE")); assertThat(issues) .extracting(Issue::line, Issue::creationDate) .containsExactly(tuple(1, dateTimeParse("2005-01-01T00:00:00+0000")),