From d781fc6589f3747324fca737c485d4b28ebaf653 Mon Sep 17 00:00:00 2001 From: Eric Hartmann Date: Fri, 24 Nov 2017 15:22:29 +0100 Subject: [PATCH] SONAR-10089 Add a migration to rename old quality gates "SonarQube way" is renamed as "Sonar way (outdated copy)" --- .../db/migration/version/v70/DbVersion70.java | 1 + .../v70/RenameOldSonarQubeWayQualityGate.java | 78 ++++++++ .../version/v70/DbVersion70Test.java | 2 +- .../RenameOldSonarQubeWayQualityGateTest.java | 184 ++++++++++++++++++ .../quality_gates.sql | 8 + 5 files changed, 272 insertions(+), 1 deletion(-) create mode 100644 server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/RenameOldSonarQubeWayQualityGate.java create mode 100644 server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/RenameOldSonarQubeWayQualityGateTest.java create mode 100644 server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/RenameOldSonarQubeWayQualityGateTest/quality_gates.sql diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70.java index bde8d70a4b2..7d8198577c5 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70.java @@ -31,6 +31,7 @@ public class DbVersion70 implements DbVersion { .add(1901, "Populate QUALITY_GATES.IS_BUILT_IN", PopulateQualityGatesIsBuiltIn.class) .add(1902, "Make QUALITY_GATES.IS_BUILT_IN not null", MakeQualityGatesIsBuiltInNotNullable.class) .add(1903, "Remove quality gates loaded templates", RemoveQualityGateLoadedTemplates.class) + .add(1904, "Rename quality gate \"SonarQube way\" to \"Sonar way\"", RenameOldSonarQubeWayQualityGate.class) ; } diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/RenameOldSonarQubeWayQualityGate.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/RenameOldSonarQubeWayQualityGate.java new file mode 100644 index 00000000000..0a0a20b63aa --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/RenameOldSonarQubeWayQualityGate.java @@ -0,0 +1,78 @@ +/* + * 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.platform.db.migration.version.v70; + +import java.sql.SQLException; +import java.util.Date; +import org.sonar.api.utils.System2; +import org.sonar.db.Database; +import org.sonar.server.platform.db.migration.step.DataChange; +import org.sonar.server.platform.db.migration.step.MassUpdate; + +import static java.lang.String.format; + +public class RenameOldSonarQubeWayQualityGate extends DataChange { + + private static final String SONARQUBE_WAY_QUALITY_GATE = "SonarQube way"; + private static final String SONARQUBE_WAY_QUALITY_GATE_OUTDATED = "Sonar way (outdated copy)"; + private final System2 system2; + + public RenameOldSonarQubeWayQualityGate(Database db, System2 system2) { + super(db); + this.system2 = system2; + } + + @Override + protected void execute(Context context) throws SQLException { + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate.select("SELECT id FROM quality_gates WHERE name = ?") + .setString(1, SONARQUBE_WAY_QUALITY_GATE); + massUpdate.rowPluralName("quality gates"); + massUpdate.update("UPDATE quality_gates SET name=?, is_built_in=?, updated_at=? WHERE id=?"); + massUpdate.execute((row, update) -> { + update.setString(1, findNewQualityGateName(context)); + update.setBoolean(2, false); + update.setDate(3, new Date(system2.now())); + update.setLong(4, row.getLong(1)); + return true; + }); + } + + private String findNewQualityGateName(Context context) throws SQLException { + if (isQualityGateNameAvailable(context, SONARQUBE_WAY_QUALITY_GATE_OUTDATED)) { + return SONARQUBE_WAY_QUALITY_GATE_OUTDATED; + } + + String newName = SONARQUBE_WAY_QUALITY_GATE_OUTDATED + " " + system2.now(); + if (isQualityGateNameAvailable(context, newName)) { + return newName; + } + + // Given up if no name available + throw new IllegalStateException(format("There are already two quality profiles with name [%s,%s]", SONARQUBE_WAY_QUALITY_GATE_OUTDATED, newName)); + } + + private static boolean isQualityGateNameAvailable(Context context, String qualityGateName) throws SQLException { + return context.prepareSelect( + "SELECT COUNT(id) FROM quality_gates WHERE name = '" + qualityGateName + "'") + .get( + row -> row.getInt(1) == 0); + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70Test.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70Test.java index 2c1870ebe96..7a3313ef31d 100644 --- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70Test.java +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70Test.java @@ -35,7 +35,7 @@ public class DbVersion70Test { @Test public void verify_migration_count() { - verifyMigrationCount(underTest, 4); + verifyMigrationCount(underTest, 5); } } diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/RenameOldSonarQubeWayQualityGateTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/RenameOldSonarQubeWayQualityGateTest.java new file mode 100644 index 00000000000..55aba3d230f --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/RenameOldSonarQubeWayQualityGateTest.java @@ -0,0 +1,184 @@ +/* + * 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.platform.db.migration.version.v70; + +import java.sql.SQLException; +import java.util.Date; +import java.util.stream.Collectors; +import org.assertj.core.groups.Tuple; +import org.hamcrest.Description; +import org.hamcrest.TypeSafeMatcher; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.utils.System2; +import org.sonar.api.utils.internal.TestSystem2; +import org.sonar.db.CoreDbTester; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; + +public class RenameOldSonarQubeWayQualityGateTest { + + private final static long PAST = 10_000_000_000L; + private final static long NOW = 50_000_000_000L; + + private final static String SONARQUBE_WAY_QUALITY_GATE = "SonarQube way"; + private final static String SONAR_WAY_OUTDATED_QUALITY_GATE = "Sonar way (outdated copy)"; + private final static String SONAR_WAY_QUALITY_GATE = "Sonar way"; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + @Rule + public CoreDbTester db = CoreDbTester.createForSchema(PopulateQualityGatesIsBuiltInTest.class, "quality_gates.sql"); + + private System2 system2 = new TestSystem2().setNow(NOW); + + private RenameOldSonarQubeWayQualityGate underTest = new RenameOldSonarQubeWayQualityGate(db.database(), system2); + + @Test + public void has_no_effect_if_table_is_empty() throws SQLException { + underTest.execute(); + + assertThat(db.countRowsOfTable("quality_gates")).isEqualTo(0); + } + + @Test + public void should_rename_SonarQubeWay_quality_gate() throws SQLException { + insertQualityGate(SONARQUBE_WAY_QUALITY_GATE, false); + + underTest.execute(); + + assertQualityGates( + tuple(SONAR_WAY_OUTDATED_QUALITY_GATE, false, new Date(PAST), new Date(NOW)) + ); + } + + @Test + public void should_set_builtin_to_false_when_renaming() throws SQLException { + insertQualityGate(SONARQUBE_WAY_QUALITY_GATE, true); + + underTest.execute(); + + assertQualityGates( + tuple(SONAR_WAY_OUTDATED_QUALITY_GATE, false, new Date(PAST), new Date(NOW)) + ); + } + + @Test + public void should_not_fail_if_a_profile_with_same_name_is_present() throws SQLException { + insertQualityGate(SONAR_WAY_OUTDATED_QUALITY_GATE, false); + insertQualityGate(SONARQUBE_WAY_QUALITY_GATE, false); + + underTest.execute(); + + assertQualityGates( + tuple(SONAR_WAY_OUTDATED_QUALITY_GATE, false, new Date(PAST), new Date(PAST)), + tuple(SONAR_WAY_OUTDATED_QUALITY_GATE + " " + NOW, false, new Date(PAST), new Date(NOW)) + ); + } + + @Test + public void should_throw_ISE_if_no_name_is_available() throws SQLException { + insertQualityGate(SONARQUBE_WAY_QUALITY_GATE, false); + insertQualityGate(SONAR_WAY_OUTDATED_QUALITY_GATE, false); + insertQualityGate(SONAR_WAY_OUTDATED_QUALITY_GATE + " " + system2.now(), false); + + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("Error during processing of row:"); + expectedException.expectCause( + new CauseMatcher( + IllegalStateException.class, + "There are already two quality profiles with name [Sonar way (outdated copy),Sonar way (outdated copy) 50000000000]")); + + underTest.execute(); + } + + @Test + public void should_update_only_SonarQubeWay() throws SQLException { + insertQualityGate("Whatever", true); + insertQualityGate("Whatever2", false); + insertQualityGate(SONAR_WAY_QUALITY_GATE, true); + insertQualityGate(SONARQUBE_WAY_QUALITY_GATE, false); + + underTest.execute(); + + assertQualityGates( + tuple("Whatever", true, new Date(PAST), new Date(PAST)), + tuple("Whatever2", false, new Date(PAST), new Date(PAST)), + tuple(SONAR_WAY_QUALITY_GATE, true, new Date(PAST), new Date(PAST)), + tuple(SONAR_WAY_OUTDATED_QUALITY_GATE, false, new Date(PAST), new Date(NOW)) + ); + } + + @Test + public void is_reentrant() throws SQLException { + insertQualityGate(SONARQUBE_WAY_QUALITY_GATE, false); + + underTest.execute(); + underTest.execute(); + + assertQualityGates( + tuple(SONAR_WAY_OUTDATED_QUALITY_GATE, false, new Date(PAST), new Date(NOW)) + ); + } + + private void assertQualityGates(Tuple... expectedTuples) { + assertThat(db.select("SELECT NAME, IS_BUILT_IN, CREATED_AT, UPDATED_AT FROM QUALITY_GATES") + .stream() + .map(map -> new Tuple(map.get("NAME"), map.get("IS_BUILT_IN"), map.get("CREATED_AT"), map.get("UPDATED_AT"))) + .collect(Collectors.toList())) + .containsExactlyInAnyOrder(expectedTuples); + } + + + private void insertQualityGate(String name, boolean builtIn) { + db.executeInsert( + "QUALITY_GATES", + "NAME", name, + "IS_BUILT_IN", String.valueOf(builtIn), + "CREATED_AT", new Date(PAST), + "UPDATED_AT", new Date(PAST)); + } + + private static class CauseMatcher extends TypeSafeMatcher { + private final Class type; + private final String expectedMessage; + + public CauseMatcher(Class type, String expectedMessage) { + this.type = type; + this.expectedMessage = expectedMessage; + } + + @Override + protected boolean matchesSafely(Throwable item) { + return item.getClass().isAssignableFrom(type) + && item.getMessage().contains(expectedMessage); + } + + @Override + public void describeTo(Description description) { + description.appendText("expects type ") + .appendValue(type) + .appendText(" and a message ") + .appendValue(expectedMessage); + } + } +} diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/RenameOldSonarQubeWayQualityGateTest/quality_gates.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/RenameOldSonarQubeWayQualityGateTest/quality_gates.sql new file mode 100644 index 00000000000..52b9423f526 --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/RenameOldSonarQubeWayQualityGateTest/quality_gates.sql @@ -0,0 +1,8 @@ +CREATE TABLE "QUALITY_GATES" ( + "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "NAME" VARCHAR(100) NOT NULL, + "IS_BUILT_IN" BOOLEAN NULL, + "CREATED_AT" TIMESTAMP, + "UPDATED_AT" TIMESTAMP, +); +CREATE UNIQUE INDEX "UNIQ_QUALITY_GATES" ON "QUALITY_GATES" ("NAME"); -- 2.39.5