diff options
author | Julien Lancelot <julien.lancelot@sonarsource.com> | 2017-08-29 12:07:16 +0200 |
---|---|---|
committer | Janos Gyerik <janos.gyerik@sonarsource.com> | 2017-09-12 11:34:55 +0200 |
commit | 1c4aa4402efa3038b7c400b5ed3cac2ddcb83ff2 (patch) | |
tree | 2366e8e4b957c51f68d895e82914f1136ef7ce4e | |
parent | 8e2eb6188bb66cd35642b8c21f5677887fa972f7 (diff) | |
download | sonarqube-1c4aa4402efa3038b7c400b5ed3cac2ddcb83ff2.tar.gz sonarqube-1c4aa4402efa3038b7c400b5ed3cac2ddcb83ff2.zip |
SONAR-9616 Use single ES query in api/project_branches/list
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; } } |