]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5531 - Added basic facets to response (status, resolution, severity & actionPlan)
authorStephane Gamard <stephane.gamard@sonarsource.com>
Fri, 5 Sep 2014 18:45:50 +0000 (20:45 +0200)
committerStephane Gamard <stephane.gamard@sonarsource.com>
Fri, 5 Sep 2014 19:00:39 +0000 (21:00 +0200)
server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java
server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java
server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java
server/sonar-server/src/main/java/org/sonar/server/search/Result.java
server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java [new file with mode: 0644]

index c0fe07b5e64b7fd651fae6538fb328188ba75dab..2965917ce45b21f589edbf8551db294cc47b7687 100644 (file)
@@ -346,7 +346,6 @@ public class IssueService implements ServerComponent {
       (options.getOffset() * options.getLimit()) + 1,
       new Long(esResults.getHits().getTotalHits()).intValue()));
 
-    // TODO Implement the logic of search here!!!
     return results;
   }
 }
index 023c5b520fad99fbb455ef864c4cdb56945d666b..0040c81b9735ea72dff043875b6a41eddfb25e6d 100644 (file)
@@ -30,6 +30,7 @@ import org.elasticsearch.index.query.BoolFilterBuilder;
 import org.elasticsearch.index.query.FilterBuilders;
 import org.elasticsearch.index.query.QueryBuilder;
 import org.elasticsearch.index.query.QueryBuilders;
+import org.elasticsearch.search.aggregations.AggregationBuilders;
 import org.sonar.api.issue.IssueQuery;
 import org.sonar.core.issue.db.IssueDto;
 import org.sonar.server.search.BaseIndex;
@@ -177,6 +178,20 @@ public class IssueIndex extends BaseIndex<IssueDoc, IssueDto, String> {
       esSearch.setQuery(esQuery);
     }
 
+    // Execute Term aggregations
+    esSearch.addAggregation(AggregationBuilders.terms(IssueNormalizer.IssueField.SEVERITY.field())
+      .field(IssueNormalizer.IssueField.SEVERITY.field()));
+    esSearch.addAggregation(AggregationBuilders.terms(IssueNormalizer.IssueField.STATUS.field())
+      .field(IssueNormalizer.IssueField.STATUS.field()));
+    esSearch.addAggregation(AggregationBuilders.terms(IssueNormalizer.IssueField.RESOLUTION.field())
+      .field(IssueNormalizer.IssueField.RESOLUTION.field()));
+    esSearch.addAggregation(AggregationBuilders.terms(IssueNormalizer.IssueField.ACTION_PLAN.field())
+      .field(IssueNormalizer.IssueField.ACTION_PLAN.field()));
+
+    // Execute Function aggregation
+    esSearch.addAggregation(AggregationBuilders.sum("totalDuration")
+      .field(IssueNormalizer.IssueField.DEBT.field()));
+
     return getClient().execute(esSearch);
   }
 
index 66c0e7aed5d6f968178d2d52b206bbfda08bac26..0e1cc8c1113f6203154dd8924f4b4d8b1d112243 100644 (file)
@@ -211,11 +211,16 @@ public class SearchAction implements RequestHandler {
 
     writePaging(results, json);
     writeIssues(results, request.paramAsStrings(EXTRA_FIELDS_PARAM), json);
+
+    // TODO normalize component name for snippet -- Mighty change over time (file move)
     writeComponents(results, json);
+    // TODO normalize project Name for snippet -- Carefull might change over time (Project Rename)
     writeProjects(results, json);
-    writeRules(results, json);
-    writeUsers(results, json);
-    writeActionPlans(results, json);
+
+    // TODO Not certain that this is required (Legacy)
+    // writeRules(results, json);
+    // writeUsers(results, json);
+    // writeActionPlans(results, json);
 
     json.endObject().close();
   }
