From 34ebb5a082e8317dbc917e37f43e522e68bb606e Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Thu, 23 Nov 2017 11:06:10 +0100 Subject: [PATCH] SONAR-10088 Return available actions in api/qualitygates/list --- .../server/qualitygate/QualityGateModule.java | 2 + .../server/qualitygate/ws/ListAction.java | 38 +++------ .../server/qualitygate/ws/QGateWsSupport.java | 85 +++++++++++++++++++ .../server/qualitygate/ws/list-example.json | 19 ++++- .../qualitygate/QualityGateModuleTest.java | 2 +- .../server/qualitygate/ws/ListActionTest.java | 51 ++++++++++- .../src/main/protobuf/ws-qualitygates.proto | 13 +++ .../main/protobuf/ws-qualityprofiles.proto | 2 +- 8 files changed, 179 insertions(+), 33 deletions(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QGateWsSupport.java diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGateModule.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGateModule.java index 6e97f2355f1..ee9980dc112 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGateModule.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/QualityGateModule.java @@ -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, diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/ListAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/ListAction.java index fe9e93dfd13..a3fc1cbc0d2 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/ListAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/ListAction.java @@ -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 qualityGates = dbClient.qualityGateDao().selectAll(dbSession); writeProtobuf(buildResponse(qualityGates, defaultQualityGate), request, response); } } - private static ListWsResponse buildResponse(Collection qualityGates, @Nullable QualityGateDto defaultQualityGate) { + private ListWsResponse buildResponse(Collection 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 index 00000000000..0a67641c680 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QGateWsSupport.java @@ -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()); + } + +} diff --git a/server/sonar-server/src/main/resources/org/sonar/server/qualitygate/ws/list-example.json b/server/sonar-server/src/main/resources/org/sonar/server/qualitygate/ws/list-example.json index 547b8d4e863..270dc554203 100644 --- a/server/sonar-server/src/main/resources/org/sonar/server/qualitygate/ws/list-example.json +++ b/server/sonar-server/src/main/resources/org/sonar/server/qualitygate/ws/list-example.json @@ -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 } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGateModuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGateModuleTest.java index 622d9fc6236..36d9eba904b 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGateModuleTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/QualityGateModuleTest.java @@ -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); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/ListActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/ListActionTest.java index 83f27cd2025..108ef2da489 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/ListActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/ListActionTest.java @@ -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); diff --git a/sonar-ws/src/main/protobuf/ws-qualitygates.proto b/sonar-ws/src/main/protobuf/ws-qualitygates.proto index 57658d9996f..15e2ab342e3 100644 --- a/sonar-ws/src/main/protobuf/ws-qualitygates.proto +++ b/sonar-ws/src/main/protobuf/ws-qualitygates.proto @@ -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; } diff --git a/sonar-ws/src/main/protobuf/ws-qualityprofiles.proto b/sonar-ws/src/main/protobuf/ws-qualityprofiles.proto index e4d9df36abc..0b0039a2506 100644 --- a/sonar-ws/src/main/protobuf/ws-qualityprofiles.proto +++ b/sonar-ws/src/main/protobuf/ws-qualityprofiles.proto @@ -54,7 +54,7 @@ message SearchWsResponse { optional bool edit = 1; optional bool setAsDefault = 2; optional bool copy = 3; - } + } } message Actions { -- 2.39.5