aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>2019-01-28 17:36:41 +0100
committersonartech <sonartech@sonarsource.com>2019-02-11 09:11:49 +0100
commit58c24e60f03e23a0c83273bc14c535ba5f18dba8 (patch)
tree69f8570f86823e6e3ec34dbbfba667dc67fa5b20
parentf6dbe525b57586765c3f17aba7b4c04788e247e2 (diff)
downloadsonarqube-58c24e60f03e23a0c83273bc14c535ba5f18dba8.tar.gz
sonarqube-58c24e60f03e23a0c83273bc14c535ba5f18dba8.zip
SONAR-11628 add api/project_analyses/unset_baseline
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDbTester.java20
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ProjectAnalysisModule.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/UnsetBaselineAction.java112
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/projectanalysis/ProjectAnalysisModuleTest.java3
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/projectanalysis/ws/UnsetBaselineActionTest.java283
5 files changed, 420 insertions, 2 deletions
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDbTester.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDbTester.java
index d0bd3db10ba..d8ed910582f 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDbTester.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDbTester.java
@@ -306,6 +306,26 @@ public class ComponentDbTester {
return branch;
}
+ public final void setManualBaseline(ComponentDto longOrMainBranchOfProject, SnapshotDto analysis) {
+ checkArgument(longOrMainBranchOfProject.isRoot());
+
+ BranchDto branchDto = db.getDbClient().branchDao().selectByUuid(dbSession, longOrMainBranchOfProject.uuid())
+ .orElseThrow(() -> new IllegalArgumentException("BranchDto not found for component " + longOrMainBranchOfProject));
+ checkArgument(branchDto.getBranchType() == LONG, "must be a main or a Long Living branch");
+ db.getDbClient().branchDao().updateManualBaseline(dbSession, longOrMainBranchOfProject.uuid(), analysis.getUuid());
+ db.commit();
+ }
+
+ public final void unsetManualBaseline(ComponentDto longOrMainBranchOfProject) {
+ checkArgument(longOrMainBranchOfProject.isRoot());
+
+ BranchDto branchDto = db.getDbClient().branchDao().selectByUuid(dbSession, longOrMainBranchOfProject.uuid())
+ .orElseThrow(() -> new IllegalArgumentException("BranchDto not found for component " + longOrMainBranchOfProject));
+ checkArgument(branchDto.getBranchType() == LONG, "must be a main or a Long Living branch");
+ db.getDbClient().branchDao().updateManualBaseline(dbSession, longOrMainBranchOfProject.uuid(), null);
+ db.commit();
+ }
+
private static <T> T firstNonNull(@Nullable T first, T second) {
return (first != null) ? first : second;
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ProjectAnalysisModule.java b/server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ProjectAnalysisModule.java
index c5cd2282b3c..bf6642e45b4 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ProjectAnalysisModule.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ProjectAnalysisModule.java
@@ -26,6 +26,7 @@ import org.sonar.server.projectanalysis.ws.DeleteEventAction;
import org.sonar.server.projectanalysis.ws.ProjectAnalysesWs;
import org.sonar.server.projectanalysis.ws.SearchAction;
import org.sonar.server.projectanalysis.ws.SetBaselineAction;
+import org.sonar.server.projectanalysis.ws.UnsetBaselineAction;
import org.sonar.server.projectanalysis.ws.UpdateEventAction;
public class ProjectAnalysisModule extends Module {
@@ -40,7 +41,8 @@ public class ProjectAnalysisModule extends Module {
DeleteEventAction.class,
DeleteAction.class,
SearchAction.class,
- SetBaselineAction.class);
+ SetBaselineAction.class,
+ UnsetBaselineAction.class);
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/UnsetBaselineAction.java b/server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/UnsetBaselineAction.java
new file mode 100644
index 00000000000..869f434f2bc
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/UnsetBaselineAction.java
@@ -0,0 +1,112 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.projectanalysis.ws;
+
+import com.google.protobuf.Empty;
+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.BranchType;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.user.UserSession;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.lang.String.format;
+import static org.apache.commons.lang.StringUtils.trimToNull;
+import static org.sonar.server.projectanalysis.ws.ProjectAnalysesWsParameters.PARAM_BRANCH;
+import static org.sonar.server.projectanalysis.ws.ProjectAnalysesWsParameters.PARAM_PROJECT;
+import static org.sonar.server.ws.WsUtils.writeProtobuf;
+
+public class UnsetBaselineAction implements ProjectAnalysesWsAction {
+ private final DbClient dbClient;
+ private final UserSession userSession;
+ private final ComponentFinder componentFinder;
+
+ public UnsetBaselineAction(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("unset_baseline")
+ .setDescription("Unset any manually-set New Code Period baseline on a project or a long-lived branch.<br/>" +
+ "Unsetting a manual baseline restores the use of the `sonar.leak.period` setting.<br/>" +
+ "Requires one of the following permissions:" +
+ "<ul>" +
+ " <li>'Administer System'</li>" +
+ " <li>'Administer' rights on the specified project</li>" +
+ "</ul>")
+ .setSince("7.7")
+ .setPost(true)
+ .setHandler(this);
+
+ action.createParam(PARAM_PROJECT)
+ .setDescription("Project key")
+ .setRequired(true);
+
+ action.createParam(PARAM_BRANCH)
+ .setDescription("Branch key");
+ }
+
+ @Override
+ public void handle(Request httpRequest, Response httpResponse) throws Exception {
+ doHandle(httpRequest);
+
+ writeProtobuf(Empty.newBuilder().build(), httpRequest, httpResponse);
+ }
+
+ private void doHandle(Request request) {
+ String projectKey = request.mandatoryParam(PARAM_PROJECT);
+ String branchKey = trimToNull(request.param(PARAM_BRANCH));
+
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ ComponentDto projectBranch = getProjectBranch(dbSession, projectKey, branchKey);
+ userSession.checkComponentPermission(UserRole.ADMIN, projectBranch);
+
+ dbClient.branchDao().updateManualBaseline(dbSession, projectBranch.uuid(), null);
+ dbSession.commit();
+ }
+ }
+
+ private ComponentDto getProjectBranch(DbSession dbSession, String projectKey, @Nullable String branchKey) {
+ if (branchKey == null) {
+ return componentFinder.getByKey(dbSession, projectKey);
+ }
+ ComponentDto project = componentFinder.getByKeyAndBranch(dbSession, projectKey, branchKey);
+
+ BranchDto branchDto = dbClient.branchDao().selectByUuid(dbSession, project.uuid())
+ .orElseThrow(() -> new NotFoundException(format("Branch '%s' is not found", branchKey)));
+
+ checkArgument(branchDto.getBranchType() == BranchType.LONG,
+ "Not a long-living branch: '%s'", branchKey);
+
+ return project;
+ }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/projectanalysis/ProjectAnalysisModuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/projectanalysis/ProjectAnalysisModuleTest.java
index c4b740dc9f9..930926b701f 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/projectanalysis/ProjectAnalysisModuleTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/projectanalysis/ProjectAnalysisModuleTest.java
@@ -23,6 +23,7 @@ import org.junit.Test;
import org.sonar.core.platform.ComponentContainer;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.core.platform.ComponentContainer.COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER;
public class ProjectAnalysisModuleTest {
@@ -30,6 +31,6 @@ public class ProjectAnalysisModuleTest {
public void verify_count_of_added_components() {
ComponentContainer container = new ComponentContainer();
new ProjectAnalysisModule().configure(container);
- assertThat(container.size()).isEqualTo(2 + 7);
+ assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 8);
}
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/projectanalysis/ws/UnsetBaselineActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/projectanalysis/ws/UnsetBaselineActionTest.java
new file mode 100644
index 00000000000..d99009ee5b8
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/projectanalysis/ws/UnsetBaselineActionTest.java
@@ -0,0 +1,283 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.projectanalysis.ws;
+
+import com.google.common.collect.ImmutableMap;
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import javax.annotation.Nullable;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchDto;
+import org.sonar.db.component.BranchType;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.SnapshotDto;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.server.component.TestComponentFinder;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.TestRequest;
+import org.sonar.server.ws.WsActionTester;
+
+import static java.util.Optional.ofNullable;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.db.component.ComponentTesting.newBranchDto;
+import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
+import static org.sonar.db.component.ComponentTesting.newProjectBranch;
+import static org.sonar.server.projectanalysis.ws.ProjectAnalysesWsParameters.PARAM_BRANCH;
+import static org.sonar.server.projectanalysis.ws.ProjectAnalysesWsParameters.PARAM_PROJECT;
+import static org.sonarqube.ws.client.WsRequest.Method.POST;
+
+@RunWith(DataProviderRunner.class)
+public class UnsetBaselineActionTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Rule
+ public UserSessionRule userSession = UserSessionRule.standalone();
+
+ @Rule
+ public DbTester db = DbTester.create(System2.INSTANCE);
+ private DbClient dbClient = db.getDbClient();
+ private DbSession dbSession = db.getSession();
+
+ private WsActionTester ws = new WsActionTester(new UnsetBaselineAction(dbClient, userSession, TestComponentFinder.from(db)));
+
+ @Test
+ public void does_not_fail_and_has_no_effect_when_there_is_no_baseline_on_main_branch() {
+ ComponentDto project = db.components().insertMainBranch(db.organizations().insert());
+ ComponentDto branch = db.components().insertProjectBranch(project);
+ SnapshotDto analysis = db.components().insertSnapshot(project);
+ logInAsProjectAdministrator(project);
+
+ call(project.getKey(), null);
+
+ verifyManualBaseline(project, null);
+ }
+
+ @Test
+ public void does_not_fail_and_has_no_effect_when_there_is_no_baseline_on_long_living_branch() {
+ ComponentDto project = db.components().insertMainBranch(db.organizations().insert());
+ ComponentDto branch = db.components().insertProjectBranch(project);
+ SnapshotDto analysis = db.components().insertSnapshot(project);
+ logInAsProjectAdministrator(project);
+
+ call(project.getKey(), branch.getBranch());
+
+ verifyManualBaseline(branch, null);
+ }
+
+ @Test
+ public void unset_baseline_when_it_is_set_on_main_branch() {
+ ComponentDto project = db.components().insertMainBranch(db.organizations().insert());
+ ComponentDto branch = db.components().insertProjectBranch(project);
+ SnapshotDto projectAnalysis = db.components().insertSnapshot(project);
+ SnapshotDto branchAnalysis = db.components().insertSnapshot(project);
+ db.components().setManualBaseline(project, projectAnalysis);
+ logInAsProjectAdministrator(project);
+
+ call(project.getKey(), null);
+
+ verifyManualBaseline(project, null);
+ }
+
+ @Test
+ public void unset_baseline_when_it_is_set_long_living_branch() {
+ ComponentDto project = db.components().insertMainBranch(db.organizations().insert());
+ ComponentDto branch = db.components().insertProjectBranch(project);
+ SnapshotDto projectAnalysis = db.components().insertSnapshot(branch);
+ SnapshotDto branchAnalysis = db.components().insertSnapshot(project);
+ db.components().setManualBaseline(branch, branchAnalysis);
+ logInAsProjectAdministrator(project);
+
+ call(project.getKey(), branch.getBranch());
+
+ verifyManualBaseline(branch, null);
+ }
+
+ @Test
+ public void fail_when_user_is_not_admin_on_project() {
+ ComponentDto project = db.components().insertMainBranch(db.organizations().insert());
+ db.components().insertProjectBranch(project);
+
+ expectedException.expect(ForbiddenException.class);
+ expectedException.expectMessage("Insufficient privileges");
+
+ call(project.getKey(), null);
+ }
+
+ @Test
+ public void fail_when_user_is_not_admin_on_project_of_branch() {
+ ComponentDto project = db.components().insertMainBranch(db.organizations().insert());
+ ComponentDto branch = db.components().insertProjectBranch(project);
+
+ expectedException.expect(ForbiddenException.class);
+ expectedException.expectMessage("Insufficient privileges");
+
+ call(project.getKey(), branch.getBranch());
+ }
+
+ @Test
+ @UseDataProvider("nullOrEmptyOrValue")
+ public void fail_with_IAE_when_missing_project_parameter(@Nullable String branchParam) {
+ ComponentDto project = db.components().insertMainBranch(db.organizations().insert());
+ db.components().insertProjectBranch(project);
+ logInAsProjectAdministrator(project);
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("The 'project' parameter is missing");
+
+ call(null, branchParam);
+ }
+
+ @Test
+ @UseDataProvider("nullOrEmptyOrValue")
+ public void fail_with_IAE_when_project_parameter_empty(@Nullable String branchParam) {
+ ComponentDto project = db.components().insertMainBranch(db.organizations().insert());
+ db.components().insertProjectBranch(project);
+ logInAsProjectAdministrator(project);
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("The 'project' parameter is missing");
+
+ call("", branchParam);
+ }
+
+ @DataProvider
+ public static Object[][] nullOrEmptyOrValue() {
+ return new Object[][] {
+ {null},
+ {""},
+ {randomAlphabetic(10)},
+ };
+ }
+
+ @Test
+ @UseDataProvider("nullOrEmpty")
+ public void does_not_fail_with_IAE_when_missing_branch_parameter(@Nullable String branchParam) {
+ ComponentDto project = db.components().insertMainBranch(db.organizations().insert());
+ db.components().insertProjectBranch(project);
+ logInAsProjectAdministrator(project);
+
+ call(project.getKey(), branchParam);
+ }
+
+ @DataProvider
+ public static Object[][] nullOrEmpty() {
+ return new Object[][] {
+ {null},
+ {""},
+ };
+ }
+
+ @DataProvider
+ public static Object[][] nonexistentParamsAndFailureMessage() {
+ return new Object[][] {
+ {ImmutableMap.of(PARAM_PROJECT, "nonexistent"), "Component 'nonexistent' on branch .* not found"},
+ {ImmutableMap.of(PARAM_BRANCH, "nonexistent"), "Component .* on branch 'nonexistent' not found"}
+ };
+ }
+
+ @Test
+ public void fail_when_branch_does_not_belong_to_project() {
+ OrganizationDto organization = db.organizations().insert();
+ ComponentDto project = db.components().insertMainBranch(organization);
+ ComponentDto branch = db.components().insertProjectBranch(project);
+ ComponentDto otherProject = db.components().insertMainBranch(organization);
+ ComponentDto otherBranch = db.components().insertProjectBranch(otherProject);
+ logInAsProjectAdministrator(project);
+
+ expectedException.expect(NotFoundException.class);
+ expectedException.expectMessage(String.format("Component '%s' on branch '%s' not found", project.getKey(), otherBranch.getKey()));
+
+ call(project.getKey(), otherBranch.getKey());
+ }
+
+ @Test
+ public void fail_with_IAE_when_branch_is_short() {
+ ComponentDto project = newPrivateProjectDto(db.organizations().insert());
+ BranchDto branch = newBranchDto(project.projectUuid(), BranchType.SHORT);
+ db.components().insertProjectBranch(project, branch);
+ logInAsProjectAdministrator(project);
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage(String.format("Not a long-living branch: '%s'", branch.getKey()));
+
+ call(project.getKey(), branch.getKey());
+ }
+
+ @Test
+ public void fail_with_NotFoundException_when_branch_is_pull_request() {
+ ComponentDto project = newPrivateProjectDto(db.organizations().insert());
+ BranchDto branch = newBranchDto(project.projectUuid(), BranchType.LONG);
+ db.components().insertProjectBranch(project, branch);
+ ComponentDto pullRequest = newProjectBranch(project, branch);
+ logInAsProjectAdministrator(project);
+
+ expectedException.expect(NotFoundException.class);
+ expectedException.expectMessage(String.format("Component '%s' on branch '%s' not found", project.getKey(), pullRequest.getKey()));
+
+ call(project.getKey(), pullRequest.getKey());
+ }
+
+ @Test
+ public void verify_ws_parameters() {
+ WebService.Action definition = ws.getDef();
+
+ assertThat(definition.isPost()).isTrue();
+ assertThat(definition.key()).isEqualTo("unset_baseline");
+ assertThat(definition.since()).isEqualTo("7.7");
+ assertThat(definition.isInternal()).isFalse();
+ }
+
+ private void logInAsProjectAdministrator(ComponentDto project) {
+ userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
+ }
+
+ private void call(@Nullable String project, @Nullable String branchName) {
+ TestRequest httpRequest = ws.newRequest().setMethod(POST.name());
+ ofNullable(project).ifPresent(t -> httpRequest.setParam(PARAM_PROJECT, t));
+ ofNullable(branchName).ifPresent(t -> httpRequest.setParam(PARAM_BRANCH, t));
+
+ httpRequest.execute();
+ }
+
+ private void verifyManualBaseline(ComponentDto project, @Nullable SnapshotDto projectAnalysis) {
+ BranchDto branchDto = db.getDbClient().branchDao().selectByUuid(dbSession, project.uuid()).get();
+ if (projectAnalysis == null) {
+ assertThat(branchDto.getManualBaseline()).isNull();
+ } else {
+ assertThat(branchDto.getManualBaseline()).isEqualTo(projectAnalysis.getUuid());
+ }
+ }
+
+}