]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7943 WS api/qualitygates/deselect handles deselect by project uuid and project key
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Fri, 5 Aug 2016 16:46:03 +0000 (18:46 +0200)
committerTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Fri, 5 Aug 2016 16:46:03 +0000 (18:46 +0200)
server/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGates.java
server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/DeselectAction.java
server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/SelectAction.java
server/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGatesTest.java
server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/AppActionTest.java
server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/DeselectActionTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/QualityGatesWsTest.java

index 786f38ed803ded084dd014ba4da20a35a4124f9d..08bcbc4897a50af72b320eca7c24e2b0914f7a82 100644 (file)
@@ -397,7 +397,8 @@ public class QualityGates {
 
   private void checkPermission(Long projectId, DbSession session) {
     ComponentDto project = componentDao.selectOrFailById(session, projectId);
-    if (!userSession.hasPermission(GlobalPermissions.QUALITY_GATE_ADMIN) && !userSession.hasComponentPermission(UserRole.ADMIN, project.key())) {
+    if (!userSession.hasPermission(GlobalPermissions.QUALITY_GATE_ADMIN)
+      && !userSession.hasComponentUuidPermission(UserRole.ADMIN, project.uuid())) {
       throw new ForbiddenException("Insufficient privileges");
     }
   }
index 0579676dfcb236ae5ebfcfec2c91b60e966a8102..29c947d5afa12dcf4ca1f146ff5e13676ec519e4 100644 (file)
  */
 package org.sonar.server.qualitygate.ws;
 
+import com.google.common.base.Optional;
+import org.elasticsearch.common.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.core.util.Uuids;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.server.component.ComponentFinder;
 import org.sonar.server.qualitygate.QualityGates;
 import org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters;
 
+import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
 import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_PROJECT_ID;
+import static org.sonarqube.ws.client.qualitygate.QualityGatesWsParameters.PARAM_PROJECT_KEY;
 
 public class DeselectAction implements QualityGatesWsAction {
 
   private final QualityGates qualityGates;
+  private final DbClient dbClient;
+  private final ComponentFinder componentFinder;
 
-  public DeselectAction(QualityGates qualityGates) {
+  public DeselectAction(QualityGates qualityGates, DbClient dbClient, ComponentFinder componentFinder) {
     this.qualityGates = qualityGates;
+    this.dbClient = dbClient;
+    this.componentFinder = componentFinder;
   }
 
   @Override
@@ -44,20 +57,48 @@ public class DeselectAction implements QualityGatesWsAction {
       .setHandler(this);
 
     action.createParam(QualityGatesWsParameters.PARAM_GATE_ID)
-      .setDescription("Quality Gate ID")
+      .setDescription("Quality Gate id")
       .setRequired(true)
-      .setExampleValue("1");
+      .setExampleValue("23");
 
     action.createParam(PARAM_PROJECT_ID)
-      .setDescription("Project ID")
-      .setRequired(true)
-      .setExampleValue("12");
+      .setDescription("Project id. Project id as an numeric value is deprecated since 6.1")
+      .setExampleValue(Uuids.UUID_EXAMPLE_01);
+
+    action.createParam(PARAM_PROJECT_KEY)
+      .setDescription("Project key")
+      .setExampleValue(KEY_PROJECT_EXAMPLE_001)
+      .setSince("6.1");
   }
 
   @Override
   public void handle(Request request, Response response) {
-    qualityGates.dissociateProject(QualityGatesWs.parseId(request, QualityGatesWsParameters.PARAM_GATE_ID), QualityGatesWs.parseId(request, PARAM_PROJECT_ID));
+    ComponentDto project = getProject(request.param(PARAM_PROJECT_ID), request.param(PARAM_PROJECT_KEY));
+    qualityGates.dissociateProject(QualityGatesWs.parseId(request, QualityGatesWsParameters.PARAM_GATE_ID), project.getId());
     response.noContent();
   }
 
+  private ComponentDto getProject(@Nullable String projectId, @Nullable String projectKey) {
+    DbSession dbSession = dbClient.openSession(false);
+    try {
+      return selectProjectById(dbSession, projectId)
+        .or(() -> componentFinder.getByUuidOrKey(dbSession, projectId, projectKey, ComponentFinder.ParamNames.PROJECT_ID_AND_KEY));
+    } finally {
+      dbClient.closeSession(dbSession);
+    }
+  }
+
+  private Optional<ComponentDto> selectProjectById(DbSession dbSession, @Nullable String projectId) {
+    if (projectId == null) {
+      return Optional.absent();
+    }
+
+    try {
+      long dbId = Long.parseLong(projectId);
+      return dbClient.componentDao().selectById(dbSession, dbId);
+    } catch (NumberFormatException e) {
+      return Optional.absent();
+    }
+  }
+
 }
index e60d1b73d61bfd805ff4bc95dbf541c785e6a9db..0dcea16e9c1f12a72c015c325a5ad70041111fe1 100644 (file)
@@ -75,8 +75,7 @@ public class SelectAction implements QualityGatesWsAction {
 
     action.createParam(PARAM_PROJECT_ID)
       .setDescription("Project id. Project id as an numeric value is deprecated since 6.1")
-      .setExampleValue(Uuids.UUID_EXAMPLE_01)
-      .setDeprecatedSince("6.1");
+      .setExampleValue(Uuids.UUID_EXAMPLE_01);
 
     action.createParam(PARAM_PROJECT_KEY)
       .setDescription("Project key")
index ec9af1871e25d7591723ec5a8d85ae0e4997532a..9d8e25947c146fa82a7f49c97bc0d95364a012f4 100644 (file)
@@ -41,10 +41,10 @@ import org.sonar.api.measures.Metric.ValueType;
 import org.sonar.api.measures.MetricFinder;
 import org.sonar.api.web.UserRole;
 import org.sonar.core.permission.GlobalPermissions;
+import org.sonar.core.util.Uuids;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.component.ComponentDao;
-import org.sonar.db.component.ComponentDto;
 import org.sonar.db.property.PropertiesDao;
 import org.sonar.db.property.PropertyDto;
 import org.sonar.db.qualitygate.QualityGateConditionDao;
@@ -70,6 +70,7 @@ import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.sonar.db.component.ComponentTesting.newProjectDto;
 
 @RunWith(MockitoJUnitRunner.class)
 public class QualityGatesTest {
@@ -94,9 +95,10 @@ public class QualityGatesTest {
   QualityGates underTest;
 
   static final String PROJECT_KEY = "SonarQube";
+  static final String PROJECT_UUID = Uuids.UUID_EXAMPLE_01;
 
   UserSession authorizedProfileAdminUserSession = new MockUserSession("gaudol").setName("Olivier").setGlobalPermissions(GlobalPermissions.QUALITY_GATE_ADMIN);
-  UserSession authorizedProjectAdminUserSession = new MockUserSession("gaudol").setName("Olivier").addProjectPermissions(UserRole.ADMIN, PROJECT_KEY);
+  UserSession authorizedProjectAdminUserSession = new MockUserSession("gaudol").setName("Olivier").addProjectUuidPermissions(UserRole.ADMIN, PROJECT_UUID);
   UserSession unauthorizedUserSession = new MockUserSession("polop").setName("Polop");
   UserSession unauthenticatedUserSession = new AnonymousMockUserSession();
 
@@ -110,9 +112,10 @@ public class QualityGatesTest {
     when(dbClient.propertiesDao()).thenReturn(propertiesDao);
     when(dbClient.componentDao()).thenReturn(componentDao);
 
-    when(componentDao.selectOrFailById(eq(dbSession), anyLong())).thenReturn(new ComponentDto().setId(1L).setKey(PROJECT_KEY));
+    when(componentDao.selectOrFailById(eq(dbSession), anyLong())).thenReturn(newProjectDto(PROJECT_UUID).setId(1L).setKey(PROJECT_KEY));
 
     underTest = new QualityGates(dbClient, metricFinder, userSessionRule, settings);
+
     userSessionRule.set(authorizedProfileAdminUserSession);
   }
 
index 619fee9c63886e746666e1cf63fef330f587f526..7e942c4be8799f78a941f7800e838307fa07cee2 100644 (file)
 package org.sonar.server.qualitygate.ws;
 
 import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.Locale;
+import java.util.Map;
 import org.json.simple.JSONValue;
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
 import org.mockito.invocation.InvocationOnMock;
-import org.mockito.runners.MockitoJUnitRunner;
 import org.mockito.stubbing.Answer;
 import org.sonar.api.i18n.I18n;
 import org.sonar.api.measures.Metric;
@@ -39,40 +39,30 @@ import org.sonar.server.qualitygate.QualityGates;
 import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.ws.WsTester;
 
-import java.util.Collection;
-import java.util.Locale;
-import java.util.Map;
-
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
-@RunWith(MockitoJUnitRunner.class)
 public class AppActionTest {
 
-  @Mock
-  private QualityGates qGates;
-
-  @Mock
-  private Periods periods;
-
-  @Mock
-  private I18n i18n;
+  private QualityGates qGates = mock(QualityGates.class);
+  private Periods periods = mock(Periods.class);
+  private I18n i18n = mock(I18n.class);
 
-  WsTester tester;
+  WsTester ws;
 
   @Before
   public void setUp() {
     SelectAction selectAction = new SelectAction(mock(DbClient.class), mock(UserSessionRule.class), mock(ComponentFinder.class));
 
-    tester = new WsTester(new QualityGatesWs(
+    ws = new WsTester(new QualityGatesWs(
       new ListAction(qGates), new ShowAction(qGates), new SearchAction(mock(QgateProjectFinder.class)),
       new CreateAction(qGates), new CopyAction(qGates), new DestroyAction(qGates), new RenameAction(qGates),
       new SetAsDefaultAction(qGates), new UnsetDefaultAction(qGates),
       new CreateConditionAction(qGates), new UpdateConditionAction(qGates), new DeleteConditionAction(qGates),
-      selectAction, new DeselectAction(qGates), new AppAction(qGates)));
+      selectAction, new DeselectAction(qGates, mock(DbClient.class), mock(ComponentFinder.class)), new AppAction(qGates)));
   }
 
   @Test
@@ -94,7 +84,7 @@ public class AppActionTest {
     when(metric.isHidden()).thenReturn(false);
     when(qGates.gateMetrics()).thenReturn(ImmutableList.of(metric));
 
-    String json = tester.newGetRequest("api/qualitygates", "app").execute().outputAsString();
+    String json = ws.newGetRequest("api/qualitygates", "app").execute().outputAsString();
 
     Map responseJson = (Map) JSONValue.parse(json);
     assertThat((Boolean) responseJson.get("edit")).isFalse();
diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/DeselectActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/DeselectActionTest.java
new file mode 100644 (file)
index 0000000..91b3a0e
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.qualitygate.ws;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.config.Settings;
+import org.sonar.api.measures.MetricFinder;
+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.ComponentDbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.property.PropertyDto;
+import org.sonar.db.qualitygate.QualityGateDto;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.qualitygate.QualityGates;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.WsActionTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.sonar.core.permission.GlobalPermissions.QUALITY_GATE_ADMIN;
+import static org.sonar.core.permission.GlobalPermissions.QUALITY_PROFILE_ADMIN;
+import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
+import static org.sonar.db.component.ComponentTesting.newProjectDto;
+import static org.sonar.server.qualitygate.QualityGates.SONAR_QUALITYGATE_PROPERTY;
+
+public class DeselectActionTest {
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  @Rule
+  public UserSessionRule userSession = UserSessionRule.standalone();
+
+  @Rule
+  public DbTester db = DbTester.create(System2.INSTANCE);
+  DbClient dbClient = db.getDbClient();
+  DbSession dbSession = db.getSession();
+  ComponentDbTester componentDb = new ComponentDbTester(db);
+
+  QualityGates qualityGates = new QualityGates(dbClient, mock(MetricFinder.class), userSession, mock(Settings.class));
+
+  WsActionTester ws;
+
+  DeselectAction underTest;
+
+  @Before
+  public void setUp() {
+    ComponentFinder componentFinder = new ComponentFinder(dbClient);
+    underTest = new DeselectAction(qualityGates, dbClient, componentFinder);
+    ws = new WsActionTester(underTest);
+
+    userSession.login("login").setGlobalPermissions(QUALITY_GATE_ADMIN);
+  }
+
+  @Test
+  public void deselect_by_id() throws Exception {
+    ComponentDto project = insertProject();
+    ComponentDto anotherProject = componentDb.insertProject();
+    QualityGateDto gate = insertQualityGate();
+    String gateId = String.valueOf(gate.getId());
+    associateProjectToQualityGate(project.getId(), gateId);
+    associateProjectToQualityGate(anotherProject.getId(), gateId);
+
+    callById(gateId, project.getId());
+
+    assertDeselected(project.getId());
+    assertSelected(gateId, anotherProject.getId());
+  }
+
+  @Test
+  public void deselect_by_uuid() throws Exception {
+    ComponentDto project = insertProject();
+    QualityGateDto gate = insertQualityGate();
+    String gateId = String.valueOf(gate.getId());
+    associateProjectToQualityGate(project.getId(), gateId);
+
+    callByUuid(gateId, project.uuid());
+
+    assertDeselected(project.getId());
+  }
+
+  @Test
+  public void deselect_by_key() throws Exception {
+    ComponentDto project = insertProject();
+    QualityGateDto gate = insertQualityGate();
+    String gateId = String.valueOf(gate.getId());
+    associateProjectToQualityGate(project.getId(), gateId);
+
+    callByKey(gateId, project.getKey());
+
+    assertDeselected(project.getId());
+  }
+
+  @Test
+  public void project_admin() throws Exception {
+    ComponentDto project = insertProject();
+    QualityGateDto gate = insertQualityGate();
+    String gateId = String.valueOf(gate.getId());
+    associateProjectToQualityGate(project.getId(), gateId);
+
+    userSession.login("login").addProjectUuidPermissions(UserRole.ADMIN, project.uuid());
+
+    callByKey(gateId, project.getKey());
+
+    assertDeselected(project.getId());
+  }
+
+  @Test
+  public void system_admin() throws Exception {
+    ComponentDto project = insertProject();
+    QualityGateDto gate = insertQualityGate();
+    String gateId = String.valueOf(gate.getId());
+    associateProjectToQualityGate(project.getId(), gateId);
+
+    userSession.login("login").setGlobalPermissions(SYSTEM_ADMIN);
+
+    callByKey(gateId, project.getKey());
+
+    assertDeselected(project.getId());
+  }
+
+  @Test
+  public void fail_when_no_quality_gate() throws Exception {
+    ComponentDto project = insertProject();
+
+    expectedException.expect(NotFoundException.class);
+
+    callByKey("1", project.getKey());
+  }
+
+  @Test
+  public void fail_when_no_project_id() throws Exception {
+    QualityGateDto gate = insertQualityGate();
+    String gateId = String.valueOf(gate.getId());
+
+    expectedException.expect(NotFoundException.class);
+
+    callById(gateId, 1L);
+  }
+
+  @Test
+  public void fail_when_no_project_key() throws Exception {
+    QualityGateDto gate = insertQualityGate();
+    String gateId = String.valueOf(gate.getId());
+
+    expectedException.expect(NotFoundException.class);
+
+    callByKey(gateId, "unknown");
+  }
+
+  @Test
+  public void fail_when_anonymous() throws Exception {
+    ComponentDto project = insertProject();
+    QualityGateDto gate = insertQualityGate();
+    String gateId = String.valueOf(gate.getId());
+    userSession.anonymous();
+
+    expectedException.expect(ForbiddenException.class);
+    callByKey(gateId, project.getKey());
+  }
+
+  @Test
+  public void fail_when_not_project_admin() throws Exception {
+    ComponentDto project = insertProject();
+    QualityGateDto gate = insertQualityGate();
+    String gateId = String.valueOf(gate.getId());
+
+    userSession.login("login").addProjectUuidPermissions(UserRole.ISSUE_ADMIN, project.uuid());
+
+    expectedException.expect(ForbiddenException.class);
+
+    callByKey(gateId, project.getKey());
+  }
+
+  @Test
+  public void fail_when_not_quality_gates_admin() throws Exception {
+    ComponentDto project = insertProject();
+    QualityGateDto gate = insertQualityGate();
+    String gateId = String.valueOf(gate.getId());
+
+    userSession.login("login").setGlobalPermissions(QUALITY_PROFILE_ADMIN);
+
+    expectedException.expect(ForbiddenException.class);
+
+    callByKey(gateId, project.getKey());
+  }
+
+  private ComponentDto insertProject() {
+    return componentDb.insertComponent(newProjectDto());
+  }
+
+  private QualityGateDto insertQualityGate() {
+    QualityGateDto gate = new QualityGateDto().setName("Custom");
+    dbClient.qualityGateDao().insert(dbSession, gate);
+    dbSession.commit();
+    return gate;
+  }
+
+  private void callByKey(String gateId, String projectKey) {
+    ws.newRequest()
+      .setParam("gateId", String.valueOf(gateId))
+      .setParam("projectKey", projectKey)
+      .execute();
+  }
+
+  private void callById(String gateId, Long projectId) {
+    ws.newRequest()
+      .setParam("gateId", String.valueOf(gateId))
+      .setParam("projectId", String.valueOf(projectId))
+      .execute();
+  }
+
+  private void callByUuid(String gateId, String projectUuid) {
+    ws.newRequest()
+      .setParam("gateId", String.valueOf(gateId))
+      .setParam("projectId", projectUuid)
+      .execute();
+  }
+
+  private void associateProjectToQualityGate(long projectId, String gateId) {
+    dbClient.propertiesDao().insertProperty(dbSession, new PropertyDto()
+      .setResourceId(projectId)
+      .setValue(gateId)
+      .setKey(SONAR_QUALITYGATE_PROPERTY));
+    db.commit();
+  }
+
+  private void assertDeselected(long projectId) {
+    assertThat(dbClient.propertiesDao().selectProjectProperty(projectId, SONAR_QUALITYGATE_PROPERTY)).isNull();
+  }
+
+  private void assertSelected(String qGateId, long projectId) {
+    assertThat(dbClient.propertiesDao().selectProjectProperty(projectId, SONAR_QUALITYGATE_PROPERTY).getValue()).isEqualTo(qGateId);
+  }
+}
index fce093f1e564d8959d0bd5bfe1b291ae8c21e67b..58916a1ab78d1015ef32b4ca4a74fa62d20fe45e 100644 (file)
@@ -73,7 +73,7 @@ public class QualityGatesWsTest {
       new CreateAction(qGates), new CopyAction(qGates), new DestroyAction(qGates), new RenameAction(qGates),
       new SetAsDefaultAction(qGates), new UnsetDefaultAction(qGates),
       new CreateConditionAction(qGates), new UpdateConditionAction(qGates), new DeleteConditionAction(qGates),
-      selectAction, new DeselectAction(qGates), new AppAction(qGates)));
+      selectAction, new DeselectAction(qGates, mock(DbClient.class), mock(ComponentFinder.class)), new AppAction(qGates)));
   }
 
   @Test
@@ -429,16 +429,4 @@ public class QualityGatesWsTest {
     ProjectQgateAssociationQuery query = queryCaptor.getValue();
     assertThat(query.membership()).isEqualTo(ProjectQgateAssociationQuery.IN);
   }
-
-  @Test
-  public void deselect_nominal() throws Exception {
-    long gateId = 42L;
-    long projectId = 666L;
-    tester.newPostRequest("api/qualitygates", "deselect")
-      .setParam("gateId", Long.toString(gateId))
-      .setParam("projectId", Long.toString(projectId))
-      .execute()
-      .assertNoContent();
-    verify(qGates).dissociateProject(gateId, projectId);
-  }
 }