]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-10313 fix error when displaying rule facet 3056/head
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Tue, 13 Feb 2018 11:29:11 +0000 (12:29 +0100)
committerSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Tue, 13 Feb 2018 16:20:33 +0000 (17:20 +0100)
server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java
server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAdditionalField.java
server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java
server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseLoader.java

index 0ec420128ddeb85d0789e28dd7eb2bb46bff56a4..a76a989689e1e1d9b894844b3259904c989c2265 100644 (file)
@@ -20,7 +20,6 @@
 package org.sonar.server.issue.ws;
 
 import com.google.common.base.Joiner;
-import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import java.util.Arrays;
@@ -69,6 +68,7 @@ import static java.lang.String.format;
 import static java.util.Collections.emptyList;
 import static java.util.Collections.singletonList;
 import static org.sonar.api.utils.Paging.forPageIndex;
+import static org.sonar.core.util.stream.MoreCollectors.toSet;
 import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
 import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
 import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
@@ -360,7 +360,7 @@ public class SearchAction implements IssuesWsAction {
 
       Set<String> facetsRequiringProjectOrOrganizationParameter = facets.getNames().stream()
         .filter(FACETS_REQUIRING_PROJECT_OR_ORGANIZATION::contains)
-        .collect(MoreCollectors.toSet());
+        .collect(toSet());
       checkArgument(facetsRequiringProjectOrOrganizationParameter.isEmpty() ||
         (!query.projectUuids().isEmpty()) || query.organizationUuid() != null, "Facet(s) '%s' require to also filter by project or organization",
         COMA_JOINER.join(facetsRequiringProjectOrOrganizationParameter));
@@ -384,35 +384,59 @@ public class SearchAction implements IssuesWsAction {
   }
 
   private void replaceRuleIdsByRuleKeys(@Nullable Facets facets, List<RuleDefinitionDto> alreadyLoadedRules) {
-    if (facets == null || facets.get(PARAM_RULES) == null) {
+    if (facets == null) {
+      return;
+    }
+    LinkedHashMap<String, Long> rulesFacet = facets.get(PARAM_RULES);
+    if (rulesFacet == null) {
       return;
     }
 
     // The facet for PARAM_RULES contains the id of the rule as the key
     // We need to update the key to be a RuleKey
-    LinkedHashMap<String, Long> rulesFacet = facets.get(PARAM_RULES);
-
     try (DbSession dbSession = dbClient.openSession(false)) {
-      Set<String> ruleKeysToLoad = new HashSet<>(rulesFacet.keySet());
-      ruleKeysToLoad.removeAll(
+      Set<Integer> ruleIdsToLoad = new HashSet<>();
+      rulesFacet.keySet().forEach(s -> {
+        try {
+          ruleIdsToLoad.add(Integer.parseInt(s));
+        } catch (NumberFormatException e) {
+          // ignore, this is already a key
+        }
+      });
+      ruleIdsToLoad.removeAll(
         alreadyLoadedRules
           .stream()
-          .map(r -> r.getKey().toString())
+          .map(RuleDefinitionDto::getId)
           .collect(Collectors.toList()));
 
-      Map<Integer, RuleKey> idToRuleKey = Stream.concat(
+      List<RuleDefinitionDto> ruleDefinitions = Stream.concat(
         alreadyLoadedRules.stream(),
-        dbClient.ruleDao().selectDefinitionByIds(dbSession, Collections2.transform(ruleKeysToLoad, Integer::parseInt)).stream())
+        dbClient.ruleDao().selectDefinitionByIds(dbSession, ruleIdsToLoad).stream())
+        .collect(MoreCollectors.toList());
+      Map<Integer, RuleKey> ruleKeyById = ruleDefinitions.stream()
         .collect(Collectors.toMap(RuleDefinitionDto::getId, RuleDefinitionDto::getKey));
+      Map<String, Integer> idByRuleKeyAsString = ruleDefinitions.stream()
+        .collect(Collectors.toMap(s -> s.getKey().toString(), RuleDefinitionDto::getId));
 
       LinkedHashMap<String, Long> newRulesFacet = new LinkedHashMap<>();
       rulesFacet.forEach((k, v) -> {
-        RuleKey ruleKey = idToRuleKey.get(Integer.parseInt(k));
-        if (ruleKey != null) {
-          newRulesFacet.put(ruleKey.toString(), v);
-        } else {
-          // RuleKey not found ES/DB incorrect?
-          LOGGER.error("Rule with id {} is not available in database", k);
+        try {
+          int ruleId = Integer.parseInt(k);
+          RuleKey ruleKey = ruleKeyById.get(ruleId);
+          if (ruleKey != null) {
+            newRulesFacet.put(ruleKey.toString(), v);
+          } else {
+            // RuleKey not found ES/DB incorrect?
+            LOGGER.error("Rule with id {} is not available in database", k);
+          }
+        } catch (NumberFormatException e) {
+          // RuleKey are added into the facet from the HTTP request, there may be a result for this rule from the
+          // ES search (with the ruleId as a key). If so, do not add this entry again (anyway, value for ruleKey is
+          // always 0 since it is added SearchAction#completeFacets).
+          String ruleId = String.valueOf(idByRuleKeyAsString.get(k));
+          if (!rulesFacet.containsKey(ruleId)) {
+            newRulesFacet.put(k, v);
+          }
         }
       });
       rulesFacet.clear();
@@ -502,7 +526,7 @@ public class SearchAction implements IssuesWsAction {
   private static void collectFacets(SearchResponseLoader.Collector collector, Facets facets) {
     Set<String> facetRules = facets.getBucketKeys(PARAM_RULES);
     if (facetRules != null) {
-      collector.addAll(SearchAdditionalField.RULES, facetRules);
+      collector.addAll(SearchAdditionalField.RULE_IDS_AND_KEYS, facetRules);
     }
     collector.addProjectUuids(facets.getBucketKeys(PARAM_PROJECT_UUIDS));
     collector.addComponentUuids(facets.getBucketKeys(PARAM_COMPONENT_UUIDS));
index 4fa8217c1cd8afbb07f4c675dc4a1e038998e60d..dd26aff6bd48d9a855a11235bafc0c6b1e8bb06c 100644 (file)
@@ -38,7 +38,8 @@ public enum SearchAdditionalField {
   DEPRECATED_ACTION_PLANS("actionPlans"),
   COMMENTS("comments"),
   LANGUAGES("languages"),
-  RULES("rules"),
+  // may contain strings representing rule ids (from ES facets) or string representing ruleKeys (from HTTP request)
+  RULE_IDS_AND_KEYS("rules"),
   TRANSITIONS("transitions"),
   USERS("users");
 
index 9f5ddbb07f3a4dc3a356dce080faf94f797f3f94..e41a988935c2a12247bddc8c08fb719f388586ed 100644 (file)
@@ -89,7 +89,7 @@ public class SearchResponseFormat {
     if (facets != null) {
       formatFacets(facets, response);
     }
-    if (fields.contains(SearchAdditionalField.RULES)) {
+    if (fields.contains(SearchAdditionalField.RULE_IDS_AND_KEYS)) {
       response.setRules(formatRules(data));
     }
     if (fields.contains(SearchAdditionalField.USERS)) {
index 9dc3608d4d58b5839f5d246d8d3a5cd40a0f5854..9c793b935de5f8df5c755c20317195e832d94c1d 100644 (file)
@@ -32,7 +32,9 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 import javax.annotation.Nullable;
+import org.sonar.api.rule.RuleKey;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.util.stream.MoreCollectors;
 import org.sonar.db.DbClient;
@@ -62,7 +64,7 @@ import static org.sonar.server.issue.SetSeverityAction.SET_SEVERITY_KEY;
 import static org.sonar.server.issue.SetTypeAction.SET_TYPE_KEY;
 import static org.sonar.server.issue.ws.SearchAdditionalField.ACTIONS;
 import static org.sonar.server.issue.ws.SearchAdditionalField.COMMENTS;
-import static org.sonar.server.issue.ws.SearchAdditionalField.RULES;
+import static org.sonar.server.issue.ws.SearchAdditionalField.RULE_IDS_AND_KEYS;
 import static org.sonar.server.issue.ws.SearchAdditionalField.TRANSITIONS;
 import static org.sonar.server.issue.ws.SearchAdditionalField.USERS;
 
@@ -174,16 +176,32 @@ public class SearchResponseLoader {
   }
 
   private void loadRules(SearchResponseData preloadedResponseData, Collector collector, DbSession dbSession, SearchResponseData result) {
-    if (collector.contains(RULES)) {
+    if (collector.contains(RULE_IDS_AND_KEYS)) {
       List<RuleDefinitionDto> preloadedRules = firstNonNull(preloadedResponseData.getRules(), emptyList());
-      Set<Integer> preloaedRuleKeys = preloadedRules.stream().map(RuleDefinitionDto::getId).collect(MoreCollectors.toSet());
-      Set<Integer> ruleKeysToLoad = copyOf(difference(collector.get(RULES), preloaedRuleKeys));
-      if (ruleKeysToLoad.isEmpty()) {
-        result.setRules(preloadedResponseData.getRules());
-      } else {
-        List<RuleDefinitionDto> loadedRules = dbClient.ruleDao().selectDefinitionByIds(dbSession, ruleKeysToLoad);
-        result.setRules(concat(preloadedRules.stream(), loadedRules.stream()).collect(toList(preloadedRules.size() + loadedRules.size())));
-      }
+      Set<Integer> ruleIdsToLoad = new HashSet<>();
+      Set<RuleKey> ruleKeysToLoad = new HashSet<>();
+      collector.get(RULE_IDS_AND_KEYS).forEach(o -> {
+        if (o instanceof String) {
+          try {
+            ruleIdsToLoad.add(Integer.parseInt((String) o));
+          } catch (NumberFormatException e) {
+            ruleKeysToLoad.add(RuleKey.parse((String) o));
+          }
+        } else {
+          throw new IllegalArgumentException("Unsupported object " + o + " of type " + o.getClass().getSimpleName() + " in additional field " + RULE_IDS_AND_KEYS);
+        }
+      });
+      ruleIdsToLoad.removeAll(preloadedRules.stream().map(RuleDefinitionDto::getId).collect(toList(preloadedRules.size())));
+      ruleKeysToLoad.removeAll(preloadedRules.stream().map(RuleDefinitionDto::getKey).collect(toList(preloadedRules.size())));
+
+      result.setRules(
+        Stream.of(
+          preloadedRules.stream(),
+          dbClient.ruleDao().selectDefinitionByIds(dbSession, ruleIdsToLoad).stream(),
+          dbClient.ruleDao().selectDefinitionByKeys(dbSession, ruleKeysToLoad).stream())
+          .flatMap(s -> s)
+          .distinct()
+          .collect(Collectors.toList()));
     }
   }
 
@@ -214,11 +232,10 @@ public class SearchResponseLoader {
 
   private void loadActionsAndTransitions(Collector collector, SearchResponseData result) {
     if (collector.contains(ACTIONS) || collector.contains(TRANSITIONS)) {
-      Map<String, ComponentDto> componentsByProjectUuid =
-        result.getComponents()
-          .stream()
-          .filter(ComponentDto::isRootProject)
-          .collect(MoreCollectors.uniqueIndex(ComponentDto::projectUuid));
+      Map<String, ComponentDto> componentsByProjectUuid = result.getComponents()
+        .stream()
+        .filter(ComponentDto::isRootProject)
+        .collect(MoreCollectors.uniqueIndex(ComponentDto::projectUuid));
       for (IssueDto dto : result.getIssues()) {
         // so that IssueDto can be used.
         if (collector.contains(ACTIONS)) {
@@ -271,6 +288,8 @@ public class SearchResponseLoader {
     private final Set<String> componentUuids = new HashSet<>();
     private final Set<String> projectUuids = new HashSet<>();
     private final List<String> issueKeys;
+    private final Set<Integer> ruleIds = new HashSet<>();
+    private final Set<String> ruleKeys = new HashSet<>();
 
     public Collector(Set<SearchAdditionalField> fields, List<String> issueKeys) {
       this.fields = fields;
@@ -281,7 +300,7 @@ public class SearchResponseLoader {
       for (IssueDto issue : issues) {
         componentUuids.add(issue.getComponentUuid());
         projectUuids.add(issue.getProjectUuid());
-        add(RULES, issue.getRuleId());
+        ruleIds.add(issue.getRuleId());
         add(USERS, issue.getAssignee());
         collectComponentsFromIssueLocations(issue);
       }
@@ -349,6 +368,14 @@ public class SearchResponseLoader {
     public Set<String> getProjectUuids() {
       return projectUuids;
     }
+
+    public Set<Integer> getRuleIds() {
+      return ruleIds;
+    }
+
+    public Set<String> getRuleKeys() {
+      return ruleKeys;
+    }
   }
 
   private static class KeyToIssueFunction implements Function<String, IssueDto> {