]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-10088 Return available actions in api/qualitygates/list
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 23 Nov 2017 10:06:10 +0000 (11:06 +0100)
committerEric Hartmann <hartmann.eric@gmail.Com>
Mon, 4 Dec 2017 12:44:55 +0000 (13:44 +0100)
server/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGateModule.java
server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/ListAction.java
server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QGateWsSupport.java [new file with mode: 0644]
server/sonar-server/src/main/resources/org/sonar/server/qualitygate/ws/list-example.json
server/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGateModuleTest.java
server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/ListActionTest.java
sonar-ws/src/main/protobuf/ws-qualitygates.proto
sonar-ws/src/main/protobuf/ws-qualityprofiles.proto

index 6e97f2355f17e289442b28ed11e70fa2da6552cc..ee9980dc1128de4e4c87c2eb26f23f99986acc51 100644 (file)
@@ -30,6 +30,7 @@ import org.sonar.server.qualitygate.ws.DestroyAction;
 import org.sonar.server.qualitygate.ws.GetByProjectAction;
 import org.sonar.server.qualitygate.ws.ListAction;
 import org.sonar.server.qualitygate.ws.ProjectStatusAction;
+import org.sonar.server.qualitygate.ws.QGateWsSupport;
 import org.sonar.server.qualitygate.ws.QualityGatesWs;
 import org.sonar.server.qualitygate.ws.QualityGatesWsSupport;
 import org.sonar.server.qualitygate.ws.RenameAction;
