--- /dev/null
+/*
+ * 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;
+
+import java.util.Optional;
+
+public interface EditionManagementState {
+ /**
+ * @return {@link Optional#empty() empty} if there is no edition installed
+ */
+ Optional<String> getCurrentEditionKey();
+
+ /**
+ * @return the pending installation status.
+ */
+ PendingStatus getPendingInstallationStatus();
+
+ /**
+ * @return {@link Optional#empty() empty} when {@link #getPendingInstallationStatus()} returns {@link PendingStatus#NONE},
+ * otherwise a {@link String}
+ */
+ Optional<String> getPendingEditionKey();
+
+ /**
+ * The license string.
+ *
+ * @return {@link Optional#empty() empty} when {@link #getPendingInstallationStatus()} returns {@link PendingStatus#NONE},
+ * otherwise a {@link String}
+ */
+ Optional<String> getPendingLicense();
+
+ enum PendingStatus {
+ NONE,
+ AUTOMATIC_IN_PROGRESS,
+ AUTOMATIC_READY,
+ AUTOMATIC_FAILED,
+ MANUAL_IN_PROGRESS
+ }
+}
--- /dev/null
+/*
+ * 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;
+
+import org.sonar.core.platform.Module;
+import org.sonar.server.edition.ws.EditionsWs;
+import org.sonar.server.edition.ws.StatusAction;
+
+public class EditionsWsModule extends Module {
+ @Override
+ protected void configureModule() {
+ add(
+ StandaloneEditionManagementStateImpl.class,
+ StatusAction.class,
+ EditionsWs.class);
+ }
+}
--- /dev/null
+/*
+ * 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;
+
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import javax.annotation.concurrent.Immutable;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+@Immutable
+public class License {
+ private final String editionKey;
+ private final List<String> pluginKeys;
+ private final String content;
+
+ public License(String editionKey, List<String> pluginKeys, String content) {
+ this.editionKey = enforceNotNullNorEmpty(editionKey, "editionKey");
+ this.pluginKeys = ImmutableList.copyOf(pluginKeys);
+ this.content = enforceNotNullNorEmpty(content, "content");
+ }
+
+ private static String enforceNotNullNorEmpty(String str, String propertyName) {
+ checkNotNull(str, "%s can't be null", propertyName);
+ checkArgument(!str.isEmpty(), "%s can't be empty", propertyName);
+ return str;
+ }
+
+ public String getEditionKey() {
+ return editionKey;
+ }
+
+ public List<String> getPluginKeys() {
+ return pluginKeys;
+ }
+
+ public String getContent() {
+ return content;
+ }
+}
--- /dev/null
+/*
+ * 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;
+
+/**
+ * Provides access to operations which will alter the Edition management state.
+ */
+public interface MutableEditionManagementState extends EditionManagementState {
+ /**
+ * Stage the specified license and records that an automatic install of the plugins will be performed.
+ *
+ * @return the new {@link PendingStatus}, always {@link PendingStatus#AUTOMATIC_IN_PROGRESS}
+ * an exception.
+ *
+ * @throws IllegalStateException if current status is not {@link PendingStatus#NONE}
+ */
+ PendingStatus startAutomaticInstall(License license);
+
+ /**
+ * Stage the specified license and records that a manual install of the plugins will be performed.
+ *
+ * @return the new {@link PendingStatus}, always {@link PendingStatus#MANUAL_IN_PROGRESS}
+ *
+ * @throws IllegalStateException if current status is not {@link PendingStatus#NONE}
+ */
+ PendingStatus startManualInstall(License license);
+
+ /**
+ * Records that the specified edition has been installed without plugin changes.
+ *
+ * @param newEditionKey can't be {@code null} nor empty
+ *
+ * @return the new {@link PendingStatus}, always {@link PendingStatus#NONE}
+ *
+ * @throws IllegalStateException if current status is not {@link PendingStatus#NONE}
+ */
+ PendingStatus newEditionWithoutInstall(String newEditionKey);
+
+ /**
+ * Records that automatic install is ready to be finalized.
+ *
+ * @return the new pending status, always {@link PendingStatus#AUTOMATIC_READY}
+ *
+ * @throws IllegalStateException if current status is not {@link PendingStatus#AUTOMATIC_IN_PROGRESS}
+ */
+ PendingStatus automaticInstallReady();
+
+ /**
+ * Finalize an automatic or manual install
+ *
+ * @return the new pending status, always {@link PendingStatus#NONE}
+ *
+ * @throws IllegalStateException if current status is neither {@link PendingStatus#AUTOMATIC_READY} nor
+ * {@link PendingStatus#MANUAL_IN_PROGRESS}
+ */
+ PendingStatus finalizeInstallation();
+}
--- /dev/null
+/*
+ * 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;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Map;
+import java.util.Optional;
+import org.picocontainer.Startable;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.property.InternalPropertiesDao;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+import static java.util.Optional.empty;
+import static org.sonar.server.edition.EditionManagementState.PendingStatus.AUTOMATIC_IN_PROGRESS;
+import static org.sonar.server.edition.EditionManagementState.PendingStatus.AUTOMATIC_READY;
+import static org.sonar.server.edition.EditionManagementState.PendingStatus.MANUAL_IN_PROGRESS;
+import static org.sonar.server.edition.EditionManagementState.PendingStatus.NONE;
+
+public class StandaloneEditionManagementStateImpl implements MutableEditionManagementState, Startable {
+ private static final String CURRENT_EDITION_KEY = "sonar.editionManagement.currentEditionKey";
+ private static final String PENDING_INSTALLATION_STATUS = "sonar.editionManagement.pendingInstallationStatus";
+ private static final String PENDING_EDITION_KEY = "sonar.editionManagement.pendingEditionKey";
+ private static final String PENDING_LICENSE = "sonar.editionManagement.pendingLicense";
+
+ private final DbClient dbClient;
+ private String currentEditionKey;
+ private PendingStatus pendingInstallationStatus = NONE;
+ private String pendingEditionKey;
+ private String pendingLicense;
+
+ public StandaloneEditionManagementStateImpl(DbClient dbClient) {
+ this.dbClient = dbClient;
+ }
+
+ @Override
+ public void start() {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ // load current state value
+ Map<String, Optional<String>> internalPropertyValues = dbClient.internalPropertiesDao().selectByKeys(dbSession,
+ ImmutableSet.of(CURRENT_EDITION_KEY, PENDING_INSTALLATION_STATUS, PENDING_EDITION_KEY, PENDING_LICENSE));
+ this.currentEditionKey = internalPropertyValues.getOrDefault(CURRENT_EDITION_KEY, empty())
+ .map(StandaloneEditionManagementStateImpl::emptyToNull)
+ .orElse(null);
+ this.pendingInstallationStatus = internalPropertyValues.getOrDefault(PENDING_INSTALLATION_STATUS, empty())
+ .map(PendingStatus::valueOf)
+ .orElse(NONE);
+ this.pendingEditionKey = internalPropertyValues.getOrDefault(PENDING_EDITION_KEY, empty())
+ .map(StandaloneEditionManagementStateImpl::emptyToNull)
+ .orElse(null);
+ this.pendingLicense = internalPropertyValues.getOrDefault(PENDING_LICENSE, empty()).orElse(null);
+ }
+ }
+
+ @Override
+ public void stop() {
+ // nothing to do
+ }
+
+ @Override
+ public Optional<String> getCurrentEditionKey() {
+ return Optional.ofNullable(currentEditionKey);
+ }
+
+ @Override
+ public PendingStatus getPendingInstallationStatus() {
+ return pendingInstallationStatus;
+ }
+
+ @Override
+ public Optional<String> getPendingEditionKey() {
+ return Optional.ofNullable(pendingEditionKey);
+ }
+
+ @Override
+ public Optional<String> getPendingLicense() {
+ return Optional.ofNullable(pendingLicense);
+ }
+
+ @Override
+ public synchronized PendingStatus startAutomaticInstall(License license) {
+ checkLicense(license);
+ changeStatusFromTo(NONE, AUTOMATIC_IN_PROGRESS);
+ this.pendingLicense = license.getContent();
+ this.pendingEditionKey = license.getEditionKey();
+ persistProperties();
+ return this.pendingInstallationStatus;
+ }
+
+ @Override
+ public synchronized PendingStatus startManualInstall(License license) {
+ checkLicense(license);
+ changeStatusFromTo(NONE, MANUAL_IN_PROGRESS);
+ this.pendingLicense = license.getContent();
+ this.pendingEditionKey = license.getEditionKey();
+ this.pendingInstallationStatus = MANUAL_IN_PROGRESS;
+ persistProperties();
+ return this.pendingInstallationStatus;
+ }
+
+ @Override
+ public synchronized PendingStatus newEditionWithoutInstall(String newEditionKey) {
+ requireNonNull(newEditionKey, "newEditionKey can't be null");
+ checkArgument(!newEditionKey.isEmpty(), "newEditionKey can't be empty");
+ changeStatusFromTo(NONE, NONE);
+ this.currentEditionKey = newEditionKey;
+ persistProperties();
+ return this.pendingInstallationStatus;
+ }
+
+ @Override
+ public synchronized PendingStatus automaticInstallReady() {
+ changeStatusFromTo(AUTOMATIC_IN_PROGRESS, AUTOMATIC_READY);
+ persistProperties();
+ return this.pendingInstallationStatus;
+ }
+
+ @Override
+ public synchronized PendingStatus finalizeInstallation() {
+ checkState(this.pendingInstallationStatus == AUTOMATIC_READY || this.pendingInstallationStatus == MANUAL_IN_PROGRESS,
+ "Can't finalize installation when state is %s", this.pendingInstallationStatus);
+
+ this.pendingInstallationStatus = NONE;
+ this.currentEditionKey = this.pendingEditionKey;
+ this.pendingEditionKey = null;
+ this.pendingLicense = null;
+ persistProperties();
+ return this.pendingInstallationStatus;
+ }
+
+ private void changeStatusFromTo(PendingStatus expectedStatus, PendingStatus newStatus) {
+ checkState(pendingInstallationStatus == expectedStatus,
+ "Can't move to {} when status is {} (should be {})",
+ newStatus, pendingInstallationStatus, expectedStatus);
+ this.pendingInstallationStatus = newStatus;
+ }
+
+ private void persistProperties() {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ InternalPropertiesDao internalPropertiesDao = dbClient.internalPropertiesDao();
+ if (pendingInstallationStatus == NONE) {
+ internalPropertiesDao.saveAsEmpty(dbSession, PENDING_EDITION_KEY);
+ internalPropertiesDao.saveAsEmpty(dbSession, PENDING_LICENSE);
+ } else {
+ internalPropertiesDao.save(dbSession, PENDING_EDITION_KEY, pendingEditionKey);
+ internalPropertiesDao.save(dbSession, PENDING_LICENSE, pendingLicense);
+ }
+ if (currentEditionKey == null) {
+ internalPropertiesDao.saveAsEmpty(dbSession, CURRENT_EDITION_KEY);
+ } else {
+ internalPropertiesDao.save(dbSession, CURRENT_EDITION_KEY, currentEditionKey);
+ }
+ internalPropertiesDao.save(dbSession, PENDING_INSTALLATION_STATUS, pendingInstallationStatus.name());
+ dbSession.commit();
+ }
+ }
+
+ private static void checkLicense(License license) {
+ requireNonNull(license, "license can't be null");
+ }
+
+ private static String emptyToNull(String s) {
+ return s.isEmpty() ? null : s;
+ }
+}
--- /dev/null
+/*
+ * 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.edition;
+
+import javax.annotation.ParametersAreNonnullByDefault;
--- /dev/null
+/*
+ * 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.Arrays;
+import org.sonar.api.server.ws.WebService;
+
+public class EditionsWs implements WebService {
+ private final EditionsWsAction[] actions;
+
+ public EditionsWs(EditionsWsAction[] actions) {
+ this.actions = actions;
+ }
+
+ @Override
+ public void define(Context context) {
+ NewController controller = context.createController("api/editions")
+ .setSince("6.7")
+ .setDescription("Manage SonarSource commercial editions.");
+ Arrays.stream(actions).forEach(action -> action.define(controller));
+ controller.done();
+ }
+}
--- /dev/null
+/*
+ * 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 org.sonar.server.ws.WsAction;
+
+/**
+ * Marker interface for actions of {@link EditionsWs}.
+ */
+public interface EditionsWsAction extends WsAction {
+}
--- /dev/null
+/*
+ * 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 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.user.UserSession;
+import org.sonar.server.ws.WsUtils;
+import org.sonarqube.ws.WsEditions;
+
+public class StatusAction implements EditionsWsAction {
+ private final UserSession userSession;
+ private final EditionManagementState editionManagementState;
+
+ public StatusAction(UserSession userSession, EditionManagementState editionManagementState) {
+ this.userSession = userSession;
+ this.editionManagementState = editionManagementState;
+ }
+
+ @Override
+ public void define(WebService.NewController controller) {
+ controller.createAction("status")
+ .setSince("6.7")
+ .setPost(false)
+ .setDescription("Provide status of SonarSource commercial edition of the current SonarQube. Require 'Administer System' permission.")
+ .setResponseExample(getClass().getResource("example-edition-status.json"))
+ .setHandler(this);
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+ userSession
+ .checkLoggedIn()
+ .checkIsSystemAdministrator();
+
+ WsEditions.StatusResponse.Builder responseBuilder = WsEditions.StatusResponse.newBuilder()
+ .setCurrentEditionKey(editionManagementState.getCurrentEditionKey().orElse(""))
+ .setNextEditionKey(editionManagementState.getPendingEditionKey().orElse(""))
+ .setInstallationStatus(WsEditions.InstallationStatus.valueOf(editionManagementState.getPendingInstallationStatus().name()));
+
+ WsUtils.writeProtobuf(responseBuilder.build(), request, response);
+ }
+}
--- /dev/null
+/*
+ * 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.edition.ws;
+
+import javax.annotation.ParametersAreNonnullByDefault;
import org.sonar.server.duplication.ws.DuplicationsParser;
import org.sonar.server.duplication.ws.DuplicationsWs;
import org.sonar.server.duplication.ws.ShowResponseBuilder;
+import org.sonar.server.edition.EditionsWsModule;
import org.sonar.server.email.ws.EmailsWsModule;
import org.sonar.server.es.IndexCreator;
import org.sonar.server.es.IndexDefinitions;
CeModule.class,
CeWsModule.class,
+ // SonarSource editions
+ EditionsWsModule.class,
+
InternalPropertiesImpl.class,
ProjectConfigurationFactory.class,
--- /dev/null
+{
+ "currentEditionKey": "",
+ "installationStatus": "AUTOMATIC_READY",
+ "nextEditionKey": "developer-edition"
+}
--- /dev/null
+/*
+ * 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;
+
+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 EditionsWsModuleTest {
+ private EditionsWsModule underTest = new EditionsWsModule();
+
+ @Test
+ public void verify_component_count() {
+ ComponentContainer container = new ComponentContainer();
+ underTest.configure(container);
+
+ assertThat(container.getPicoContainer().getComponentAdapters())
+ .hasSize(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 3);
+ }
+}
--- /dev/null
+/*
+ * 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;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Collections;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class LicenseTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Test
+ public void constructor_fails_if_editionKey_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("editionKey can't be null");
+
+ new License(null, Collections.emptyList(), "content");
+ }
+
+ @Test
+ public void constructor_fails_if_editionKey_is_empty() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("editionKey can't be empty");
+
+ new License("", Collections.emptyList(), "content");
+ }
+
+ @Test
+ public void constructor_fails_if_plugins_is_null() {
+ expectedException.expect(NullPointerException.class);
+
+ new License("edition-key", null, "content");
+ }
+
+ @Test
+ public void constructor_fails_if_content_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("content can't be null");
+
+ new License("edition-key", Collections.emptyList(), null);
+ }
+
+ @Test
+ public void constructor_fails_if_content_is_empty() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("content can't be empty");
+
+ new License("edition-key", Collections.emptyList(), "");
+ }
+
+ @Test
+ public void verify_getters() {
+ ImmutableList<String> pluginKeys = ImmutableList.of("a", "b", "c");
+
+ License underTest = new License("edition-key", pluginKeys, "content");
+
+ assertThat(underTest.getEditionKey()).isEqualTo("edition-key");
+ assertThat(underTest.getPluginKeys()).isEqualTo(pluginKeys);
+ assertThat(underTest.getContent()).isEqualTo("content");
+ }
+}
--- /dev/null
+/*
+ * 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.Optional;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.server.edition.EditionManagementState;
+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;
+
+public class StatusActionTest {
+ @Rule
+ public UserSessionRule userSessionRule = UserSessionRule.standalone();
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private EditionManagementState editionManagementState = mock(EditionManagementState.class);
+ private StatusAction underTest = new StatusAction(userSessionRule, editionManagementState);
+ private WsActionTester actionTester = new WsActionTester(underTest);
+
+ @Test
+ public void verify_definition() {
+ WebService.Action def = actionTester.getDef();
+ assertThat(def.key()).isEqualTo("status");
+ assertThat(def.since()).isEqualTo("6.7");
+ assertThat(def.isPost()).isFalse();
+ assertThat(def.description()).isNotEmpty();
+ assertThat(def.params()).isEmpty();
+ }
+
+ @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 verify_example() {
+ userSessionRule.logIn().setSystemAdministrator();
+ when(editionManagementState.getCurrentEditionKey()).thenReturn(Optional.empty());
+ when(editionManagementState.getPendingEditionKey()).thenReturn(Optional.of("developer-edition"));
+ when(editionManagementState.getPendingInstallationStatus()).thenReturn(EditionManagementState.PendingStatus.AUTOMATIC_READY);
+
+ TestRequest request = actionTester.newRequest();
+
+ JsonAssert.assertJson(request.execute().getInput()).isSimilarTo(actionTester.getDef().responseExampleAsString());
+ }
+
+ @Test
+ public void response_contains_optional_fields_as_empty_string() {
+ userSessionRule.logIn().setSystemAdministrator();
+ when(editionManagementState.getCurrentEditionKey()).thenReturn(Optional.empty());
+ when(editionManagementState.getPendingEditionKey()).thenReturn(Optional.empty());
+ when(editionManagementState.getPendingInstallationStatus()).thenReturn(EditionManagementState.PendingStatus.NONE);
+
+ TestRequest request = actionTester.newRequest();
+
+ JsonAssert.assertJson(request.execute().getInput())
+ .isSimilarTo("{" +
+ " \"currentEditionKey\": \"\"," +
+ " \"installationStatus\": \"NONE\"," +
+ " \"nextEditionKey\": \"\"" +
+ "}");
+ }
+}
--- /dev/null
+// SonarQube, open source software quality management tool.
+// Copyright (C) 2008-2016 SonarSource
+// mailto:contact AT sonarsource DOT com
+//
+// SonarQube 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.
+//
+// SonarQube 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.
+
+syntax = "proto2";
+
+package sonarqube.ws.editions;
+
+option java_package = "org.sonarqube.ws";
+option java_outer_classname = "WsEditions";
+option optimize_for = SPEED;
+
+// GET api/editions/status
+message StatusResponse {
+ optional string currentEditionKey = 1;
+ optional InstallationStatus installationStatus = 2;
+ optional string nextEditionKey = 3;
+}
+
+enum InstallationStatus {
+ NONE = 0;
+ AUTOMATIC_IN_PROGRESS = 1;
+ AUTOMATIC_READY = 2;
+ AUTOMATIC_FAILED = 3;
+ MANUAL_IN_PROGRESS = 4;
+}