aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulien Lancelot <julien.lancelot@sonarsource.com>2017-08-11 19:37:42 +0200
committerJanos Gyerik <janos.gyerik@sonarsource.com>2017-09-12 10:59:56 +0200
commit50328a09b14418b47232bb75665040fc755d6b58 (patch)
tree724fc1df6b10765228f85e1e3be7765e1b742851
parent7fbdc10fe125547f2d61bfcc00aa31a63242d482 (diff)
downloadsonarqube-50328a09b14418b47232bb75665040fc755d6b58.tar.gz
sonarqube-50328a09b14418b47232bb75665040fc755d6b58.zip
SONAR-9616 Create api/projectbranches/show
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDao.java5
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchMapper.java2
-rw-r--r--server/sonar-db-dao/src/main/resources/org/sonar/db/component/BranchMapper.xml7
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/component/BranchDaoTest.java13
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/component/ComponentFinder.java10
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/projectbranch/ws/BranchWsModule.java1
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/projectbranch/ws/ListAction.java8
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/projectbranch/ws/ShowAction.java167
-rw-r--r--server/sonar-server/src/main/resources/org/sonar/server/projectbranch/ws/show-example.json8
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/projectbranch/ws/BranchWsModuleTest.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/projectbranch/ws/ListActionTest.java1
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/projectbranch/ws/ShowActionTest.java237
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/projectbranches/ProjectBranchesParameters.java3
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/projectbranches/ProjectBranchesService.java10
-rw-r--r--sonar-ws/src/main/protobuf/ws-projectbranches.proto5
-rw-r--r--sonar-ws/src/test/java/org/sonarqube/ws/client/projectbranches/ProjectBranchesServiceTest.java16
16 files changed, 489 insertions, 6 deletions
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDao.java
index 0d169e35d0a..50ff5f66deb 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDao.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDao.java
@@ -66,6 +66,11 @@ public class BranchDao implements Dao {
return executeLargeInputs(uuids, mapper(session)::selectByUuids);
}
+ public Optional<BranchDto> selectByUuid(DbSession session, String uuid) {
+ return Optional.ofNullable(mapper(session).selectByUuid(uuid));
+ }
+
+
private static BranchMapper mapper(DbSession dbSession) {
return dbSession.getMapper(BranchMapper.class);
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchMapper.java
index 2e4df028bc1..0c978360aec 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchMapper.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchMapper.java
@@ -32,6 +32,8 @@ public interface BranchMapper {
BranchDto selectByKey(@Param("projectUuid") String projectUuid,
@Param("keyType") BranchKeyType keyType, @Param("key") String key);
+ BranchDto selectByUuid(@Param("uuid") String uuid);
+
Collection<BranchDto> selectByProjectUuid(@Param("projectUuid") String projectUuid);
List<BranchDto> selectByUuids(@Param("uuids") Collection<String> uuids);
diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/BranchMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/BranchMapper.xml
index 78215959491..9c39fff0ac1 100644
--- a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/BranchMapper.xml
+++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/BranchMapper.xml
@@ -72,4 +72,11 @@
</foreach>
</select>
+ <select id="selectByUuid" resultType="org.sonar.db.component.BranchDto">
+ select <include refid="columns" />
+ from project_branches pb
+ where
+ pb.uuid = #{uuid, jdbcType=VARCHAR}
+ </select>
+
</mapper>
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/BranchDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/BranchDaoTest.java
index 86dc1db2594..616a6e1cde1 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/component/BranchDaoTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/component/BranchDaoTest.java
@@ -189,4 +189,17 @@ public class BranchDaoTest {
.containsExactlyInAnyOrder(branch1.uuid());
assertThat(underTest.selectByUuids(db.getSession(), singletonList("unknown"))).isEmpty();
}
+
+ @Test
+ public void selectByUuid() {
+ ComponentDto project = db.components().insertPrivateProject();
+ ComponentDto branch1 = db.components().insertProjectBranch(project);
+ ComponentDto branch2 = db.components().insertProjectBranch(project);
+
+ assertThat(underTest.selectByUuid(db.getSession(), branch1.uuid()).get())
+ .extracting(BranchDto::getUuid)
+ .containsExactlyInAnyOrder(branch1.uuid());
+ assertThat(underTest.selectByUuid(db.getSession(), project.uuid())).isNotPresent();
+ assertThat(underTest.selectByUuid(db.getSession(), "unknown")).isNotPresent();
+ }
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ComponentFinder.java b/server/sonar-server/src/main/java/org/sonar/server/component/ComponentFinder.java
index ec479bf2e2e..dc2f394bb11 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/component/ComponentFinder.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/component/ComponentFinder.java
@@ -105,6 +105,14 @@ public class ComponentFinder {
throw new NotFoundException(format(message, messageArguments));
}
+ private static ComponentDto checkComponent(java.util.Optional<ComponentDto> componentDto, String message, Object... messageArguments) {
+ if (componentDto.isPresent() && componentDto.get().isEnabled()) {
+ return componentDto.get();
+ }
+ throw new NotFoundException(format(message, messageArguments));
+ }
+
+
public ComponentDto getRootComponentByUuidOrKey(DbSession dbSession, @Nullable String projectUuid, @Nullable String projectKey) {
ComponentDto project;
if (projectUuid != null) {
@@ -144,7 +152,7 @@ public class ComponentFinder {
}
public ComponentDto getByKeyAndBranch(DbSession dbSession, String key, String branch) {
- return checkFoundWithOptional(dbClient.componentDao().selectByKeyAndBranch(dbSession, key, branch), "Component '%s' on branch '%s' not found", key, branch);
+ return checkComponent(dbClient.componentDao().selectByKeyAndBranch(dbSession, key, branch), "Component '%s' on branch '%s' not found", key, branch);
}
public enum ParamNames {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/projectbranch/ws/BranchWsModule.java b/server/sonar-server/src/main/java/org/sonar/server/projectbranch/ws/BranchWsModule.java
index cfee79582ef..0a0d4c2cd80 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/projectbranch/ws/BranchWsModule.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/projectbranch/ws/BranchWsModule.java
@@ -26,6 +26,7 @@ public class BranchWsModule extends Module {
protected void configureModule() {
add(
ListAction.class,
+ ShowAction.class,
BranchesWs.class);
}
}
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 4b1d5783cc4..211799777a0 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,7 +25,6 @@ import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import org.sonar.api.resources.Qualifiers;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
@@ -42,12 +41,14 @@ import org.sonar.server.ws.WsUtils;
import org.sonarqube.ws.WsBranches;
import org.sonarqube.ws.WsBranches.Branch.Status;
+import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static java.util.Arrays.asList;
import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
import static org.sonar.api.measures.CoreMetrics.BUGS_KEY;
import static org.sonar.api.measures.CoreMetrics.CODE_SMELLS_KEY;
import static org.sonar.api.measures.CoreMetrics.VULNERABILITIES_KEY;
+import static org.sonar.api.resources.Qualifiers.PROJECT;
import static org.sonar.core.util.Protobuf.setNullable;
import static org.sonar.core.util.stream.MoreCollectors.index;
import static org.sonar.core.util.stream.MoreCollectors.toList;
@@ -74,6 +75,7 @@ public class ListAction implements BranchWsAction {
.setSince("6.6")
.setDescription("List the branches of a project")
.setResponseExample(Resources.getResource(getClass(), "list-example.json"))
+ .setInternal(true)
.setHandler(this);
action
@@ -93,9 +95,7 @@ public class ListAction implements BranchWsAction {
"Project key '%s' not found", projectKey);
userSession.checkComponentPermission(UserRole.USER, project);
- if (!project.isEnabled() || !Qualifiers.PROJECT.equals(project.qualifier())) {
- throw new IllegalArgumentException("Invalid project key");
- }
+ checkArgument(project.isEnabled() && PROJECT.equals(project.qualifier()), "Invalid project key");
List<MetricDto> metrics = dbClient.metricDao().selectByKeys(dbSession, asList(ALERT_STATUS_KEY, BUGS_KEY, VULNERABILITIES_KEY, CODE_SMELLS_KEY));
Map<Integer, MetricDto> metricsById = metrics.stream().collect(uniqueIndex(MetricDto::getId));
diff --git a/server/sonar-server/src/main/java/org/sonar/server/projectbranch/ws/ShowAction.java b/server/sonar-server/src/main/java/org/sonar/server/projectbranch/ws/ShowAction.java
new file mode 100644
index 00000000000..0129706d379
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/projectbranch/ws/ShowAction.java
@@ -0,0 +1,167 @@
+/*
+ * 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.projectbranch.ws;
+
+import com.google.common.collect.Multimap;
+import com.google.common.io.Resources;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import javax.annotation.Nullable;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.web.UserRole;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.BranchDto;
+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.user.UserSession;
+import org.sonar.server.ws.WsUtils;
+import org.sonarqube.ws.WsBranches;
+import org.sonarqube.ws.WsBranches.Branch.Status;
+import org.sonarqube.ws.WsBranches.ShowWsResponse;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Arrays.asList;
+import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
+import static org.sonar.api.measures.CoreMetrics.BUGS_KEY;
+import static org.sonar.api.measures.CoreMetrics.CODE_SMELLS_KEY;
+import static org.sonar.api.measures.CoreMetrics.VULNERABILITIES_KEY;
+import static org.sonar.core.util.Protobuf.setNullable;
+import static org.sonar.core.util.stream.MoreCollectors.index;
+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.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
+import static org.sonar.server.ws.KeyExamples.KEY_FILE_EXAMPLE_001;
+import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.ACTION_SHOW;
+import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.PARAM_BRANCH;
+import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.PARAM_COMPONENT;
+
+public class ShowAction implements BranchWsAction {
+
+ private final DbClient dbClient;
+ private final UserSession userSession;
+ private final ComponentFinder componentFinder;
+
+ public ShowAction(DbClient dbClient, UserSession userSession, ComponentFinder componentFinder) {
+ this.dbClient = dbClient;
+ this.userSession = userSession;
+ this.componentFinder = componentFinder;
+ }
+
+ @Override
+ public void define(WebService.NewController context) {
+ WebService.NewAction action = context.createAction(ACTION_SHOW)
+ .setSince("6.6")
+ .setDescription("Show branch information of a project")
+ .setResponseExample(Resources.getResource(getClass(), "show-example.json"))
+ .setInternal(true)
+ .setHandler(this);
+
+ action
+ .createParam(PARAM_COMPONENT)
+ .setDescription("Component key")
+ .setExampleValue(KEY_FILE_EXAMPLE_001)
+ .setRequired(true);
+
+ action
+ .createParam(PARAM_BRANCH)
+ .setDescription("Branch key")
+ .setExampleValue(KEY_BRANCH_EXAMPLE_001)
+ .setRequired(true);
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+ String projectKey = request.mandatoryParam(PARAM_COMPONENT);
+ String branchName = request.mandatoryParam(PARAM_BRANCH);
+
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ ComponentDto component = componentFinder.getByKeyAndBranch(dbSession, projectKey, branchName);
+ userSession.checkComponentPermission(UserRole.USER, component);
+ ComponentDto project = componentFinder.getByUuid(dbSession, component.projectUuid());
+
+ List<MetricDto> metrics = dbClient.metricDao().selectByKeys(dbSession, asList(ALERT_STATUS_KEY, BUGS_KEY, VULNERABILITIES_KEY, CODE_SMELLS_KEY));
+ Map<Integer, MetricDto> metricsById = metrics.stream().collect(uniqueIndex(MetricDto::getId));
+ Map<String, Integer> metricIdsByKey = metrics.stream().collect(uniqueIndex(MetricDto::getKey, MetricDto::getId));
+
+ BranchDto branch = getBranch(dbSession, component.projectUuid());
+ String mergeBranchUuid = branch.getMergeBranchUuid();
+ BranchDto mergeBranch = mergeBranchUuid == null ? null : getBranch(dbSession, mergeBranchUuid);
+
+ Multimap<String, MeasureDto> measuresByComponentUuids = dbClient.measureDao()
+ .selectByComponentsAndMetrics(dbSession, Collections.singletonList(branch.getUuid()), metricsById.keySet())
+ .stream().collect(index(MeasureDto::getComponentUuid));
+
+ WsUtils.writeProtobuf(buildResponse(branch, project, mergeBranch, metricIdsByKey, measuresByComponentUuids), request, response);
+ }
+ }
+
+ private BranchDto getBranch(DbSession dbSession, String uuid) {
+ Optional<BranchDto> branch = dbClient.branchDao().selectByUuid(dbSession, uuid);
+ checkState(branch != null, "Branch uuid '%s' not found", uuid);
+ return branch.get();
+ }
+
+ private static ShowWsResponse buildResponse(BranchDto branch, ComponentDto project, @Nullable BranchDto mergeBranch,
+ Map<String, Integer> metricIdsByKey, Multimap<String, MeasureDto> measuresByComponentUuids) {
+ WsBranches.Branch.Builder builder = WsBranches.Branch.newBuilder();
+ setNullable(branch.getKey(), builder::setName);
+ builder.setProject(project.getKey());
+ builder.setIsMain(branch.isMain());
+ builder.setType(WsBranches.Branch.BranchType.valueOf(branch.getBranchType().name()));
+ if (mergeBranch != null) {
+ setNullable(mergeBranch.getKey(), builder::setMergeBranch);
+ }
+
+ Status.Builder statusBuilder = Status.newBuilder();
+ Collection<MeasureDto> componentMeasures = measuresByComponentUuids.get(branch.getUuid());
+ if (branch.getBranchType().equals(LONG)) {
+ int qualityGateStatusMetricId = metricIdsByKey.get(ALERT_STATUS_KEY);
+ componentMeasures.stream().filter(m -> m.getMetricId() == qualityGateStatusMetricId).findAny()
+ .ifPresent(measure -> statusBuilder.setQualityGateStatus(measure.getData()));
+ }
+
+ if (branch.getBranchType().equals(SHORT)) {
+ int bugsMetricId = metricIdsByKey.get(BUGS_KEY);
+ componentMeasures.stream().filter(m -> m.getMetricId() == bugsMetricId).findAny()
+ .ifPresent(measure -> statusBuilder.setBugs(measure.getValue().intValue()));
+
+ int vulnerabilitiesMetricId = metricIdsByKey.get(VULNERABILITIES_KEY);
+ componentMeasures.stream().filter(m -> m.getMetricId() == vulnerabilitiesMetricId).findAny()
+ .ifPresent(measure -> statusBuilder.setVulnerabilities(measure.getValue().intValue()));
+
+ int codeSmellMetricId = metricIdsByKey.get(CODE_SMELLS_KEY);
+ componentMeasures.stream().filter(m -> m.getMetricId() == codeSmellMetricId).findAny()
+ .ifPresent(measure -> statusBuilder.setCodeSmells(measure.getValue().intValue()));
+ }
+
+ builder.setStatus(statusBuilder);
+ return ShowWsResponse.newBuilder().setBranch(builder).build();
+ }
+
+}
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/projectbranch/ws/show-example.json b/server/sonar-server/src/main/resources/org/sonar/server/projectbranch/ws/show-example.json
new file mode 100644
index 00000000000..857822f4b6f
--- /dev/null
+++ b/server/sonar-server/src/main/resources/org/sonar/server/projectbranch/ws/show-example.json
@@ -0,0 +1,8 @@
+{
+ "branch": {
+ "name": "feature/bar",
+ "project": "sonarqube",
+ "isMain": false,
+ "type": "LONG"
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/projectbranch/ws/BranchWsModuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/projectbranch/ws/BranchWsModuleTest.java
index 35d8690dd29..39810004936 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/projectbranch/ws/BranchWsModuleTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/projectbranch/ws/BranchWsModuleTest.java
@@ -30,6 +30,6 @@ public class BranchWsModuleTest {
public void verify_count_of_added_components() {
ComponentContainer container = new ComponentContainer();
new BranchWsModule().configure(container);
- assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 2);
+ assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 3);
}
}
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 df17348fee6..d6fc838000b 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
@@ -80,6 +80,7 @@ public class ListActionTest {
WebService.Action definition = tester.getDef();
assertThat(definition.key()).isEqualTo("list");
assertThat(definition.isPost()).isFalse();
+ assertThat(definition.isInternal()).isTrue();
assertThat(definition.params()).extracting(WebService.Param::key).containsExactlyInAnyOrder("project");
assertThat(definition.since()).isEqualTo("6.6");
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/projectbranch/ws/ShowActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/projectbranch/ws/ShowActionTest.java
new file mode 100644
index 00000000000..e37116f1fcf
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/projectbranch/ws/ShowActionTest.java
@@ -0,0 +1,237 @@
+/*
+ * 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.projectbranch.ws;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchType;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ResourceTypesRule;
+import org.sonar.db.component.SnapshotDto;
+import org.sonar.db.metric.MetricDto;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.WsActionTester;
+import org.sonarqube.ws.WsBranches.ShowWsResponse;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
+import static org.sonar.api.measures.CoreMetrics.BUGS_KEY;
+import static org.sonar.api.measures.CoreMetrics.CODE_SMELLS_KEY;
+import static org.sonar.api.measures.CoreMetrics.VULNERABILITIES_KEY;
+import static org.sonar.api.resources.Qualifiers.PROJECT;
+import static org.sonar.db.component.ComponentTesting.newFileDto;
+import static org.sonar.test.JsonAssert.assertJson;
+import static org.sonarqube.ws.WsBranches.Branch;
+
+public class ShowActionTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Rule
+ public DbTester db = DbTester.create(System2.INSTANCE);
+
+ @Rule
+ public UserSessionRule userSession = UserSessionRule.standalone();
+
+ private ResourceTypesRule resourceTypes = new ResourceTypesRule()
+ .setRootQualifiers(PROJECT);
+
+ private MetricDto qualityGateStatus;
+ private MetricDto bugs;
+ private MetricDto vulnerabilities;
+ private MetricDto codeSmells;
+
+ public WsActionTester ws = new WsActionTester(new ShowAction(db.getDbClient(), userSession, new ComponentFinder(db.getDbClient(), resourceTypes)));
+
+ @Before
+ public void setUp() throws Exception {
+ qualityGateStatus = db.measures().insertMetric(m -> m.setKey(ALERT_STATUS_KEY));
+ bugs = db.measures().insertMetric(m -> m.setKey(BUGS_KEY));
+ vulnerabilities = db.measures().insertMetric(m -> m.setKey(VULNERABILITIES_KEY));
+ codeSmells = db.measures().insertMetric(m -> m.setKey(CODE_SMELLS_KEY));
+ }
+
+ @Test
+ public void test_definition() {
+ WebService.Action definition = ws.getDef();
+ assertThat(definition.key()).isEqualTo("show");
+ assertThat(definition.isPost()).isFalse();
+ assertThat(definition.isInternal()).isTrue();
+ assertThat(definition.params()).extracting(WebService.Param::key).containsExactlyInAnyOrder("component", "branch");
+ assertThat(definition.since()).isEqualTo("6.6");
+ }
+
+ @Test
+ public void long_living_branch() {
+ ComponentDto project = db.components().insertMainBranch();
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+ ComponentDto longLivingBranch = db.components().insertProjectBranch(project,
+ b -> b.setKey("long").setBranchType(BranchType.LONG));
+
+ ShowWsResponse response = ws.newRequest()
+ .setParam("component", longLivingBranch.getKey())
+ .setParam("branch", longLivingBranch.getBranch())
+ .executeProtobuf(ShowWsResponse.class);
+
+ assertThat(response.getBranch())
+ .extracting(Branch::getName, Branch::getProject, Branch::getType, Branch::getMergeBranch)
+ .containsExactlyInAnyOrder(longLivingBranch.getBranch(), project.getKey(), Branch.BranchType.LONG, "");
+ }
+
+ @Test
+ public void short_living_branches() {
+ ComponentDto project = db.components().insertMainBranch();
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+ ComponentDto longLivingBranch = db.components().insertProjectBranch(project,
+ b -> b.setKey("long").setBranchType(BranchType.LONG));
+ ComponentDto shortLivingBranch = db.components().insertProjectBranch(project,
+ b -> b.setKey("short").setBranchType(BranchType.SHORT).setMergeBranchUuid(longLivingBranch.uuid()));
+
+ ShowWsResponse response = ws.newRequest()
+ .setParam("component", shortLivingBranch.getKey())
+ .setParam("branch", shortLivingBranch.getBranch())
+ .executeProtobuf(ShowWsResponse.class);
+
+ assertThat(response.getBranch())
+ .extracting(Branch::getName, Branch::getProject, Branch::getType, Branch::getMergeBranch)
+ .containsExactlyInAnyOrder(shortLivingBranch.getBranch(), project.getKey(), Branch.BranchType.SHORT, longLivingBranch.getBranch());
+ }
+
+ @Test
+ public void quality_gate_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));
+ SnapshotDto branchAnalysis = db.components().insertSnapshot(branch);
+ db.measures().insertMeasure(branch, branchAnalysis, qualityGateStatus, m -> m.setData("OK"));
+
+ ShowWsResponse response = ws.newRequest()
+ .setParam("component", branch.getKey())
+ .setParam("branch", branch.getBranch())
+ .executeProtobuf(ShowWsResponse.class);
+
+ assertThat(response.getBranch())
+ .extracting(b -> b.getStatus().hasQualityGateStatus(), b -> b.getStatus().getQualityGateStatus())
+ .containsExactlyInAnyOrder(true, "OK");
+ }
+
+ @Test
+ public void bugs_vulnerabilities_and_code_smells_on_short_living_branch() {
+ ComponentDto project = db.components().insertMainBranch();
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+ ComponentDto longLivingBranch = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.LONG));
+ ComponentDto shortLivingBranch = db.components().insertProjectBranch(project,
+ b -> b.setBranchType(BranchType.SHORT).setMergeBranchUuid(longLivingBranch.uuid()));
+ SnapshotDto branchAnalysis = db.components().insertSnapshot(shortLivingBranch);
+ db.measures().insertMeasure(shortLivingBranch, branchAnalysis, bugs, m -> m.setValue(1d));
+ db.measures().insertMeasure(shortLivingBranch, branchAnalysis, vulnerabilities, m -> m.setValue(2d));
+ db.measures().insertMeasure(shortLivingBranch, branchAnalysis, codeSmells, m -> m.setValue(3d));
+
+ ShowWsResponse response = ws.newRequest()
+ .setParam("component", shortLivingBranch.getKey())
+ .setParam("branch", shortLivingBranch.getBranch())
+ .executeProtobuf(ShowWsResponse.class);
+
+
+ assertThat(response.getBranch().getStatus())
+ .extracting(Branch.Status::hasBugs, Branch.Status::getBugs, Branch.Status::hasVulnerabilities, Branch.Status::getVulnerabilities, Branch.Status::hasCodeSmells, Branch.Status::getCodeSmells)
+ .containsExactlyInAnyOrder(true, 1, true, 2, true, 3);
+ }
+
+ @Test
+ public void file() {
+ ComponentDto project = db.components().insertMainBranch();
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+ ComponentDto longLivingBranch = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.LONG));
+ ComponentDto file = db.components().insertComponent(newFileDto(longLivingBranch));
+
+ ShowWsResponse response = ws.newRequest()
+ .setParam("component", file.getKey())
+ .setParam("branch", file.getBranch())
+ .executeProtobuf(ShowWsResponse.class);
+
+ assertThat(response.getBranch())
+ .extracting(Branch::getName, Branch::getProject, Branch::getType, Branch::getMergeBranch)
+ .containsExactlyInAnyOrder(file.getBranch(), project.getKey(), Branch.BranchType.LONG, "");
+ }
+
+ @Test
+ public void fail_if_missing_component_parameter() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("The 'component' parameter is missing");
+
+ ws.newRequest()
+ .setParam("branch", "my_branch")
+ .execute();
+ }
+
+ @Test
+ public void fail_if_missing_branch_parameter() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("The 'branch' parameter is missing");
+
+ ws.newRequest()
+ .setParam("component", "my_project")
+ .execute();
+ }
+
+ @Test
+ public void fail_if_branch_does_not_exist() {
+ ComponentDto project = db.components().insertPrivateProject();
+ ComponentDto file = db.components().insertComponent(newFileDto(project));
+ userSession.addProjectPermission(UserRole.USER, project);
+ db.components().insertProjectBranch(project, b -> b.setKey("my_branch"));
+
+ expectedException.expect(NotFoundException.class);
+ expectedException.expectMessage(String.format("Component '%s' on branch '%s' not found", file.getKey(), "another_branch"));
+
+ ws.newRequest()
+ .setParam("component", file.getKey())
+ .setParam("branch", "another_branch")
+ .execute();
+ }
+
+ @Test
+ 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()));
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+
+ String json = ws.newRequest()
+ .setParam("component", longLivingBranch.getKey())
+ .setParam("branch", longLivingBranch.getBranch())
+ .execute()
+ .getInput();
+
+ assertJson(json).isSimilarTo(ws.getDef().responseExampleAsString());
+ }
+
+}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/projectbranches/ProjectBranchesParameters.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/projectbranches/ProjectBranchesParameters.java
index 9b056ef0bc7..e64477d1b11 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/projectbranches/ProjectBranchesParameters.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/projectbranches/ProjectBranchesParameters.java
@@ -25,9 +25,12 @@ public class ProjectBranchesParameters {
// actions
public static final String ACTION_LIST = "list";
+ public static final String ACTION_SHOW = "show";
// parameters
public static final String PARAM_PROJECT = "project";
+ public static final String PARAM_COMPONENT = "component";
+ public static final String PARAM_BRANCH = "branch";
private ProjectBranchesParameters() {
// static utility class
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/projectbranches/ProjectBranchesService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/projectbranches/ProjectBranchesService.java
index bb0aecba3d5..c9a5a21fc24 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/projectbranches/ProjectBranchesService.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/projectbranches/ProjectBranchesService.java
@@ -20,12 +20,15 @@
package org.sonarqube.ws.client.projectbranches;
import org.sonarqube.ws.WsBranches.ListWsResponse;
+import org.sonarqube.ws.WsBranches.ShowWsResponse;
import org.sonarqube.ws.client.BaseService;
import org.sonarqube.ws.client.GetRequest;
import org.sonarqube.ws.client.WsConnector;
import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.ACTION_LIST;
+import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.ACTION_SHOW;
import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.CONTROLLER;
+import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.PARAM_BRANCH;
import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.PARAM_PROJECT;
public class ProjectBranchesService extends BaseService {
@@ -40,4 +43,11 @@ public class ProjectBranchesService extends BaseService {
return call(get, ListWsResponse.parser());
}
+ public ShowWsResponse show(String project, String branch) {
+ GetRequest get = new GetRequest(path(ACTION_SHOW))
+ .setParam(PARAM_PROJECT, project)
+ .setParam(PARAM_BRANCH, branch);
+ return call(get, ShowWsResponse.parser());
+ }
+
}
diff --git a/sonar-ws/src/main/protobuf/ws-projectbranches.proto b/sonar-ws/src/main/protobuf/ws-projectbranches.proto
index 915e983e788..8054febfafc 100644
--- a/sonar-ws/src/main/protobuf/ws-projectbranches.proto
+++ b/sonar-ws/src/main/protobuf/ws-projectbranches.proto
@@ -30,6 +30,11 @@ message ListWsResponse {
repeated Branch branches = 1;
}
+// WS api/project_branches/show
+message ShowWsResponse {
+ optional Branch branch = 1;
+}
+
message Branch {
optional string name = 1;
optional string project = 2;
diff --git a/sonar-ws/src/test/java/org/sonarqube/ws/client/projectbranches/ProjectBranchesServiceTest.java b/sonar-ws/src/test/java/org/sonarqube/ws/client/projectbranches/ProjectBranchesServiceTest.java
index c3dbc7c581b..54bec8cd894 100644
--- a/sonar-ws/src/test/java/org/sonarqube/ws/client/projectbranches/ProjectBranchesServiceTest.java
+++ b/sonar-ws/src/test/java/org/sonarqube/ws/client/projectbranches/ProjectBranchesServiceTest.java
@@ -23,12 +23,14 @@ package org.sonarqube.ws.client.projectbranches;
import org.junit.Rule;
import org.junit.Test;
import org.sonarqube.ws.WsBranches.ListWsResponse;
+import org.sonarqube.ws.WsBranches.ShowWsResponse;
import org.sonarqube.ws.client.GetRequest;
import org.sonarqube.ws.client.ServiceTester;
import org.sonarqube.ws.client.WsConnector;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
+import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.PARAM_BRANCH;
import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.PARAM_PROJECT;
public class ProjectBranchesServiceTest {
@@ -51,4 +53,18 @@ public class ProjectBranchesServiceTest {
.andNoOtherParam();
}
+ @Test
+ public void show() {
+ underTest.show("projectKey", "my_branch");
+
+ assertThat(serviceTester.getGetParser()).isSameAs(ShowWsResponse.parser());
+
+ GetRequest getRequest = serviceTester.getGetRequest();
+ serviceTester.assertThat(getRequest)
+ .hasPath("show")
+ .hasParam(PARAM_PROJECT, "projectKey")
+ .hasParam(PARAM_BRANCH, "my_branch")
+ .andNoOtherParam();
+ }
+
}