@@ -50,6 +51,7 @@ public class QualityGateModule extends Module {
       QgateProjectFinder.class,
       QualityGateFinder.class,
       // WS
+      QGateWsSupport.class,
       QualityGatesWsSupport.class,
       QualityGatesWs.class,
       ListAction.class,
index fe9e93dfd13049ab0c17a00ddbc0c199c652c1a8..a3fc1cbc0d20e0a5f4687d0f855dff9468dfeeee 100644 (file)
@@ -21,30 +21,29 @@ package org.sonar.server.qualitygate.ws;
 
 import com.google.common.io.Resources;
 import java.util.Collection;
-import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
-import org.apache.commons.lang.StringUtils;
 import org.sonar.api.server.ws.Change;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
-import org.sonar.db.property.PropertyDto;
 import org.sonar.db.qualitygate.QualityGateDto;
 import org.sonarqube.ws.Qualitygates.ListWsResponse;
+import org.sonarqube.ws.Qualitygates.ListWsResponse.QualityGate;
 
 import static org.sonar.core.util.Protobuf.setNullable;
 import static org.sonar.core.util.stream.MoreCollectors.toList;
-import static org.sonar.server.qualitygate.QualityGates.SONAR_QUALITYGATE_PROPERTY;
 import static org.sonar.server.ws.WsUtils.writeProtobuf;
 
 public class ListAction implements QualityGatesWsAction {
 
   private final DbClient dbClient;
+  private final QGateWsSupport wsSupport;
 
-  public ListAction(DbClient dbClient) {
+  public ListAction(DbClient dbClient, QGateWsSupport wsSupport) {
     this.dbClient = dbClient;
+    this.wsSupport = wsSupport;
   }
 
   @Override
@@ -56,50 +55,35 @@ public class ListAction implements QualityGatesWsAction {
       .setChangelog(
         new Change("7.0", "'isDefault' field is added on quality gate"),
         new Change("7.0", "'default' field on root level is deprecated"),
-        new Change("7.0", "'isBuiltIn' field is added in the response"))
+        new Change("7.0", "'isBuiltIn' field is added in the response"),
+        new Change("7.0", "'actions' fields are added in the response"))
       .setHandler(this);
   }
 
   @Override
   public void handle(Request request, Response response) {
     try (DbSession dbSession = dbClient.openSession(false)) {
-      QualityGateDto defaultQualityGate = getDefault(dbSession);
+      QualityGateDto defaultQualityGate = wsSupport.getDefault(dbSession);
       Collection<QualityGateDto> qualityGates = dbClient.qualityGateDao().selectAll(dbSession);
       writeProtobuf(buildResponse(qualityGates, defaultQualityGate), request, response);
     }
   }
 
-  private static ListWsResponse buildResponse(Collection<QualityGateDto> qualityGates, @Nullable QualityGateDto defaultQualityGate) {
+  private ListWsResponse buildResponse(Collection<QualityGateDto> qualityGates, @Nullable QualityGateDto defaultQualityGate) {
     Long defaultId = defaultQualityGate == null ? null : defaultQualityGate.getId();
     ListWsResponse.Builder builder = ListWsResponse.newBuilder()
+      .setActions(ListWsResponse.RootActions.newBuilder().setCreate(wsSupport.isQualityGateAdmin()))
       .addAllQualitygates(qualityGates.stream()
-        .map(qualityGate -> ListWsResponse.QualityGate.newBuilder()
+        .map(qualityGate -> QualityGate.newBuilder()
           .setId(qualityGate.getId())
           .setName(qualityGate.getName())
           .setIsDefault(qualityGate.getId().equals(defaultId))
           .setIsBuiltIn(qualityGate.isBuiltIn())
+          .setActions(wsSupport.getActions(qualityGate, defaultQualityGate))
           .build())
         .collect(toList()));
     setNullable(defaultId, builder::setDefault);
     return builder.build();
   }
 
-  @CheckForNull
-  private QualityGateDto getDefault(DbSession dbSession) {
-    Long defaultId = getDefaultId(dbSession);
-    if (defaultId == null) {
-      return null;
-    }
-    return dbClient.qualityGateDao().selectById(dbSession, defaultId);
-  }
-
-  @CheckForNull
-  private Long getDefaultId(DbSession dbSession) {
-    PropertyDto defaultQgate = dbClient.propertiesDao().selectGlobalProperty(dbSession, SONAR_QUALITYGATE_PROPERTY);
-    if (defaultQgate == null || StringUtils.isBlank(defaultQgate.getValue())) {
-      return null;
-    }
-    return Long.valueOf(defaultQgate.getValue());
-  }
-
 }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QGateWsSupport.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QGateWsSupport.java
new file mode 100644 (file)
index 0000000..0a67641
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * 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.qualitygate.ws;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.server.ServerSide;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.property.PropertyDto;
+import org.sonar.db.qualitygate.QualityGateDto;
+import org.sonar.server.organization.DefaultOrganizationProvider;
+import org.sonar.server.user.UserSession;
+import org.sonarqube.ws.Qualitygates;
+
+import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_GATES;
+import static org.sonar.server.qualitygate.QualityGates.SONAR_QUALITYGATE_PROPERTY;
+
+@ServerSide
+public class QGateWsSupport {
+
+  private final DbClient dbClient;
+  private final UserSession userSession;
+  private final DefaultOrganizationProvider defaultOrganizationProvider;
+
+  public QGateWsSupport(DbClient dbClient, UserSession userSession, DefaultOrganizationProvider defaultOrganizationProvider) {
+    this.dbClient = dbClient;
+    this.userSession = userSession;
+    this.defaultOrganizationProvider = defaultOrganizationProvider;
+  }
+
+  boolean isQualityGateAdmin() {
+    return userSession.hasPermission(ADMINISTER_QUALITY_GATES, defaultOrganizationProvider.get().getUuid());
+  }
+
+  Qualitygates.Actions getActions(QualityGateDto qualityGate, @Nullable QualityGateDto defaultQualityGate) {
+    Long defaultId = defaultQualityGate == null ? null : defaultQualityGate.getId();
+    boolean isDefault = qualityGate.getId().equals(defaultId);
+    boolean isBuiltIn = qualityGate.isBuiltIn();
+    boolean isQualityGateAdmin = isQualityGateAdmin();
+    return Qualitygates.Actions.newBuilder()
+      .setCopy(isQualityGateAdmin)
+      .setEdit(!isBuiltIn && isQualityGateAdmin)
+      .setSetAsDefault(!isDefault && isQualityGateAdmin)
+      .setAssociateProjects(!isDefault && isQualityGateAdmin)
+      .build();
+  }
+
+  @CheckForNull
+  QualityGateDto getDefault(DbSession dbSession) {
+    Long defaultId = getDefaultId(dbSession);
+    if (defaultId == null) {
+      return null;
+    }
+    return dbClient.qualityGateDao().selectById(dbSession, defaultId);
+  }
+
+  @CheckForNull
+  private Long getDefaultId(DbSession dbSession) {
+    PropertyDto defaultQgate = dbClient.propertiesDao().selectGlobalProperty(dbSession, SONAR_QUALITYGATE_PROPERTY);
+    if (defaultQgate == null || StringUtils.isBlank(defaultQgate.getValue())) {
+      return null;
+    }
+    return Long.valueOf(defaultQgate.getValue());
+  }
+
+}
index 547b8d4e8634d9ac9ba4087fdf27d83525d7f9cb..270dc554203d1dd4eee6c95c00f3cd5410c5c6b5 100644 (file)
@@ -4,14 +4,29 @@
       "id": 2,
       "name": "Sonar way",
       "isDefault": true,
-      "isBuiltIn": true
+      "isBuiltIn": true,
+      "actions": {
+        "edit": false,
+        "setAsDefault": false,
+        "copy": true,
+        "associateProjects": false
+      }
     },
     {
       "id": 4,
       "name": "Sonar way - Without Coverage",
       "isDefault": false,
-      "isBuiltIn": false
+      "isBuiltIn": false,
+      "actions": {
+        "edit": true,
+        "setAsDefault": true,
+        "copy": true,
+        "associateProjects": true
+      }
     }
   ],
+  "actions": {
+    "create": true
+  },
   "default": 2
 }
