aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulien Lancelot <julien.lancelot@sonarsource.com>2017-08-29 12:07:16 +0200
committerJanos Gyerik <janos.gyerik@sonarsource.com>2017-09-12 11:34:55 +0200
commit1c4aa4402efa3038b7c400b5ed3cac2ddcb83ff2 (patch)
tree2366e8e4b957c51f68d895e82914f1136ef7ce4e
parent8e2eb6188bb66cd35642b8c21f5677887fa972f7 (diff)
downloadsonarqube-1c4aa4402efa3038b7c400b5ed3cac2ddcb83ff2.tar.gz
sonarqube-1c4aa4402efa3038b7c400b5ed3cac2ddcb83ff2.zip
SONAR-9616 Use single ES query in api/project_branches/list
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/index/BranchStatistics.java63
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java56
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/projectbranch/ws/ListAction.java34
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexTest.java64
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/projectbranch/ws/ListActionTest.java28
-rw-r--r--sonar-ws/src/main/protobuf/ws-projectbranches.proto6
6 files changed, 205 insertions, 46 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/index/BranchStatistics.java b/server/sonar-server/src/main/java/org/sonar/server/issue/index/BranchStatistics.java
new file mode 100644
index 00000000000..140ceb0b119
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/index/BranchStatistics.java
@@ -0,0 +1,63 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.index;
+
+import java.util.Map;
+import org.sonar.api.rules.RuleType;
+
+import static org.sonar.api.rules.RuleType.BUG;
+import static org.sonar.api.rules.RuleType.CODE_SMELL;
+import static org.sonar.api.rules.RuleType.VULNERABILITY;
+
+public class BranchStatistics {
+
+ private final String branchUuid;
+ private final long bugs;
+ private final long vulnerabilities;
+ private final long codeSmells;
+
+ public BranchStatistics(String branchUuid, Map<String, Long> issueCountByType) {
+ this.branchUuid = branchUuid;
+ this.bugs = getNonNullValue(issueCountByType, BUG);
+ this.vulnerabilities = getNonNullValue(issueCountByType, VULNERABILITY);
+ this.codeSmells = getNonNullValue(issueCountByType, CODE_SMELL);
+ }
+
+ public String getBranchUuid() {
+ return branchUuid;
+ }
+
+ public long getBugs() {
+ return bugs;
+ }
+
+ public long getVulnerabilities() {
+ return vulnerabilities;
+ }
+
+ public long getCodeSmells() {
+ return codeSmells;
+ }
+
+ private static long getNonNullValue(Map<String, Long> issueCountByType, RuleType type) {
+ Long value = issueCountByType.get(type.name());
+ return value == null ? 0L : value;
+ }
+}
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 5099362e291..78a7dc1a53c 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
@@ -50,6 +50,8 @@ import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuil
import org.elasticsearch.search.aggregations.bucket.filter.InternalFilter;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
import org.elasticsearch.search.aggregations.bucket.histogram.ExtendedBounds;
+import org.elasticsearch.search.aggregations.bucket.terms.InternalTerms;
+import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms.Order;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
@@ -86,6 +88,7 @@ import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
import static org.elasticsearch.index.query.QueryBuilders.rangeQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;
+import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
import static org.sonar.server.es.BaseDoc.epochMillisToEpochSeconds;
import static org.sonar.server.es.EsUtils.escapeSpecialRegexChars;
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_ORGANIZATION_UUID;
@@ -251,7 +254,7 @@ public class IssueIndex {
}
private static void addSimpleStickyFacetIfNeeded(SearchOptions options, StickyFacetBuilder stickyFacetBuilder, SearchRequestBuilder esSearch,
- String facetName, String fieldName, Object... selectedValues) {
+ String facetName, String fieldName, Object... selectedValues) {
if (options.getFacets().contains(facetName)) {
esSearch.addAggregation(stickyFacetBuilder.buildStickyFacet(fieldName, facetName, DEFAULT_FACET_SIZE, selectedValues));
}
@@ -545,7 +548,7 @@ public class IssueIndex {
.timeZone(DateTimeZone.forTimeZone(TimeZone.getTimeZone("GMT")))
.offset(offsetInSeconds + "s")
// ES dateHistogram bounds are inclusive while createdBefore parameter is exclusive
- .extendedBounds(new ExtendedBounds(startTime, endTime - (offsetInSeconds*1_000L) -1L));
+ .extendedBounds(new ExtendedBounds(startTime, endTime - (offsetInSeconds * 1_000L) - 1L));
addEffortAggregationIfNeeded(query, dateHistogram);
return Optional.of(dateHistogram);
}
@@ -678,8 +681,7 @@ public class IssueIndex {
.setQuery(
boolQuery()
.mustNot(existsQuery(IssueIndexDefinition.FIELD_ISSUE_RESOLUTION))
- .filter(termQuery(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE, assignee))
- )
+ .filter(termQuery(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE, assignee)))
.setSize(0);
IntStream.range(0, projectUuids.size()).forEach(i -> {
String projectUuid = projectUuids.get(i);
@@ -688,22 +690,46 @@ public class IssueIndex {
.addAggregation(AggregationBuilders
.filter(projectUuid, boolQuery()
.filter(termQuery(IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID, projectUuid))
- .filter(rangeQuery(IssueIndexDefinition.FIELD_ISSUE_FUNC_CREATED_AT).gte(epochMillisToEpochSeconds(from)))
- )
+ .filter(rangeQuery(IssueIndexDefinition.FIELD_ISSUE_FUNC_CREATED_AT).gte(epochMillisToEpochSeconds(from))))
.subAggregation(AggregationBuilders.count(projectUuid + "_count").field(IssueIndexDefinition.FIELD_ISSUE_KEY))
- .subAggregation(AggregationBuilders.max(projectUuid + "_maxFuncCreatedAt").field(IssueIndexDefinition.FIELD_ISSUE_FUNC_CREATED_AT))
- );
+ .subAggregation(AggregationBuilders.max(projectUuid + "_maxFuncCreatedAt").field(IssueIndexDefinition.FIELD_ISSUE_FUNC_CREATED_AT)));
});
SearchResponse response = request.get();
return response.getAggregations().asList().stream()
.map(x -> (InternalFilter) x)
.flatMap(projectBucket -> {
- long count = ((InternalValueCount) projectBucket.getAggregations().get(projectBucket.getName() + "_count")).getValue();
- if (count < 1L) {
- return Stream.empty();
- }
- long lastIssueDate = (long) ((InternalMax) projectBucket.getAggregations().get(projectBucket.getName() + "_maxFuncCreatedAt")).getValue();
- return Stream.of(new ProjectStatistics(projectBucket.getName(), count, lastIssueDate));
- }).collect(MoreCollectors.toList(projectUuids.size()));
+ long count = ((InternalValueCount) projectBucket.getAggregations().get(projectBucket.getName() + "_count")).getValue();
+ if (count < 1L) {
+ return Stream.empty();
+ }
+ long lastIssueDate = (long) ((InternalMax) projectBucket.getAggregations().get(projectBucket.getName() + "_maxFuncCreatedAt")).getValue();
+ return Stream.of(new ProjectStatistics(projectBucket.getName(), count, lastIssueDate));
+ }).collect(MoreCollectors.toList(projectUuids.size()));
}
+
+ public List<BranchStatistics> searchBranchStatistics(List<String> branchUuids) {
+ if (branchUuids.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ SearchRequestBuilder request = client.prepareSearch(IssueIndexDefinition.INDEX_TYPE_ISSUE)
+ .setQuery(
+ boolQuery()
+ .must(termsQuery(IssueIndexDefinition.FIELD_ISSUE_BRANCH_UUID, branchUuids))
+ .must(termQuery(IssueIndexDefinition.FIELD_ISSUE_IS_MAIN_BRANCH, Boolean.toString(false))))
+ .setSize(0)
+ .addAggregation(AggregationBuilders.terms("branchUuids")
+ .field(IssueIndexDefinition.FIELD_ISSUE_BRANCH_UUID)
+ .size(branchUuids.size())
+ .subAggregation(AggregationBuilders.terms("types")
+ .field(IssueIndexDefinition.FIELD_ISSUE_TYPE)));
+ SearchResponse response = request.get();
+ return ((StringTerms) response.getAggregations().get("branchUuids")).getBuckets().stream()
+ .map(bucket -> new BranchStatistics(bucket.getKeyAsString(),
+ ((StringTerms) bucket.getAggregations().get("types")).getBuckets()
+ .stream()
+ .collect(uniqueIndex(StringTerms.Bucket::getKeyAsString, InternalTerms.Bucket::getDocCount))))
+ .collect(MoreCollectors.toList(branchUuids.size()));
+ }
+
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/projectbranch/ws/ListAction.java b/server/sonar-server/src/main/java/org/sonar/server/projectbranch/ws/ListAction.java
index 05595f72c00..fd176f09b8a 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/projectbranch/ws/ListAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/projectbranch/ws/ListAction.java
@@ -24,8 +24,8 @@ import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
+import java.util.function.Function;
import javax.annotation.Nullable;
-import org.elasticsearch.action.search.SearchResponse;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
@@ -39,9 +39,7 @@ import org.sonar.db.component.ComponentDto;
import org.sonar.db.measure.MeasureDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.server.component.ComponentFinder;
-import org.sonar.server.es.Facets;
-import org.sonar.server.es.SearchOptions;
-import org.sonar.server.issue.IssueQuery;
+import org.sonar.server.issue.index.BranchStatistics;
import org.sonar.server.issue.index.IssueIndex;
import org.sonar.server.user.UserSession;
import org.sonar.server.ws.WsUtils;
@@ -57,11 +55,7 @@ import static org.sonar.core.util.stream.MoreCollectors.toList;
import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
import static org.sonar.db.component.BranchType.LONG;
import static org.sonar.db.component.BranchType.SHORT;
-import static org.sonar.server.rule.index.RuleIndex.FACET_TYPES;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
-import static org.sonarqube.ws.Common.RuleType.BUG;
-import static org.sonarqube.ws.Common.RuleType.CODE_SMELL;
-import static org.sonarqube.ws.Common.RuleType.VULNERABILITY;
import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.ACTION_LIST;
import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.PARAM_PROJECT;
@@ -112,19 +106,24 @@ public class ListAction implements BranchWsAction {
Map<String, MeasureDto> qualityGateMeasuresByComponentUuids = dbClient.measureDao()
.selectByComponentsAndMetrics(dbSession, branches.stream().map(BranchDto::getUuid).collect(toList()), singletonList(qualityGateMetric.getId()))
.stream().collect(uniqueIndex(MeasureDto::getComponentUuid));
+ Map<String, BranchStatistics> branchStatisticsByBranchUuid = issueIndex.searchBranchStatistics(branches.stream()
+ .filter(b -> b.getBranchType().equals(SHORT))
+ .map(BranchDto::getUuid).collect(toList()))
+ .stream().collect(uniqueIndex(BranchStatistics::getBranchUuid, Function.identity()));
WsBranches.ListWsResponse.Builder protobufResponse = WsBranches.ListWsResponse.newBuilder();
branches.stream()
.filter(b -> b.getKeeType().equals(BranchKeyType.BRANCH))
- .forEach(b -> addBranch(protobufResponse, b, mergeBranchesByUuid, qualityGateMeasuresByComponentUuids.get(b.getUuid())));
+ .forEach(b -> addBranch(protobufResponse, b, mergeBranchesByUuid, qualityGateMeasuresByComponentUuids.get(b.getUuid()), branchStatisticsByBranchUuid.get(b.getUuid())));
WsUtils.writeProtobuf(protobufResponse.build(), request, response);
}
}
- private void addBranch(WsBranches.ListWsResponse.Builder response, BranchDto branch, Map<String, BranchDto> mergeBranchesByUuid, @Nullable MeasureDto qualityGateMeasure) {
+ private static void addBranch(WsBranches.ListWsResponse.Builder response, BranchDto branch, Map<String, BranchDto> mergeBranchesByUuid, @Nullable MeasureDto qualityGateMeasure,
+ BranchStatistics branchStatistics) {
WsBranches.Branch.Builder builder = toBranchBuilder(branch, Optional.ofNullable(mergeBranchesByUuid.get(branch.getMergeBranchUuid())));
setLongLivingBranchStatus(builder, branch, qualityGateMeasure);
- setShortLivingBranchStatus(builder, branch);
+ setShortLivingBranchStatus(builder, branch, branchStatistics);
response.addBranches(builder);
}
@@ -153,19 +152,14 @@ public class ListAction implements BranchWsAction {
}
}
- private void setShortLivingBranchStatus(WsBranches.Branch.Builder builder, BranchDto branch) {
+ private static void setShortLivingBranchStatus(WsBranches.Branch.Builder builder, BranchDto branch, @Nullable BranchStatistics branchStatistics) {
if (!branch.getBranchType().equals(BranchType.SHORT)) {
return;
}
- SearchResponse searchResponse = issueIndex.search(IssueQuery.builder().branchUuid(branch.getUuid()).build(), new SearchOptions()
- .setLimit(0)
- .addFacets(FACET_TYPES));
- Facets facets = new Facets(searchResponse);
- Map<String, Long> typesFacet = Objects.requireNonNull(facets.get(FACET_TYPES), "Facet types does not exists");
WsBranches.Branch.Status.Builder statusBuilder = WsBranches.Branch.Status.newBuilder();
- setNullable(typesFacet.get(BUG.name()), v -> statusBuilder.setBugs(v.intValue()));
- setNullable(typesFacet.get(VULNERABILITY.name()), v -> statusBuilder.setVulnerabilities(v.intValue()));
- setNullable(typesFacet.get(CODE_SMELL.name()), v -> statusBuilder.setCodeSmells(v.intValue()));
+ statusBuilder.setBugs(branchStatistics == null ? 0L : branchStatistics.getBugs());
+ statusBuilder.setVulnerabilities(branchStatistics == null ? 0L : branchStatistics.getVulnerabilities());
+ statusBuilder.setCodeSmells(branchStatistics == null ? 0L : branchStatistics.getCodeSmells());
builder.setStatus(statusBuilder);
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexTest.java
index c68a2aecfc5..939a8181059 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexTest.java
@@ -19,8 +19,8 @@
*/
package org.sonar.server.issue.index;
-import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterators;
import java.util.ArrayList;
import java.util.Arrays;
@@ -29,7 +29,9 @@ import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.stream.Collectors;
+import java.util.stream.IntStream;
import org.assertj.core.api.Fail;
+import org.assertj.core.groups.Tuple;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.search.SearchHit;
import org.junit.Before;
@@ -68,8 +70,12 @@ import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
+import static org.assertj.core.api.Assertions.tuple;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import static org.sonar.api.rules.RuleType.BUG;
+import static org.sonar.api.rules.RuleType.CODE_SMELL;
+import static org.sonar.api.rules.RuleType.VULNERABILITY;
import static org.sonar.api.utils.DateUtils.addDays;
import static org.sonar.api.utils.DateUtils.parseDate;
import static org.sonar.api.utils.DateUtils.parseDateTime;
@@ -1287,14 +1293,66 @@ public class IssueIndexTest {
newDoc("issue2", project).setTags(ImmutableSet.of("convention", "bug")),
newDoc("issue3", project).setTags(emptyList()),
newDoc("issue4", project).setTags(ImmutableSet.of("convention", "java8", "bug")).setResolution(Issue.RESOLUTION_FIXED),
- newDoc("issue5", project).setTags(ImmutableSet.of("convention"))
- );
+ newDoc("issue5", project).setTags(ImmutableSet.of("convention")));
assertThat(underTest.countTags(projectQuery(project.uuid()), 5)).containsOnly(entry("convention", 3L), entry("bug", 2L), entry("java8", 1L));
assertThat(underTest.countTags(projectQuery(project.uuid()), 2)).contains(entry("convention", 3L), entry("bug", 2L)).doesNotContainEntry("java8", 1L);
assertThat(underTest.countTags(projectQuery("other"), 10)).isEmpty();
}
+ @Test
+ public void searchBranchStatistics() {
+ ComponentDto project = db.components().insertMainBranch();
+ ComponentDto branch1 = db.components().insertProjectBranch(project);
+ ComponentDto branch2 = db.components().insertProjectBranch(project);
+ ComponentDto branch3 = db.components().insertProjectBranch(project);
+ ComponentDto fileOnBranch3 = db.components().insertComponent(newFileDto(branch3));
+ indexIssues(newDoc(project),
+ newDoc(branch1).setType(BUG), newDoc(branch1).setType(VULNERABILITY), newDoc(branch1).setType(CODE_SMELL),
+ newDoc(branch3).setType(CODE_SMELL), newDoc(branch3).setType(CODE_SMELL), newDoc(fileOnBranch3).setType(CODE_SMELL));
+
+ List<BranchStatistics> branchStatistics = underTest.searchBranchStatistics(asList(branch1.uuid(), branch2.uuid(), branch3.uuid()));
+
+ assertThat(branchStatistics).extracting(BranchStatistics::getBranchUuid, BranchStatistics::getBugs, BranchStatistics::getVulnerabilities, BranchStatistics::getCodeSmells)
+ .containsExactlyInAnyOrder(
+ tuple(branch1.uuid(), 1L, 1L, 1L),
+ tuple(branch3.uuid(), 0L, 0L, 3L));
+ }
+
+ @Test
+ public void searchBranchStatistics_on_many_branches() {
+ ComponentDto project = db.components().insertMainBranch();
+ List<String> branchUuids = new ArrayList<>();
+ List<Tuple> expectedResult = new ArrayList<>();
+ IntStream.range(0, 15).forEach(i -> {
+ ComponentDto branch = db.components().insertProjectBranch(project);
+ addIssues(branch, 1 + i, 2 + i, 3 + i);
+ expectedResult.add(tuple(branch.uuid(), 1L + i, 2L + i, 3L + i));
+ branchUuids.add(branch.uuid());
+ });
+
+ List<BranchStatistics> branchStatistics = underTest.searchBranchStatistics(branchUuids);
+
+ assertThat(branchStatistics)
+ .extracting(BranchStatistics::getBranchUuid, BranchStatistics::getBugs, BranchStatistics::getVulnerabilities, BranchStatistics::getCodeSmells)
+ .hasSize(15)
+ .containsAll(expectedResult);
+ }
+
+ @Test
+ public void searchBranchStatistics_on_empty_list() {
+ assertThat(underTest.searchBranchStatistics(emptyList())).isEmpty();
+ assertThat(underTest.searchBranchStatistics(singletonList("unknown"))).isEmpty();
+ }
+
+ private void addIssues(ComponentDto branch, int bugs, int vulnerabilities, int codeSmelles) {
+ List<IssueDoc> issues = new ArrayList<>();
+ IntStream.range(0, bugs).forEach(b -> issues.add(newDoc(branch).setType(BUG)));
+ IntStream.range(0, vulnerabilities).forEach(v -> issues.add(newDoc(branch).setType(VULNERABILITY)));
+ IntStream.range(0, codeSmelles).forEach(c -> issues.add(newDoc(branch).setType(CODE_SMELL)));
+ indexIssues(issues.toArray(new IssueDoc[issues.size()]));
+ }
+
private IssueQuery projectQuery(String projectUuid) {
return IssueQuery.builder().projectUuids(singletonList(projectUuid)).resolved(false).build();
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/projectbranch/ws/ListActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/projectbranch/ws/ListActionTest.java
index ba6722e6247..bf11eae5f34 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/projectbranch/ws/ListActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/projectbranch/ws/ListActionTest.java
@@ -236,7 +236,7 @@ public class ListActionTest {
}
@Test
- public void quality_gate_status_on_long_living_branch() {
+ public void status_on_long_living_branch() {
ComponentDto project = db.components().insertMainBranch();
userSession.logIn().addProjectPermission(UserRole.USER, project);
ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.LONG));
@@ -253,7 +253,7 @@ public class ListActionTest {
}
@Test
- public void bugs_vulnerabilities_and_code_smells_on_short_living_branch() {
+ public void status_on_short_living_branches() {
ComponentDto project = db.components().insertMainBranch();
userSession.logIn().addProjectPermission(UserRole.USER, project);
ComponentDto longLivingBranch = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.LONG));
@@ -276,9 +276,27 @@ public class ListActionTest {
assertThat(response.getBranchesList().stream().map(WsBranches.Branch::getStatus))
.extracting(Status::hasBugs, Status::getBugs, Status::hasVulnerabilities, Status::getVulnerabilities, Status::hasCodeSmells, Status::getCodeSmells)
.containsExactlyInAnyOrder(
- tuple(false, 0, false, 0, false, 0),
- tuple(false, 0, false, 0, false, 0),
- tuple(true, 1, true, 2, true, 3));
+ tuple(false, 0L, false, 0L, false, 0L),
+ tuple(false, 0L, false, 0L, false, 0L),
+ tuple(true, 1L, true, 2L, true, 3L));
+ }
+
+ @Test
+ public void status_on_short_living_branch_with_no_issue() {
+ ComponentDto project = db.components().insertMainBranch();
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+ ComponentDto longLivingBranch = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.LONG));
+ db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.SHORT).setMergeBranchUuid(longLivingBranch.uuid()));
+ issueIndexer.indexOnStartup(emptySet());
+ permissionIndexerTester.allowOnlyAnyone(project);
+
+ ListWsResponse response = ws.newRequest()
+ .setParam("project", project.getKey())
+ .executeProtobuf(ListWsResponse.class);
+
+ assertThat(response.getBranchesList().stream().filter(b -> b.getType().equals(Common.BranchType.SHORT)).map(WsBranches.Branch::getStatus))
+ .extracting(Status::getBugs, Status::getVulnerabilities, Status::getCodeSmells)
+ .containsExactlyInAnyOrder(tuple(0L, 0L, 0L));
}
@Test
diff --git a/sonar-ws/src/main/protobuf/ws-projectbranches.proto b/sonar-ws/src/main/protobuf/ws-projectbranches.proto
index a5c67745885..bfeb635b5ea 100644
--- a/sonar-ws/src/main/protobuf/ws-projectbranches.proto
+++ b/sonar-ws/src/main/protobuf/ws-projectbranches.proto
@@ -49,9 +49,9 @@ message Branch {
// Quality gate status is only present for long living branch
optional string qualityGateStatus = 1;
// Merge bugs, vulnerabilities and codeSmell are only present for short living branch
- optional int32 bugs = 2;
- optional int32 vulnerabilities = 3;
- optional int32 codeSmells = 4;
+ optional int64 bugs = 2;
+ optional int64 vulnerabilities = 3;
+ optional int64 codeSmells = 4;
}
}