From 27ab750cd6c6f9c74f46ecd852661f323860b7ed Mon Sep 17 00:00:00 2001 From: =?utf8?q?S=C3=A9bastien=20Lesaint?= Date: Fri, 13 Oct 2017 14:26:52 +0200 Subject: [PATCH] SONAR-9939 add WS api/editions/preview (mock implementation) same mock implementation as for api/editions/apply_license --- .../server/edition/EditionsWsModule.java | 2 + .../server/edition/ws/ApplyLicenseAction.java | 4 +- .../server/edition/ws/PreviewAction.java | 116 ++++++++++++++ .../edition/ws/example-edition-preview.json | 4 + .../server/edition/EditionsWsModuleTest.java | 2 +- .../server/edition/ws/PreviewActionTest.java | 141 ++++++++++++++++++ sonar-ws/src/main/protobuf/ws-editions.proto | 12 ++ 7 files changed, 278 insertions(+), 3 deletions(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/edition/ws/PreviewAction.java create mode 100644 server/sonar-server/src/main/resources/org/sonar/server/edition/ws/example-edition-preview.json create mode 100644 server/sonar-server/src/test/java/org/sonar/server/edition/ws/PreviewActionTest.java diff --git a/server/sonar-server/src/main/java/org/sonar/server/edition/EditionsWsModule.java b/server/sonar-server/src/main/java/org/sonar/server/edition/EditionsWsModule.java index 1f22a1c3182..e62724d7d51 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/edition/EditionsWsModule.java +++ b/server/sonar-server/src/main/java/org/sonar/server/edition/EditionsWsModule.java @@ -22,6 +22,7 @@ package org.sonar.server.edition; import org.sonar.core.platform.Module; import org.sonar.server.edition.ws.ApplyLicenseAction; import org.sonar.server.edition.ws.EditionsWs; +import org.sonar.server.edition.ws.PreviewAction; import org.sonar.server.edition.ws.StatusAction; public class EditionsWsModule extends Module { @@ -30,6 +31,7 @@ public class EditionsWsModule extends Module { add( StatusAction.class, ApplyLicenseAction.class, + PreviewAction.class, EditionsWs.class); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/edition/ws/ApplyLicenseAction.java b/server/sonar-server/src/main/java/org/sonar/server/edition/ws/ApplyLicenseAction.java index 75dc8356053..da7bb813090 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/edition/ws/ApplyLicenseAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/edition/ws/ApplyLicenseAction.java @@ -34,8 +34,8 @@ import org.sonarqube.ws.WsEditions; public class ApplyLicenseAction implements EditionsWsAction { private static final String PARAM_LICENSE = "license"; - private UserSession userSession; - private MutableEditionManagementState editionManagementState; + private final UserSession userSession; + private final MutableEditionManagementState editionManagementState; public ApplyLicenseAction(UserSession userSession, MutableEditionManagementState editionManagementState) { this.userSession = userSession; diff --git a/server/sonar-server/src/main/java/org/sonar/server/edition/ws/PreviewAction.java b/server/sonar-server/src/main/java/org/sonar/server/edition/ws/PreviewAction.java new file mode 100644 index 00000000000..15031634963 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/edition/ws/PreviewAction.java @@ -0,0 +1,116 @@ +/* + * 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.edition.ws; + +import java.util.Collections; +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.server.edition.EditionManagementState; +import org.sonar.server.edition.License; +import org.sonar.server.exceptions.BadRequestException; +import org.sonar.server.user.UserSession; +import org.sonar.server.ws.WsUtils; +import org.sonarqube.ws.WsEditions; + +import static java.util.Optional.ofNullable; +import static org.sonarqube.ws.WsEditions.PreviewStatus.AUTOMATIC_INSTALL; +import static org.sonarqube.ws.WsEditions.PreviewStatus.MANUAL_INSTALL; +import static org.sonarqube.ws.WsEditions.PreviewStatus.NO_INSTALL; + +public class PreviewAction implements EditionsWsAction { + private static final String PARAM_LICENSE = "license"; + + private final UserSession userSession; + private final EditionManagementState editionManagementState; + + public PreviewAction(UserSession userSession, EditionManagementState editionManagementState) { + this.userSession = userSession; + this.editionManagementState = editionManagementState; + } + + @Override + public void define(WebService.NewController controller) { + WebService.NewAction action = controller.createAction("preview") + .setSince("6.7") + .setPost(true) + .setDescription("Preview the changes to SonarQube to match the specified license. Require 'Administer System' permission.") + .setResponseExample(getClass().getResource("example-edition-preview.json")) + .setHandler(this); + + action.createParam(PARAM_LICENSE) + .setRequired(true) + .setSince("6.7") + .setDescription("the license"); + } + + @Override + public void handle(Request request, Response response) throws Exception { + userSession.checkLoggedIn().checkIsSystemAdministrator(); + + if (editionManagementState.getPendingInstallationStatus() != EditionManagementState.PendingStatus.NONE) { + throw BadRequestException.create("Can't apply a license when applying one is already in progress"); + } + + String license = request.mandatoryParam(PARAM_LICENSE); + License newLicense = new License(license, Collections.emptyList(), license); + NextState nextState = computeNextState(newLicense); + + WsUtils.writeProtobuf(buildResponse(nextState), request, response); + } + + private static WsEditions.PreviewResponse buildResponse(NextState nextState) { + return WsEditions.PreviewResponse.newBuilder() + .setNextEditionKey(nextState.getPendingEditionKey().orElse("")) + .setPreviewStatus(nextState.getPreviewStatus()) + .build(); + } + + private static NextState computeNextState(License newLicense) { + String licenseContent = newLicense.getContent(); + if (licenseContent.contains("manual")) { + return new NextState(newLicense.getEditionKey(), MANUAL_INSTALL); + } else if (licenseContent.contains("done")) { + return new NextState(newLicense.getEditionKey(), NO_INSTALL); + } + return new NextState(newLicense.getEditionKey(), AUTOMATIC_INSTALL); + } + + private static final class NextState { + private final String pendingEditionKey; + private final WsEditions.PreviewStatus previewStatus; + + private NextState(@Nullable String pendingEditionKey, WsEditions.PreviewStatus previewStatus) { + this.pendingEditionKey = pendingEditionKey; + this.previewStatus = previewStatus; + } + + Optional getPendingEditionKey() { + return ofNullable(pendingEditionKey); + } + + WsEditions.PreviewStatus getPreviewStatus() { + return previewStatus; + } + } + +} diff --git a/server/sonar-server/src/main/resources/org/sonar/server/edition/ws/example-edition-preview.json b/server/sonar-server/src/main/resources/org/sonar/server/edition/ws/example-edition-preview.json new file mode 100644 index 00000000000..2427d69c401 --- /dev/null +++ b/server/sonar-server/src/main/resources/org/sonar/server/edition/ws/example-edition-preview.json @@ -0,0 +1,4 @@ +{ + "nextEditionKey": "developer-edition", + "previewStatus": "AUTOMATIC_INSTALL" +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/edition/EditionsWsModuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/edition/EditionsWsModuleTest.java index c4fae543a77..116a4f4f2cd 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/edition/EditionsWsModuleTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/edition/EditionsWsModuleTest.java @@ -34,6 +34,6 @@ public class EditionsWsModuleTest { underTest.configure(container); assertThat(container.getPicoContainer().getComponentAdapters()) - .hasSize(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 3); + .hasSize(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 4); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/edition/ws/PreviewActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/edition/ws/PreviewActionTest.java new file mode 100644 index 00000000000..46efe099940 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/edition/ws/PreviewActionTest.java @@ -0,0 +1,141 @@ +/* + * 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.edition.ws; + +import com.tngtech.java.junit.dataprovider.DataProvider; +import com.tngtech.java.junit.dataprovider.DataProviderRunner; +import com.tngtech.java.junit.dataprovider.UseDataProvider; +import java.util.Arrays; +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.server.edition.EditionManagementState; +import org.sonar.server.exceptions.BadRequestException; +import org.sonar.server.exceptions.ForbiddenException; +import org.sonar.server.exceptions.UnauthorizedException; +import org.sonar.server.tester.UserSessionRule; +import org.sonar.server.ws.TestRequest; +import org.sonar.server.ws.WsActionTester; +import org.sonar.test.JsonAssert; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.sonar.server.edition.EditionManagementState.PendingStatus.NONE; + +@RunWith(DataProviderRunner.class) +public class PreviewActionTest { + private static final String PARAM_LICENSE = "license"; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + @Rule + public UserSessionRule userSessionRule = UserSessionRule.standalone(); + + private EditionManagementState editionManagementState = mock(EditionManagementState.class); + private PreviewAction underTest = new PreviewAction(userSessionRule, editionManagementState); + private WsActionTester actionTester = new WsActionTester(underTest); + + @Test + public void verify_definition() { + WebService.Action def = actionTester.getDef(); + + assertThat(def.key()).isEqualTo("preview"); + assertThat(def.since()).isEqualTo("6.7"); + assertThat(def.isPost()).isTrue(); + assertThat(def.isInternal()).isFalse(); + assertThat(def.description()).isNotEmpty(); + assertThat(def.params()).hasSize(1); + + WebService.Param licenseParam = def.param("license"); + assertThat(licenseParam.isRequired()).isTrue(); + assertThat(licenseParam.description()).isNotEmpty(); + } + + @Test + public void request_fails_if_user_not_logged_in() { + userSessionRule.anonymous(); + TestRequest request = actionTester.newRequest(); + + expectedException.expect(UnauthorizedException.class); + expectedException.expectMessage("Authentication is required"); + + request.execute(); + } + + @Test + public void request_fails_if_user_is_not_system_administer() { + userSessionRule.logIn(); + TestRequest request = actionTester.newRequest(); + + expectedException.expect(ForbiddenException.class); + expectedException.expectMessage("Insufficient privileges"); + + request.execute(); + } + + @Test + public void request_fails_if_license_param_is_not_provided() { + userSessionRule.logIn().setSystemAdministrator(); + when(editionManagementState.getPendingInstallationStatus()).thenReturn(NONE); + TestRequest request = actionTester.newRequest(); + + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("The 'license' parameter is missing"); + + request.execute(); + } + + @Test + @UseDataProvider("notNonePendingInstallationStatuses") + public void request_fails_with_BadRequestException_is_pendingStatus_is_not_NONE(EditionManagementState.PendingStatus notNone) { + userSessionRule.logIn().setSystemAdministrator(); + when(editionManagementState.getPendingInstallationStatus()).thenReturn(notNone); + TestRequest request = actionTester.newRequest() + .setParam(PARAM_LICENSE, "foo"); + + expectedException.expect(BadRequestException.class); + expectedException.expectMessage("Can't apply a license when applying one is already in progress"); + + request.execute(); + } + + @Test + public void verify_example() { + userSessionRule.logIn().setSystemAdministrator(); + when(editionManagementState.getPendingInstallationStatus()).thenReturn(NONE); + + TestRequest request = actionTester.newRequest() + .setParam(PARAM_LICENSE, "developer-edition"); + + JsonAssert.assertJson(request.execute().getInput()).isSimilarTo(actionTester.getDef().responseExampleAsString()); + } + + @DataProvider + public static Object[][] notNonePendingInstallationStatuses() { + return Arrays.stream(EditionManagementState.PendingStatus.values()) + .filter(s -> s != NONE) + .map(s -> new Object[] {s}) + .toArray(Object[][]::new); + } + +} diff --git a/sonar-ws/src/main/protobuf/ws-editions.proto b/sonar-ws/src/main/protobuf/ws-editions.proto index 8f173acc923..791ad47215f 100644 --- a/sonar-ws/src/main/protobuf/ws-editions.proto +++ b/sonar-ws/src/main/protobuf/ws-editions.proto @@ -39,3 +39,15 @@ enum InstallationStatus { AUTOMATIC_FAILED = 3; MANUAL_IN_PROGRESS = 4; } + +// POST api/editions/preview +message PreviewResponse { + optional string nextEditionKey = 1; + optional PreviewStatus previewStatus = 2; +} + +enum PreviewStatus { + NO_INSTALL = 0; + AUTOMATIC_INSTALL = 1; + MANUAL_INSTALL = 2; +} -- 2.39.5