diff options
4 files changed, 69 insertions, 21 deletions
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 aeae39e6a64..9ed81f2a73f 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 @@ -25,6 +25,7 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.function.Function; +import java.util.stream.Collectors; import javax.annotation.Nullable; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; @@ -36,6 +37,7 @@ import org.sonar.db.component.BranchDto; import org.sonar.db.component.BranchKeyType; import org.sonar.db.component.BranchType; import org.sonar.db.component.ComponentDto; +import org.sonar.db.component.SnapshotDto; import org.sonar.db.measure.MeasureDto; import org.sonar.db.metric.MetricDto; import org.sonar.server.component.ComponentFinder; @@ -50,6 +52,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static java.util.Collections.singletonList; import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY; import static org.sonar.api.resources.Qualifiers.PROJECT; +import static org.sonar.api.utils.DateUtils.formatDateTime; import static org.sonar.core.util.Protobuf.setNullable; import static org.sonar.core.util.stream.MoreCollectors.toList; import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex; @@ -110,20 +113,26 @@ public class ListAction implements BranchWsAction { .filter(b -> b.getBranchType().equals(SHORT)) .map(BranchDto::getUuid).collect(toList())) .stream().collect(uniqueIndex(BranchStatistics::getBranchUuid, Function.identity())); + Map<String, String> analysisDateByBranchUuid = dbClient.snapshotDao() + .selectLastAnalysesByRootComponentUuids(dbSession, branches.stream().map(BranchDto::getUuid).collect(Collectors.toList())) + .stream().collect(uniqueIndex(SnapshotDto::getComponentUuid, s -> formatDateTime(s.getCreatedAt()))); 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()), branchStatisticsByBranchUuid.get(b.getUuid()))); + .forEach(b -> addBranch(protobufResponse, b, mergeBranchesByUuid, qualityGateMeasuresByComponentUuids.get(b.getUuid()), branchStatisticsByBranchUuid.get(b.getUuid()), + analysisDateByBranchUuid.get(b.getUuid()))); WsUtils.writeProtobuf(protobufResponse.build(), request, response); } } private static void addBranch(WsBranches.ListWsResponse.Builder response, BranchDto branch, Map<String, BranchDto> mergeBranchesByUuid, @Nullable MeasureDto qualityGateMeasure, - BranchStatistics branchStatistics) { + BranchStatistics branchStatistics, @Nullable String analysisDate) { WsBranches.Branch.Builder builder = toBranchBuilder(branch, Optional.ofNullable(mergeBranchesByUuid.get(branch.getMergeBranchUuid()))); - setLongLivingBranchStatus(builder, branch, qualityGateMeasure); - setShortLivingBranchStatus(builder, branch, branchStatistics); + setBranchStatus(builder, branch, qualityGateMeasure, branchStatistics); + if (analysisDate != null) { + builder.setAnalysisDate(analysisDate); + } response.addBranches(builder); } @@ -144,23 +153,16 @@ public class ListAction implements BranchWsAction { return builder; } - private static void setLongLivingBranchStatus(WsBranches.Branch.Builder builder, BranchDto branch, @Nullable MeasureDto qualityGateMeasure) { - if (branch.getBranchType().equals(LONG) && qualityGateMeasure != null) { - WsBranches.Branch.Status.Builder statusBuilder = WsBranches.Branch.Status.newBuilder(); + private static void setBranchStatus(WsBranches.Branch.Builder builder, BranchDto branch, @Nullable MeasureDto qualityGateMeasure, @Nullable BranchStatistics branchStatistics) { + WsBranches.Branch.Status.Builder statusBuilder = WsBranches.Branch.Status.newBuilder(); + if (branch.getBranchType() == LONG && qualityGateMeasure != null) { statusBuilder.setQualityGateStatus(qualityGateMeasure.getData()); - builder.setStatus(statusBuilder); } - } - - private static void setShortLivingBranchStatus(WsBranches.Branch.Builder builder, BranchDto branch, @Nullable BranchStatistics branchStatistics) { - if (!branch.getBranchType().equals(BranchType.SHORT)) { - return; + if (branch.getBranchType() == BranchType.SHORT) { + statusBuilder.setBugs(branchStatistics == null ? 0L : branchStatistics.getBugs()); + statusBuilder.setVulnerabilities(branchStatistics == null ? 0L : branchStatistics.getVulnerabilities()); + statusBuilder.setCodeSmells(branchStatistics == null ? 0L : branchStatistics.getCodeSmells()); } - WsBranches.Branch.Status.Builder statusBuilder = WsBranches.Branch.Status.newBuilder(); - 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/main/resources/org/sonar/server/projectbranch/ws/list-example.json b/server/sonar-server/src/main/resources/org/sonar/server/projectbranch/ws/list-example.json index 0078ef5f40b..9d285888732 100644 --- a/server/sonar-server/src/main/resources/org/sonar/server/projectbranch/ws/list-example.json +++ b/server/sonar-server/src/main/resources/org/sonar/server/projectbranch/ws/list-example.json @@ -3,13 +3,15 @@ { "name": "feature/bar", "isMain": false, - "type": "LONG" + "type": "LONG", + "analysisDate": "2017-04-01T01:15:42+0100" }, { "name": "feature/foo", "isMain": false, "type": "SHORT", - "mergeBranch": "feature/bar" + "mergeBranch": "feature/bar", + "analysisDate": "2017-04-03T13:37:00+0100" } ] } 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 c712694835a..229936f100c 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 @@ -26,6 +26,7 @@ import org.junit.rules.ExpectedException; import org.sonar.api.config.internal.MapSettings; import org.sonar.api.resources.ResourceTypes; import org.sonar.api.server.ws.WebService; +import org.sonar.api.utils.DateUtils; import org.sonar.api.utils.System2; import org.sonar.api.web.UserRole; import org.sonar.db.DbTester; @@ -34,6 +35,7 @@ import org.sonar.db.component.ComponentDto; import org.sonar.db.component.ComponentTesting; import org.sonar.db.component.ResourceTypesRule; import org.sonar.db.component.SnapshotDto; +import org.sonar.db.component.SnapshotTesting; import org.sonar.db.metric.MetricDto; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.rule.RuleDefinitionDto; @@ -65,6 +67,8 @@ import static org.sonar.api.resources.Qualifiers.PROJECT; 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.dateToLong; +import static org.sonar.api.utils.DateUtils.parseDateTime; import static org.sonar.test.JsonAssert.assertJson; import static org.sonarqube.ws.WsBranches.Branch.Status; @@ -107,9 +111,13 @@ public class ListActionTest { public void test_example() { ComponentDto project = db.components().insertPrivateProject(p -> p.setDbKey("sonarqube")); ComponentDto longLivingBranch = db.components().insertProjectBranch(project, b -> b.setKey("feature/bar").setBranchType(BranchType.LONG)); - db.components().insertProjectBranch(project, b -> b.setKey("feature/foo").setBranchType(BranchType.SHORT).setMergeBranchUuid(longLivingBranch.uuid())); + ComponentDto shortLivingBranch = db.components().insertProjectBranch(project, b -> b.setKey("feature/foo").setBranchType(BranchType.SHORT).setMergeBranchUuid(longLivingBranch.uuid())); userSession.logIn().addProjectPermission(UserRole.USER, project); + db.getDbClient().snapshotDao().insert(db.getSession(), SnapshotTesting.newAnalysis(longLivingBranch).setLast(true).setCreatedAt(DateUtils.parseDateTime("2017-04-01T01:15:42+0100").getTime())); + db.getDbClient().snapshotDao().insert(db.getSession(), SnapshotTesting.newAnalysis(shortLivingBranch).setLast(true).setCreatedAt(DateUtils.parseDateTime("2017-04-03T13:37:00+0100").getTime())); + db.commit(); + String json = ws.newRequest() .setParam("project", project.getDbKey()) .execute() @@ -304,6 +312,41 @@ public class ListActionTest { } @Test + public void response_contains_date_of_last_analysis() { + Long lastAnalysisLongLivingBranch = dateToLong(parseDateTime("2017-04-01T00:00:00+0100")); + Long previousAnalysisShortLivingBranch = dateToLong(parseDateTime("2017-04-02T00:00:00+0100")); + Long lastAnalysisShortLivingBranch = dateToLong(parseDateTime("2017-04-03T00:00:00+0100")); + + ComponentDto project = db.components().insertMainBranch(); + userSession.logIn().addProjectPermission(UserRole.USER, project); + ComponentDto shortLivingBranch1 = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.SHORT).setMergeBranchUuid(project.uuid())); + ComponentDto longLivingBranch2 = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.LONG)); + ComponentDto shortLivingBranch2 = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.SHORT).setMergeBranchUuid(longLivingBranch2.uuid())); + db.getDbClient().snapshotDao().insert(db.getSession(), + SnapshotTesting.newAnalysis(longLivingBranch2).setCreatedAt(lastAnalysisLongLivingBranch)); + db.getDbClient().snapshotDao().insert(db.getSession(), + SnapshotTesting.newAnalysis(shortLivingBranch2).setCreatedAt(previousAnalysisShortLivingBranch).setLast(false)); + db.getDbClient().snapshotDao().insert(db.getSession(), + SnapshotTesting.newAnalysis(shortLivingBranch2).setCreatedAt(lastAnalysisShortLivingBranch)); + db.commit(); + issueIndexer.indexOnStartup(emptySet()); + permissionIndexerTester.allowOnlyAnyone(project); + + ListWsResponse response = ws.newRequest() + .setParam("project", project.getKey()) + .executeProtobuf(ListWsResponse.class); + + assertThat(response.getBranchesList()) + .extracting(WsBranches.Branch::getType, WsBranches.Branch::hasAnalysisDate, b -> "".equals(b.getAnalysisDate()) ? null : dateToLong(parseDateTime(b.getAnalysisDate()))) + .containsExactlyInAnyOrder( + tuple(Common.BranchType.LONG, false, null), + tuple(Common.BranchType.SHORT, false, null), + tuple(Common.BranchType.LONG, true, lastAnalysisLongLivingBranch), + tuple(Common.BranchType.SHORT, true, lastAnalysisShortLivingBranch) + ); + } + + @Test public void fail_when_using_branch_db_key() throws Exception { OrganizationDto organization = db.organizations().insert(); ComponentDto project = db.components().insertMainBranch(organization); diff --git a/sonar-ws/src/main/protobuf/ws-projectbranches.proto b/sonar-ws/src/main/protobuf/ws-projectbranches.proto index bfeb635b5ea..d4b2a183e4e 100644 --- a/sonar-ws/src/main/protobuf/ws-projectbranches.proto +++ b/sonar-ws/src/main/protobuf/ws-projectbranches.proto @@ -44,6 +44,7 @@ message Branch { optional string mergeBranch = 4; optional Status status = 5; optional bool isOrphan = 6; + optional string analysisDate = 7; message Status { // Quality gate status is only present for long living branch |