}
// This is a special entry point for the case of a configurable migration
- public final void migrate(List<String> uuids) throws SQLException {
+ public void migrate(List<String> uuids) throws SQLException {
try (Connection readConnection = createDdlConnection();
Connection writeConnection = createDdlConnection()) {
Context context = new Context(db, readConnection, writeConnection);
*/
package org.sonar.server.platform.db.migration.adhoc;
+import org.sonar.api.server.ServerSide;
import org.sonar.db.Database;
+@ServerSide
public class AddMeasuresMigratedColumnToPortfoliosTable extends AbstractAddMeasuresMigratedColumnToTable {
static final String PORTFOLIOS_TABLE_NAME = "portfolios";
*/
package org.sonar.server.platform.db.migration.adhoc;
+import org.sonar.api.server.ServerSide;
import org.sonar.db.Database;
+@ServerSide
public class AddMeasuresMigratedColumnToProjectBranchesTable extends AbstractAddMeasuresMigratedColumnToTable {
public static final String PROJECT_BRANCHES_TABLE_NAME = "project_branches";
*/
package org.sonar.server.platform.db.migration.adhoc;
+import org.sonar.api.server.ServerSide;
import org.sonar.db.Database;
+@ServerSide
public class CreateIndexOnPortfoliosMeasuresMigrated extends AbstractCreateIndexOnMeasuresMigrated {
static final String TABLE_NAME = "portfolios";
*/
package org.sonar.server.platform.db.migration.adhoc;
+import org.sonar.api.server.ServerSide;
import org.sonar.db.Database;
+@ServerSide
public class CreateIndexOnProjectBranchesMeasuresMigrated extends AbstractCreateIndexOnMeasuresMigrated {
static final String TABLE_NAME = "project_branches";
package org.sonar.server.platform.db.migration.adhoc;
import java.sql.SQLException;
+import org.sonar.api.server.ServerSide;
import org.sonar.db.Database;
import org.sonar.server.platform.db.migration.sql.CreateTableBuilder;
import org.sonar.server.platform.db.migration.step.CreateTableChange;
import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.UUID_SIZE;
import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;
+@ServerSide
public class CreateMeasuresTable extends CreateTableChange {
static final String MEASURES_TABLE_NAME = "measures";
static final String COLUMN_COMPONENT_UUID = "component_uuid";
*/
package org.sonar.server.platform.db.migration.adhoc;
+import org.sonar.api.server.ServerSide;
import org.sonar.api.utils.System2;
import org.sonar.db.Database;
+@ServerSide
public class MigrateBranchesLiveMeasuresToMeasures extends AbstractMigrateLiveMeasuresToMeasures {
public MigrateBranchesLiveMeasuresToMeasures(Database db, System2 system2) {
*/
package org.sonar.server.platform.db.migration.adhoc;
+import org.sonar.api.server.ServerSide;
import org.sonar.api.utils.System2;
import org.sonar.db.Database;
+@ServerSide
public class MigratePortfoliosLiveMeasuresToMeasures extends AbstractMigrateLiveMeasuresToMeasures {
- protected MigratePortfoliosLiveMeasuresToMeasures(Database db, System2 system2) {
+ public MigratePortfoliosLiveMeasuresToMeasures(Database db, System2 system2) {
super(db, system2, "portfolios", "portfolio");
}
}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.platform.ws;
-
-import com.google.common.io.Resources;
-import java.sql.SQLException;
-import java.util.List;
-import org.sonar.api.server.ws.Request;
-import org.sonar.api.server.ws.Response;
-import org.sonar.api.server.ws.WebService;
-import org.sonar.api.utils.text.JsonWriter;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.server.platform.db.migration.adhoc.MigrateBranchesLiveMeasuresToMeasures;
-import org.sonar.server.platform.db.migration.adhoc.MigratePortfoliosLiveMeasuresToMeasures;
-import org.sonar.server.user.UserSession;
-
-import static java.lang.String.format;
-import static java.util.Optional.ofNullable;
-
-public class MigrateMeasuresAction implements SystemWsAction {
- public static final String SYSTEM_MEASURES_MIGRATION_ENABLED = "system.measures.migration.enabled";
- public static final String PARAM_SIZE = "size";
-
- private final UserSession userSession;
- private final DbClient dbClient;
- private final MigrateBranchesLiveMeasuresToMeasures branchesMigration;
- private final MigratePortfoliosLiveMeasuresToMeasures portfoliosMigration;
-
- public MigrateMeasuresAction(UserSession userSession, DbClient dbClient,
- MigrateBranchesLiveMeasuresToMeasures branchesMigration, MigratePortfoliosLiveMeasuresToMeasures portfoliosMigration) {
- this.userSession = userSession;
- this.dbClient = dbClient;
- this.branchesMigration = branchesMigration;
- this.portfoliosMigration = portfoliosMigration;
- }
-
- @Override
- public void define(WebService.NewController controller) {
- WebService.NewAction action = controller.createAction("migrate_measures")
- .setDescription("Prepare the migration to the next major version of SonarQube." +
- "<br/>" +
- "Sending a POST request to this URL will migrate some rows from the 'live_measures' to the 'measures' table. " +
- "Requires system administration permission.")
- .setSince("9.9.8")
- .setPost(true)
- .setHandler(this)
- .setInternal(true)
- .setResponseExample(Resources.getResource(this.getClass(), "example-migrate_measures.json"));
-
- action.createParam(PARAM_SIZE)
- .setDescription("The number of branches or portfolios to migrate")
- .setDefaultValue(10);
- }
-
- @Override
- public void handle(Request request, Response response) throws Exception {
- userSession.checkIsSystemAdministrator();
-
- if (!isMigrationEnabled()) {
- throw new IllegalStateException("Migration is not enabled. Please call the endpoint /api/system/prepare_migration?enable=true and retry.");
- }
-
- int size = request.mandatoryParamAsInt(PARAM_SIZE);
- if (size <= 0) {
- throw new IllegalArgumentException("Size must be greater than 0");
- }
-
- int migratedItems = migrateBranches(size);
- if (migratedItems < size) {
- int remainingSize = size - migratedItems;
- migratedItems += migratePortfolios(remainingSize);
- }
-
- BranchStats statistics = getStatistics();
- try (JsonWriter json = response.newJsonWriter()) {
- json.beginObject()
- .prop("status", "success")
- .prop("message", format("%s branches or portfolios migrated", migratedItems))
- .prop("remainingBranches", statistics.remainingBranches)
- .prop("totalBranches", statistics.totalBranches)
- .prop("remainingPortfolios", statistics.remainingPortfolios)
- .prop("totalPortfolios", statistics.totalPortfolios)
- .endObject();
- }
- }
-
- private int migrateBranches(int size) throws SQLException {
- List<String> branchesToMigrate = getBranchesToMigrate(size);
- if (!branchesToMigrate.isEmpty()) {
- branchesMigration.migrate(branchesToMigrate);
- }
- return branchesToMigrate.size();
- }
-
- private List<String> getBranchesToMigrate(int size) {
- try (DbSession dbSession = dbClient.openSession(false)) {
- return dbClient.branchDao().selectUuidsWithMeasuresMigratedFalse(dbSession, size);
- }
- }
-
- private int migratePortfolios(int size) throws SQLException {
- List<String> portfoliosToMigrate = getPortfoliosToMigrate(size);
- if (!portfoliosToMigrate.isEmpty()) {
- portfoliosMigration.migrate(portfoliosToMigrate);
- }
- return portfoliosToMigrate.size();
- }
-
- private List<String> getPortfoliosToMigrate(int size) {
- try (DbSession dbSession = dbClient.openSession(false)) {
- return dbClient.portfolioDao().selectUuidsWithMeasuresMigratedFalse(dbSession, size);
- }
- }
-
- private boolean isMigrationEnabled() {
- return ofNullable(dbClient.propertiesDao().selectGlobalProperty(SYSTEM_MEASURES_MIGRATION_ENABLED))
- .map(p -> Boolean.parseBoolean(p.getValue()))
- .orElse(false);
- }
-
- private BranchStats getStatistics() {
- try (DbSession dbSession = dbClient.openSession(false)) {
- int remainingBranches = dbClient.branchDao().countByMeasuresMigratedFalse(dbSession);
- int totalBranches = dbClient.branchDao().countAll(dbSession);
- int remainingPortfolios = dbClient.portfolioDao().countByMeasuresMigratedFalse(dbSession);
- int totalPortfolios = dbClient.portfolioDao().selectAll(dbSession).size();
-
- return new BranchStats(remainingBranches, totalBranches, remainingPortfolios, totalPortfolios);
- }
- }
-
- private record BranchStats(int remainingBranches, int totalBranches, int remainingPortfolios, int totalPortfolios) {
- }
-
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.platform.ws;
-
-import com.google.common.io.Resources;
-import java.sql.SQLException;
-import org.sonar.api.server.ws.Request;
-import org.sonar.api.server.ws.Response;
-import org.sonar.api.server.ws.WebService;
-import org.sonar.api.server.ws.WebService.NewAction;
-import org.sonar.api.utils.text.JsonWriter;
-import org.sonar.db.DbClient;
-import org.sonar.db.property.PropertyDto;
-import org.sonar.server.platform.db.migration.adhoc.AddMeasuresMigratedColumnToPortfoliosTable;
-import org.sonar.server.platform.db.migration.adhoc.AddMeasuresMigratedColumnToProjectBranchesTable;
-import org.sonar.server.platform.db.migration.adhoc.CreateIndexOnPortfoliosMeasuresMigrated;
-import org.sonar.server.platform.db.migration.adhoc.CreateIndexOnProjectBranchesMeasuresMigrated;
-import org.sonar.server.platform.db.migration.adhoc.CreateMeasuresTable;
-import org.sonar.server.user.UserSession;
-
-import static java.lang.String.format;
-import static org.sonar.core.config.CorePropertyDefinitions.SYSTEM_MEASURES_MIGRATION_ENABLED;
-
-/**
- * Implementation of the {@code prepare_migration} action for the System WebService.
- */
-public class PrepareMigrationAction implements SystemWsAction {
-
- public static final String PARAM_ENABLE = "enable";
- private final UserSession userSession;
- private final DbClient dbClient;
- private final CreateMeasuresTable createMeasuresTable;
- private final AddMeasuresMigratedColumnToProjectBranchesTable addMeasuresMigratedColumnToProjectBranchesTable;
- private final AddMeasuresMigratedColumnToPortfoliosTable addMeasuresMigratedColumnToPortfoliosTable;
- private final CreateIndexOnProjectBranchesMeasuresMigrated createIndexOnProjectBranchesMeasuresMigrated;
- private final CreateIndexOnPortfoliosMeasuresMigrated createIndexOnPortfoliosMeasuresMigrated;
-
- public PrepareMigrationAction(UserSession userSession, DbClient dbClient, CreateMeasuresTable createMeasuresTable,
- AddMeasuresMigratedColumnToProjectBranchesTable addMeasuresMigratedColumnToProjectBranchesTable,
- AddMeasuresMigratedColumnToPortfoliosTable addMeasuresMigratedColumnToPortfoliosTable,
- CreateIndexOnProjectBranchesMeasuresMigrated createIndexOnProjectBranchesMeasuresMigrated,
- CreateIndexOnPortfoliosMeasuresMigrated createIndexOnPortfoliosMeasuresMigrated) {
- this.userSession = userSession;
- this.dbClient = dbClient;
- this.createMeasuresTable = createMeasuresTable;
- this.addMeasuresMigratedColumnToProjectBranchesTable = addMeasuresMigratedColumnToProjectBranchesTable;
- this.addMeasuresMigratedColumnToPortfoliosTable = addMeasuresMigratedColumnToPortfoliosTable;
- this.createIndexOnProjectBranchesMeasuresMigrated = createIndexOnProjectBranchesMeasuresMigrated;
- this.createIndexOnPortfoliosMeasuresMigrated = createIndexOnPortfoliosMeasuresMigrated;
- }
-
- @Override
- public void define(WebService.NewController controller) {
- NewAction action = controller.createAction("prepare_migration")
- .setDescription("Prepare the migration to the next major version of SonarQube." +
- "<br/>" +
- "Sending a POST request to this URL enables the 'live_measures' table migration. " +
- "It is strongly advised to <strong>make a database backup</strong> before invoking this WS. " +
- "Requires system administration permission.")
- .setSince("9.9.8")
- .setPost(true)
- .setHandler(this)
- .setInternal(true)
- .setResponseExample(Resources.getResource(this.getClass(), "example-prepare_migration.json"));
-
- action.createParam(PARAM_ENABLE)
- .setDescription("Set to true to enable the migration mode. Set to false to disable.")
- .setBooleanPossibleValues()
- .setRequired(true);
- }
-
- @Override
- public void handle(Request request, Response response) throws Exception {
- userSession.checkIsSystemAdministrator();
-
- boolean enable = request.mandatoryParamAsBoolean(PARAM_ENABLE);
- if (enable) {
- updateDdl();
- }
- updateProperty(enable);
-
- try (JsonWriter json = response.newJsonWriter()) {
- json.beginObject()
- .prop("message", format("The 'live_measures' migration mode is %s", enable ? "enabled" : "disabled"))
- .endObject();
- }
- }
-
- private void updateDdl() throws SQLException {
- createMeasuresTable.execute();
- addMeasuresMigratedColumnToProjectBranchesTable.execute();
- addMeasuresMigratedColumnToPortfoliosTable.execute();
- createIndexOnProjectBranchesMeasuresMigrated.execute();
- createIndexOnPortfoliosMeasuresMigrated.execute();
- }
-
- private void updateProperty(boolean enable) {
- dbClient.propertiesDao().saveProperty(new PropertyDto().setKey(SYSTEM_MEASURES_MIGRATION_ENABLED).setValue(Boolean.toString(enable)));
- }
-
-}
package org.sonar.server.platform.ws;
import org.sonar.core.platform.Module;
-import org.sonar.server.platform.db.migration.adhoc.AddMeasuresMigratedColumnToPortfoliosTable;
-import org.sonar.server.platform.db.migration.adhoc.AddMeasuresMigratedColumnToProjectBranchesTable;
-import org.sonar.server.platform.db.migration.adhoc.CreateIndexOnPortfoliosMeasuresMigrated;
-import org.sonar.server.platform.db.migration.adhoc.CreateIndexOnProjectBranchesMeasuresMigrated;
-import org.sonar.server.platform.db.migration.adhoc.CreateMeasuresTable;
-import org.sonar.server.platform.db.migration.adhoc.MigrateBranchesLiveMeasuresToMeasures;
-import org.sonar.server.platform.db.migration.adhoc.MigratePortfoliosLiveMeasuresToMeasures;
public class SystemWsModule extends Module {
LivenessActionSupport.class,
LivenessAction.class,
- CreateMeasuresTable.class,
- AddMeasuresMigratedColumnToProjectBranchesTable.class,
- AddMeasuresMigratedColumnToPortfoliosTable.class,
- CreateIndexOnProjectBranchesMeasuresMigrated.class,
- CreateIndexOnPortfoliosMeasuresMigrated.class,
- PrepareMigrationAction.class,
-
- MigrateBranchesLiveMeasuresToMeasures.class,
- MigratePortfoliosLiveMeasuresToMeasures.class,
- MigrateMeasuresAction.class,
-
InfoAction.class,
LogsAction.class,
MigrateDbAction.class,
+++ /dev/null
-{
- "status": "success",
- "message": "2 branches or portfolios migrated",
- "remainingBranches": 1,
- "totalBranches": 3,
- "remainingPortfolios": 1,
- "totalPortfolios": 2
-}
+++ /dev/null
-{
- "message": "The 'live_measures' migration mode is enabled"
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.platform.ws;
-
-import com.google.gson.Gson;
-import com.tngtech.java.junit.dataprovider.DataProvider;
-import com.tngtech.java.junit.dataprovider.DataProviderRunner;
-import java.sql.SQLException;
-import java.util.List;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.sonar.db.Database;
-import org.sonar.db.DbTester;
-import org.sonar.db.component.BranchType;
-import org.sonar.db.component.ComponentDto;
-import org.sonar.db.portfolio.PortfolioDto;
-import org.sonar.db.property.PropertyDto;
-import org.sonar.server.exceptions.ForbiddenException;
-import org.sonar.server.platform.db.migration.adhoc.AddMeasuresMigratedColumnToPortfoliosTable;
-import org.sonar.server.platform.db.migration.adhoc.AddMeasuresMigratedColumnToProjectBranchesTable;
-import org.sonar.server.platform.db.migration.adhoc.MigrateBranchesLiveMeasuresToMeasures;
-import org.sonar.server.platform.db.migration.adhoc.MigratePortfoliosLiveMeasuresToMeasures;
-import org.sonar.server.tester.UserSessionRule;
-import org.sonar.server.ws.TestRequest;
-import org.sonar.server.ws.TestResponse;
-import org.sonar.server.ws.WsActionTester;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
-import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
-import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoInteractions;
-import static org.sonar.db.component.BranchType.BRANCH;
-import static org.sonar.server.platform.ws.MigrateMeasuresAction.SYSTEM_MEASURES_MIGRATION_ENABLED;
-import static org.sonar.test.JsonAssert.assertJson;
-
-@RunWith(DataProviderRunner.class)
-public class MigrateMeasuresActionTest {
- private static final Gson GSON = new Gson();
-
- public static final String PARAM_SIZE = "size";
- @Rule
- public UserSessionRule userSessionRule = UserSessionRule.standalone().logIn().setSystemAdministrator();
-
- @Rule
- public DbTester dbTester = DbTester.create();
-
- private final MigrateBranchesLiveMeasuresToMeasures measuresMigration = mock();
- private final MigratePortfoliosLiveMeasuresToMeasures portfoliosMigration = mock();
- private final MigrateMeasuresAction underTest = new MigrateMeasuresAction(userSessionRule, dbTester.getDbClient(), measuresMigration, portfoliosMigration);
- private final WsActionTester tester = new WsActionTester(underTest);
-
- @Test
- public void should_throw_if_migration_is_not_enabled() {
- TestRequest request = tester.newRequest();
-
- assertThatIllegalStateException()
- .isThrownBy(request::execute)
- .withMessage("Migration is not enabled. Please call the endpoint /api/system/prepare_migration?enable=true and retry.");
- }
-
- @Test
- @DataProvider(value = {"0", "-1", "-100"})
- public void should_throws_IAE_if_size_in_invalid(int size) throws SQLException {
- enableMigration();
-
- TestRequest request = tester
- .newRequest()
- .setParam(PARAM_SIZE, Integer.toString(size));
-
- assertThatIllegalArgumentException()
- .isThrownBy(request::execute)
- .withMessage("Size must be greater than 0");
- }
-
- @Test
- public void verify_example() throws SQLException {
- enableMigration();
- // 3 branches, 2 migrated
- ComponentDto project = dbTester.components().insertPrivateProject();
- ComponentDto branch1 = dbTester.components().insertProjectBranch(project, b -> b.setBranchType(BRANCH));
- dbTester.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.BRANCH));
- dbTester.getDbClient().branchDao().updateMeasuresMigrated(dbTester.getSession(), project.branchUuid(), true);
- dbTester.getDbClient().branchDao().updateMeasuresMigrated(dbTester.getSession(), branch1.branchUuid(), true);
- // 2 portfolios, 1 migrated
- PortfolioDto portfolio1 = dbTester.components().insertPrivatePortfolioDto("name1");
- dbTester.components().insertPrivatePortfolioDto("name2");
- dbTester.getDbClient().portfolioDao().updateMeasuresMigrated(dbTester.getSession(), portfolio1.getUuid(), true);
- dbTester.getSession().commit();
-
- TestResponse response = tester.newRequest()
- .execute();
-
- assertJson(response.getInput()).isSimilarTo(getClass().getResource("example-migrate_measures.json"));
- }
-
- @Test
- public void does_not_migrate_portfolios_if_measures_are_not_finished() throws SQLException {
- enableMigration();
- // 2 branches
- ComponentDto project = dbTester.components().insertPrivateProject();
- ComponentDto branch = dbTester.components().insertProjectBranch(project, b -> b.setBranchType(BRANCH));
- dbTester.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.BRANCH));
-
- TestResponse response = tester.newRequest()
- .setParam(PARAM_SIZE, "2")
- .execute();
-
- assertThat(GSON.fromJson(response.getInput(), ActionResponse.class))
- .isEqualTo(new ActionResponse("success", "2 branches or portfolios migrated", 3, 3, 0, 0));
- verify(measuresMigration).migrate(List.of(project.uuid(), branch.uuid()));
- verifyNoInteractions(portfoliosMigration);
- }
-
- @Test
- public void migrate_portfolios_to_reach_the_requested_size() throws SQLException {
- enableMigration();
-
- // 1 branch
- ComponentDto project = dbTester.components().insertPrivateProject();
- // 2 portfolios
- PortfolioDto portfolio1 = dbTester.components().insertPrivatePortfolioDto("name1");
- dbTester.components().insertPrivatePortfolioDto("name2");
-
- TestResponse response = tester.newRequest()
- .setParam(PARAM_SIZE, "2")
- .execute();
-
- assertThat(GSON.fromJson(response.getInput(), ActionResponse.class))
- .isEqualTo(new ActionResponse("success", "2 branches or portfolios migrated", 1, 1, 2, 2));
- verify(measuresMigration).migrate(List.of(project.uuid()));
- verify(portfoliosMigration).migrate(List.of(portfolio1.getUuid()));
- }
-
- @Test
- public void migrate_portfolios_only_if_measures_are_done() throws SQLException {
- enableMigration();
- // 2 branches, all migrated
- ComponentDto project = dbTester.components().insertPrivateProject();
- ComponentDto branch1 = dbTester.components().insertProjectBranch(project, b -> b.setBranchType(BRANCH));
- dbTester.getDbClient().branchDao().updateMeasuresMigrated(dbTester.getSession(), project.branchUuid(), true);
- dbTester.getDbClient().branchDao().updateMeasuresMigrated(dbTester.getSession(), branch1.branchUuid(), true);
- // 2 portfolios, 1 migrated
- PortfolioDto portfolio1 = dbTester.components().insertPrivatePortfolioDto("name1");
- PortfolioDto portfolio2 = dbTester.components().insertPrivatePortfolioDto("name2");
- dbTester.getDbClient().portfolioDao().updateMeasuresMigrated(dbTester.getSession(), portfolio1.getUuid(), true);
- dbTester.commit();
-
- TestResponse response = tester.newRequest()
- .setParam(PARAM_SIZE, "2")
- .execute();
-
- assertThat(GSON.fromJson(response.getInput(), ActionResponse.class))
- .isEqualTo(new ActionResponse("success", "1 branches or portfolios migrated", 0, 2, 1, 2));
- verifyNoInteractions(measuresMigration);
- verify(portfoliosMigration).migrate(List.of(portfolio2.getUuid()));
- }
-
- @Test
- public void does_nothing_if_migration_is_finished() throws SQLException {
- enableMigration();
- // 2 branches, all migrated
- ComponentDto project = dbTester.components().insertPrivateProject();
- ComponentDto branch1 = dbTester.components().insertProjectBranch(project, b -> b.setBranchType(BRANCH));
- dbTester.getDbClient().branchDao().updateMeasuresMigrated(dbTester.getSession(), project.branchUuid(), true);
- dbTester.getDbClient().branchDao().updateMeasuresMigrated(dbTester.getSession(), branch1.branchUuid(), true);
- // 2 portfolios, all migrated
- PortfolioDto portfolio1 = dbTester.components().insertPrivatePortfolioDto("name1");
- PortfolioDto portfolio2 = dbTester.components().insertPrivatePortfolioDto("name2");
- dbTester.getDbClient().portfolioDao().updateMeasuresMigrated(dbTester.getSession(), portfolio1.getUuid(), true);
- dbTester.getDbClient().portfolioDao().updateMeasuresMigrated(dbTester.getSession(), portfolio2.getUuid(), true);
- dbTester.commit();
-
- TestResponse response = tester.newRequest()
- .setParam(PARAM_SIZE, "2")
- .execute();
-
- assertThat(GSON.fromJson(response.getInput(), ActionResponse.class))
- .isEqualTo(new ActionResponse("success", "0 branches or portfolios migrated", 0, 2, 0, 2));
- verifyNoInteractions(measuresMigration, portfoliosMigration);
- }
-
- private void enableMigration() throws SQLException {
- Database database = dbTester.getDbClient().getDatabase();
- new AddMeasuresMigratedColumnToProjectBranchesTable(database).execute();
- new AddMeasuresMigratedColumnToPortfoliosTable(database).execute();
- dbTester.getDbClient().propertiesDao().saveProperty(new PropertyDto().setKey(SYSTEM_MEASURES_MIGRATION_ENABLED).setValue("true"));
- }
-
- @Test
- public void throws_ForbiddenException_if_user_is_not_logged_in() {
- userSessionRule.anonymous();
-
- TestRequest request = tester.newRequest();
-
- assertThatExceptionOfType(ForbiddenException.class)
- .isThrownBy(request::execute);
- }
-
- @Test
- public void throws_ForbiddenException_if_user_is_not_system_admin() {
- userSessionRule.logIn();
-
- TestRequest request = tester.newRequest();
-
- assertThatExceptionOfType(ForbiddenException.class)
- .isThrownBy(request::execute);
- }
-
- private record ActionResponse(String status, String message, int remainingBranches, int totalBranches, int remainingPortfolios,
- int totalPortfolios) {
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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.platform.ws;
-
-import com.tngtech.java.junit.dataprovider.DataProvider;
-import com.tngtech.java.junit.dataprovider.DataProviderRunner;
-import java.sql.SQLException;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.sonar.db.DbTester;
-import org.sonar.db.property.PropertyDto;
-import org.sonar.server.exceptions.ForbiddenException;
-import org.sonar.server.platform.db.migration.adhoc.AddMeasuresMigratedColumnToPortfoliosTable;
-import org.sonar.server.platform.db.migration.adhoc.AddMeasuresMigratedColumnToProjectBranchesTable;
-import org.sonar.server.platform.db.migration.adhoc.CreateIndexOnPortfoliosMeasuresMigrated;
-import org.sonar.server.platform.db.migration.adhoc.CreateIndexOnProjectBranchesMeasuresMigrated;
-import org.sonar.server.platform.db.migration.adhoc.CreateMeasuresTable;
-import org.sonar.server.tester.UserSessionRule;
-import org.sonar.server.ws.TestRequest;
-import org.sonar.server.ws.TestResponse;
-import org.sonar.server.ws.WsActionTester;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
-import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoInteractions;
-import static org.sonar.core.config.CorePropertyDefinitions.SYSTEM_MEASURES_MIGRATION_ENABLED;
-import static org.sonar.test.JsonAssert.assertJson;
-
-@RunWith(DataProviderRunner.class)
-public class PrepareMigrationActionTest {
-
- public static final String PARAM_ENABLE = "enable";
- @Rule
- public UserSessionRule userSessionRule = UserSessionRule.standalone().logIn().setSystemAdministrator();
-
- @Rule
- public DbTester dbTester = DbTester.create();
-
- private final CreateMeasuresTable createMeasuresTable = mock();
- private final AddMeasuresMigratedColumnToProjectBranchesTable addMeasuresMigratedColumnToProjectBranchesTable = mock();
- private final AddMeasuresMigratedColumnToPortfoliosTable addMeasuresMigratedColumnToPortfoliosTable = mock();
- private final CreateIndexOnProjectBranchesMeasuresMigrated createIndexOnProjectBranchesMeasuresMigrated = mock();
- private final CreateIndexOnPortfoliosMeasuresMigrated createIndexOnPortfoliosMeasuresMigrated = mock();
-
- private final PrepareMigrationAction underTest = new PrepareMigrationAction(userSessionRule, dbTester.getDbClient(), createMeasuresTable,
- addMeasuresMigratedColumnToProjectBranchesTable, addMeasuresMigratedColumnToPortfoliosTable, createIndexOnProjectBranchesMeasuresMigrated, createIndexOnPortfoliosMeasuresMigrated);
- private final WsActionTester tester = new WsActionTester(underTest);
-
- @Test
- public void should_throw_if_enable_parameter_is_missing() {
- TestRequest request = tester.newRequest();
-
- assertThatIllegalArgumentException()
- .isThrownBy(request::execute)
- .withMessage("The 'enable' parameter is missing");
- }
-
- @Test
- public void verify_example() {
- TestResponse response = tester.newRequest()
- .setParam(PARAM_ENABLE, "true")
- .execute();
-
- assertJson(response.getInput()).isSimilarTo(getClass().getResource("example-prepare_migration.json"));
- }
-
- @Test
- public void throws_ForbiddenException_if_user_is_not_logged_in() {
- userSessionRule.anonymous();
-
- TestRequest request = tester.newRequest();
-
- assertThatExceptionOfType(ForbiddenException.class)
- .isThrownBy(request::execute);
- }
-
- @Test
- public void throws_ForbiddenException_if_user_is_not_system_admin() {
- userSessionRule.logIn();
-
- TestRequest request = tester.newRequest();
-
- assertThatExceptionOfType(ForbiddenException.class)
- .isThrownBy(request::execute);
- }
-
- @Test
- @DataProvider(value = {"true", "yes"})
- public void should_enable_migration(String enableParamValue) throws SQLException {
- assertThat(getPropertyValue()).isNull();
-
- TestResponse response = tester.newRequest()
- .setParam(PARAM_ENABLE, enableParamValue)
- .execute();
-
- assertThat(response.getStatus()).isEqualTo(200);
- assertThat(getPropertyValue()).isTrue();
-
- verify(createMeasuresTable).execute();
- verify(addMeasuresMigratedColumnToProjectBranchesTable).execute();
- verify(addMeasuresMigratedColumnToPortfoliosTable).execute();
- verify(createIndexOnProjectBranchesMeasuresMigrated).execute();
- verify(createIndexOnPortfoliosMeasuresMigrated).execute();
-
- // reentrant
- response = tester.newRequest()
- .setParam(PARAM_ENABLE, enableParamValue)
- .execute();
-
- assertThat(response.getStatus()).isEqualTo(200);
- assertThat(getPropertyValue()).isTrue();
- }
-
- @Test
- public void property_is_unchanged_if_the_migrations_failed() throws SQLException {
- doThrow(new SQLException("Oops")).when(createMeasuresTable).execute();
-
- TestRequest request = tester.newRequest()
- .setParam(PARAM_ENABLE, "true");
-
- assertThatExceptionOfType(RuntimeException.class)
- .isThrownBy(request::execute);
-
- assertThat(getPropertyValue()).isNull();
- }
-
- @Test
- @DataProvider(value = {"false", "no"})
- public void should_disable_migration(String disableParamValue) {
- dbTester.getDbClient().propertiesDao().saveProperty(new PropertyDto().setKey(SYSTEM_MEASURES_MIGRATION_ENABLED).setValue("true"));
-
- TestResponse response = tester.newRequest()
- .setParam(PARAM_ENABLE, disableParamValue)
- .execute();
-
- assertThat(response.getStatus()).isEqualTo(200);
- assertThat(getPropertyValue()).isFalse();
-
- verifyNoInteractions(createMeasuresTable, addMeasuresMigratedColumnToPortfoliosTable, addMeasuresMigratedColumnToProjectBranchesTable,
- createIndexOnProjectBranchesMeasuresMigrated, createIndexOnPortfoliosMeasuresMigrated);
-
- // reentrant
- response = tester.newRequest()
- .setParam(PARAM_ENABLE, disableParamValue)
- .execute();
-
- assertThat(response.getStatus()).isEqualTo(200);
- assertThat(getPropertyValue()).isFalse();
- }
-
- private Boolean getPropertyValue() {
- PropertyDto propertyDto = dbTester.getDbClient().propertiesDao().selectGlobalProperty(SYSTEM_MEASURES_MIGRATION_ENABLED);
- if (propertyDto == null) {
- return null;
- }
- return Boolean.parseBoolean(propertyDto.getValue());
- }
-
-}
public void verify_count_of_added_components() {
ListContainer container = new ListContainer();
new SystemWsModule().configure(container);
- assertThat(container.getAddedObjects()).hasSize(24);
+ assertThat(container.getAddedObjects()).hasSize(15);
}
}