index 622d9fc62361fcd7014d35ebb7ca6fbe43654298..36d9eba904b2ef878bfe2f5b3b047d19c194e8a4 100644 (file)
@@ -29,6 +29,6 @@ public class QualityGateModuleTest {
   public void verify_count_of_added_components() {
     ComponentContainer container = new ComponentContainer();
     new QualityGateModule().configure(container);
-    assertThat(container.size()).isEqualTo(24 + 2);
+    assertThat(container.size()).isEqualTo(25 + 2);
   }
 }
index 83f27cd202509824fba4a9ca43c5daf7166f0a0d..108ef2da489bb9387889ce0a5ad86c94162526e4 100644 (file)
@@ -27,12 +27,16 @@ import org.sonar.api.server.ws.Change;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.db.DbTester;
 import org.sonar.db.qualitygate.QualityGateDto;
+import org.sonar.server.organization.DefaultOrganizationProvider;
+import org.sonar.server.organization.TestDefaultOrganizationProvider;
 import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.ws.WsActionTester;
 import org.sonarqube.ws.Qualitygates.ListWsResponse.QualityGate;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.AssertionsForClassTypes.tuple;
+import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_GATES;
+import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_PROFILES;
 import static org.sonar.test.JsonAssert.assertJson;
 import static org.sonarqube.ws.Qualitygates.ListWsResponse;
 
