From 4ef9902edc622be0ca26e1bf7728016307efe422 Mon Sep 17 00:00:00 2001 From: Stephane Gamard Date: Fri, 5 Sep 2014 20:45:50 +0200 Subject: [PATCH] SONAR-5531 - Added basic facets to response (status, resolution, severity & actionPlan) --- .../org/sonar/server/issue/IssueService.java | 1 - .../sonar/server/issue/index/IssueIndex.java | 15 +++ .../sonar/server/issue/ws/SearchAction.java | 11 +- .../java/org/sonar/server/search/Result.java | 20 +++- .../server/issue/IssueServiceMediumTest.java | 111 ++++++++++++++++++ 5 files changed, 150 insertions(+), 8 deletions(-) create mode 100644 server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java index c0fe07b5e64..2965917ce45 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java @@ -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; } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java index 023c5b520fa..0040c81b973 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java @@ -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 { 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); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java index 66c0e7aed5d..0e1cc8c1113 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java @@ -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(); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/search/Result.java b/server/sonar-server/src/main/java/org/sonar/server/search/Result.java index 85a32c26cf4..84d83f448c6 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/search/Result.java +++ b/server/sonar-server/src/main/java/org/sonar/server/search/Result.java @@ -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 { + private static final Logger LOGGER = LoggerFactory.getLogger(Result.class); + private final List hits; private final Multimap facets; private final long total; @@ -60,9 +68,13 @@ public class Result { } if (response.getAggregations() != null) { for (Map.Entry 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 index 00000000000..a158e7eb1bf --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java @@ -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()); + } +} -- 2.39.5