index 85a32c26cf4c887f594222e7752e13f185f60842..84d83f448c6f9a7aee2efd169fb5fe4eda6211d1 100644 (file)
@@ -27,14 +27,22 @@ import org.elasticsearch.action.search.SearchResponse;
 import org.elasticsearch.search.SearchHit;
 import org.elasticsearch.search.aggregations.Aggregation;
 import org.elasticsearch.search.aggregations.bucket.terms.Terms;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
 
 public class Result<K> {
 
+  private static final Logger LOGGER = LoggerFactory.getLogger(Result.class);
+
   private final List<K> hits;
   private final Multimap<String, FacetValue> facets;
   private final long total;
@@ -60,9 +68,13 @@ public class Result<K> {
     }
     if (response.getAggregations() != null) {
       for (Map.Entry<String, Aggregation> facet : response.getAggregations().asMap().entrySet()) {
-        Terms aggregation = (Terms) facet.getValue();
-        for (Terms.Bucket value : aggregation.getBuckets()) {
-          this.facets.put(facet.getKey(), new FacetValue(value.getKey(), (int) value.getDocCount()));
+        if (Terms.class.isAssignableFrom(facet.getValue().getClass())) {
+          Terms aggregation = (Terms) facet.getValue();
+          for (Terms.Bucket value : aggregation.getBuckets()) {
+            this.facets.put(facet.getKey(), new FacetValue(value.getKey(), (int) value.getDocCount()));
+          }
+        } else {
+          LOGGER.warn("Cannot process {} type of aggregation", facet.getValue().getClass());
         }
       }
     }
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java
new file mode 100644 (file)
index 0000000..a158e7e
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.issue;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.sonar.api.issue.IssueQuery;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.core.component.ComponentDto;
+import org.sonar.core.issue.db.IssueDto;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.rule.RuleDto;
+import org.sonar.server.component.persistence.ComponentDao;
+import org.sonar.server.db.DbClient;
+import org.sonar.server.issue.db.IssueDao;
+import org.sonar.server.issue.index.IssueResult;
+import org.sonar.server.rule.RuleTesting;
+import org.sonar.server.rule.db.RuleDao;
+import org.sonar.server.search.QueryOptions;
+import org.sonar.server.tester.ServerTester;
+
+import java.util.UUID;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class IssueServiceMediumTest {
+
+  @ClassRule
+  public static ServerTester tester = new ServerTester();
+
+  DbClient db;
+  DbSession session;
+  IssueService service;
+
+  RuleDto rule;
+  ComponentDto project;
+  ComponentDto resource;
+
+  @Before
+  public void setUp() throws Exception {
+    tester.clearDbAndIndexes();
+    db = tester.get(DbClient.class);
+    session = db.openSession(false);
+    service = tester.get(IssueService.class);
+
+    rule = RuleTesting.newXooX1();
+    tester.get(RuleDao.class).insert(session, rule);
+
+    project = new ComponentDto()
+      .setId(1L)
+      .setKey("MyProject")
+      .setProjectId(1L);
+    tester.get(ComponentDao.class).insert(session, project);
+
+    resource = new ComponentDto()
+      .setProjectId(1L)
+      .setKey("MyComponent")
+      .setId(2L);
+    tester.get(ComponentDao.class).insert(session, resource);
+  }
+
+  @After
+  public void after() {
+    session.close();
+  }
+
+  @Test
+  public void has_facets() throws Exception {
+    IssueDto issue1 = getIssue().setActionPlanKey("P1");
+    IssueDto issue2 = getIssue().setActionPlanKey("P2");
+    tester.get(IssueDao.class).insert(session, issue1, issue2);
+    session.commit();
+
+    IssueResult result = service.search(IssueQuery.builder().build(), new QueryOptions());
+    assertThat(result.getHits()).hasSize(2);
+    assertThat(result.getFacets().keySet()).hasSize(4);
+    assertThat(result.getFacetKeys("actionPlan")).hasSize(2);
+  }
+
+  private IssueDto getIssue() {
+    return new IssueDto()
+      .setIssueCreationDate(DateUtils.parseDate("2014-09-04"))
+      .setIssueUpdateDate(DateUtils.parseDate("2014-12-04"))
+      .setRule(rule)
+      .setDebt(10L)
+      .setRootComponent(project)
+      .setComponent(resource)
+      .setStatus("OPEN").setResolution("OPEN")
+      .setSeverity("MAJOR")
+      .setKee(UUID.randomUUID().toString());
+  }
+}