@@ -44,8 +48,9 @@ public class ListActionTest {
   public UserSessionRule userSession = UserSessionRule.standalone();
   @Rule
   public DbTester db = DbTester.create();
+  private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
 
-  private WsActionTester ws = new WsActionTester(new ListAction(db.getDbClient()));
+  private WsActionTester ws = new WsActionTester(new ListAction(db.getDbClient(), new QGateWsSupport(db.getDbClient(), userSession, defaultOrganizationProvider)));
 
   @Test
   public void verify_definition() {
@@ -58,12 +63,14 @@ public class ListActionTest {
       .containsExactlyInAnyOrder(
         tuple("7.0", "'isDefault' field is added on quality gate"),
         tuple("7.0", "'default' field on root level is deprecated"),
-        tuple("7.0", "'isBuiltIn' field is added in the response"));
+        tuple("7.0", "'isBuiltIn' field is added in the response"),
+        tuple("7.0", "'actions' fields are added in the response"));
     assertThat(action.params()).isEmpty();
   }
 
   @Test
   public void json_example() {
+    userSession.logIn("admin").addPermission(ADMINISTER_QUALITY_GATES, defaultOrganizationProvider.get().getUuid());
     QualityGateDto defaultQualityGate = db.qualityGates().insertQualityGate(qualityGate -> qualityGate.setName("Sonar way").setBuiltIn(true));
     db.qualityGates().insertQualityGate(qualityGate -> qualityGate.setName("Sonar way - Without Coverage").setBuiltIn(false));
     db.qualityGates().setDefaultQualityGate(defaultQualityGate);
@@ -126,6 +133,46 @@ public class ListActionTest {
       .containsExactlyInAnyOrder(tuple(qualityGate.getId(), qualityGate.getName(), false));
   }
 
+  @Test
+  public void actions_with_quality_gate_administer_permission() {
+    userSession.logIn("john").addPermission(ADMINISTER_QUALITY_GATES, defaultOrganizationProvider.get().getUuid());
+    QualityGateDto defaultQualityGate = db.qualityGates().insertQualityGate(qg -> qg.setName("Sonar way").setBuiltIn(true));
+    QualityGateDto otherQualityGate = db.qualityGates().insertQualityGate(qg -> qg.setName("Sonar way - Without Coverage").setBuiltIn(false));
+    db.qualityGates().setDefaultQualityGate(defaultQualityGate);
+
+    ListWsResponse response = ws.newRequest().executeProtobuf(ListWsResponse.class);
+
+    assertThat(response.getActions())
+      .extracting(ListWsResponse.RootActions::getCreate)
+      .containsExactlyInAnyOrder(true);
+    assertThat(response.getQualitygatesList())
+      .extracting(QualityGate::getName,
+        qg -> qg.getActions().getEdit(), qp -> qp.getActions().getCopy(), qp -> qp.getActions().getSetAsDefault(), qp -> qp.getActions().getAssociateProjects())
+      .containsExactlyInAnyOrder(
+        tuple(defaultQualityGate.getName(), false, true, false, false),
+        tuple(otherQualityGate.getName(), true, true, true, true));
+  }
+
+  @Test
+  public void actions_without_quality_gate_administer_permission() {
+    userSession.logIn("john").addPermission(ADMINISTER_QUALITY_PROFILES, defaultOrganizationProvider.get().getUuid());
+    QualityGateDto defaultQualityGate = db.qualityGates().insertQualityGate(qg -> qg.setName("Sonar way").setBuiltIn(true));
+    QualityGateDto otherQualityGate = db.qualityGates().insertQualityGate(qg -> qg.setName("Sonar way - Without Coverage").setBuiltIn(false));
+    db.qualityGates().setDefaultQualityGate(defaultQualityGate);
+
+    ListWsResponse response = ws.newRequest().executeProtobuf(ListWsResponse.class);
+
+    assertThat(response.getActions())
+      .extracting(ListWsResponse.RootActions::getCreate)
+      .containsExactlyInAnyOrder(false);
+    assertThat(response.getQualitygatesList())
+      .extracting(QualityGate::getName,
+        qg -> qg.getActions().getEdit(), qp -> qp.getActions().getCopy(), qp -> qp.getActions().getSetAsDefault(), qp -> qp.getActions().getAssociateProjects())
+      .containsExactlyInAnyOrder(
+        tuple(defaultQualityGate.getName(), false, false, false, false),
+        tuple(otherQualityGate.getName(), false, false, false, false));
+  }
+
   @Test
   public void empty() {
     ListWsResponse response = ws.newRequest().executeProtobuf(ListWsResponse.class);
index 57658d9996f4509084225c6d323f0b647d3e3377..15e2ab342e385b1732ccca1d0ca2850111268c5d 100644 (file)
@@ -140,13 +140,26 @@ message ListWsResponse {
   repeated QualityGate qualitygates = 1;
   // Deprecated since 7.0
   optional int64 default = 2;
+  optional RootActions actions = 3;
 
   message QualityGate {
     optional int64 id = 1;
     optional string name = 2;
     optional bool isDefault = 3;
     optional bool isBuiltIn = 4;
+    optional Actions actions = 5;
   }
+
+  message RootActions {
+    optional bool create = 1;
+  }
+}
+
+message Actions {
+  optional bool edit = 1;
+  optional bool setAsDefault = 2;
+  optional bool copy = 3;
+  optional bool associateProjects = 4;
 }
 
 
index e4d9df36abca122d53dc2bde06937b6b9505d07c..0b0039a2506e887c52434a0d9e7e1fcdf72defda 100644 (file)
@@ -54,7 +54,7 @@ message SearchWsResponse {
         optional bool edit = 1;
         optional bool setAsDefault = 2;
         optional bool copy = 3;
-      }
+    }
   }
 
   message Actions {