diff options
author | Jacek <52388493+jacek-poreda-sonarsource@users.noreply.github.com> | 2019-07-31 15:03:39 +0200 |
---|---|---|
committer | SonarTech <sonartech@sonarsource.com> | 2019-07-31 20:21:14 +0200 |
commit | eb9b15871391f10850b2c3ecdad13ef3ef19aaba (patch) | |
tree | 6dd32bfe46594f5a80a3332d9b672a7af27f3269 /server/sonar-db-migration | |
parent | 1771582bc9977760b66c8e6275f12e5357341746 (diff) | |
download | sonarqube-eb9b15871391f10850b2c3ecdad13ef3ef19aaba.tar.gz sonarqube-eb9b15871391f10850b2c3ecdad13ef3ef19aaba.zip |
SONAR-8115 storing project quality gate in table (#1919)
* migrate properties to project_qgate table
* use project_qgates table instead of property
* change usage of project quality gate in CE
Diffstat (limited to 'server/sonar-db-migration')
5 files changed, 291 insertions, 2 deletions
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v80/DbVersion80.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v80/DbVersion80.java index a11fd4e4156..f3f4721ef6d 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v80/DbVersion80.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v80/DbVersion80.java @@ -28,6 +28,7 @@ public class DbVersion80 implements DbVersion { registry .add(3000, "Set Organizations#guarded column nullable", MakeOrganizationsGuardedNullable.class) .add(3001, "Create ProjectQualityGates table", CreateProjectQualityGatesTable.class) - .add(3002, "Make index on DEPRECATED_RULE_KEYS.RULE_ID non unique", MakeDeprecatedRuleKeysRuleIdIndexNonUnique.class); + .add(3002, "Make index on DEPRECATED_RULE_KEYS.RULE_ID non unique", MakeDeprecatedRuleKeysRuleIdIndexNonUnique.class) + .add(3003, "Populate ProjectQualityGate table from Properties table", PopulateProjectQualityGatesTable.class); } } diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v80/PopulateProjectQualityGatesTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v80/PopulateProjectQualityGatesTable.java new file mode 100644 index 00000000000..ba0ed09cb67 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v80/PopulateProjectQualityGatesTable.java @@ -0,0 +1,80 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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.v80; + +import java.sql.SQLException; +import java.util.List; +import org.sonar.db.Database; +import org.sonar.server.platform.db.migration.step.DataChange; +import org.sonar.server.platform.db.migration.step.Upsert; + +public class PopulateProjectQualityGatesTable extends DataChange { + + public PopulateProjectQualityGatesTable(Database db) { + super(db); + } + + @Override + protected void execute(Context context) throws SQLException { + List<ProjectQualityGate> projectQualityGates = context.prepareSelect( + "select prj.uuid, qg.uuid from properties p \n" + + "join projects prj on p.resource_id = prj.id\n" + + "join quality_gates qg on qg.id = CAST(p.text_value AS int)\n" + + "where p.prop_key = 'sonar.qualitygate'\n" + + "and not exists(select pqg.project_uuid from project_qgates pqg where pqg.project_uuid = prj.uuid)") + .list(row -> { + String projectUuid = row.getString(1); + String qualityGateUuid = row.getString(2); + return new ProjectQualityGate(projectUuid, qualityGateUuid); + }); + + if (!projectQualityGates.isEmpty()) { + populateProjectQualityGates(context, projectQualityGates); + } + } + + private static void populateProjectQualityGates(Context context, List<ProjectQualityGate> projectQualityGates) throws SQLException { + Upsert insertQuery = prepareInsertProjectQualityGateQuery(context); + for (ProjectQualityGate projectQualityGate : projectQualityGates) { + insertQuery + .setString(1, projectQualityGate.projectUuid) + .setString(2, projectQualityGate.qualityGateUuid) + .addBatch(); + } + insertQuery + .execute() + .commit(); + } + + private static Upsert prepareInsertProjectQualityGateQuery(Context context) throws SQLException { + return context.prepareUpsert("insert into project_qgates(project_uuid, quality_gate_uuid) VALUES (?, ?)"); + } + + private static class ProjectQualityGate { + private final String projectUuid; + private final String qualityGateUuid; + + ProjectQualityGate(String projectUuid, String qualityGateUuid) { + this.projectUuid = projectUuid; + this.qualityGateUuid = qualityGateUuid; + } + } + +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v80/DbVersion80Test.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v80/DbVersion80Test.java index cefa4368acf..f69a48b5e7c 100644 --- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v80/DbVersion80Test.java +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v80/DbVersion80Test.java @@ -35,7 +35,7 @@ public class DbVersion80Test { @Test public void verify_migration_count() { - verifyMigrationCount(underTest, 3); + verifyMigrationCount(underTest, 4); } } diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v80/PopulateProjectQualityGatesTableTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v80/PopulateProjectQualityGatesTableTest.java new file mode 100644 index 00000000000..4b7af24e2cc --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v80/PopulateProjectQualityGatesTableTest.java @@ -0,0 +1,131 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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.v80; + +import java.sql.SQLException; +import java.time.Instant; +import java.util.List; +import java.util.Random; +import java.util.stream.Collectors; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.db.CoreDbTester; + +import static java.lang.String.valueOf; + + +public class PopulateProjectQualityGatesTableTest { + private static final String PROJECTS_TABLE_NAME = "projects"; + private static final String QUALITY_GATES_TABLE_NAME = "quality_gates"; + private static final String PROPERTIES_TABLE_NAME = "properties"; + private static final int NUMBER_OF_PROJECTS_TO_INSERT = 5; + + private final Random random = new Random(); + + @Rule + public CoreDbTester dbTester = CoreDbTester.createForSchema(PopulateProjectQualityGatesTableTest.class, "schema.sql"); + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private PopulateProjectQualityGatesTable underTest = new PopulateProjectQualityGatesTable(dbTester.database()); + + @Test + public void copy_quality_gates_properties_to_project_qgate_table() throws SQLException { + long firstQualityGateId = insertQualityGate("qg1"); + long secondQualityGateId = insertQualityGate("qg2"); + + for (long i = 1; i <= NUMBER_OF_PROJECTS_TO_INSERT; i++) { + long projectId = insertComponent("p" + i); + long qualityGateId = random.nextBoolean() ? firstQualityGateId : secondQualityGateId; + insertQualityGateProperty(projectId, qualityGateId); + } + + underTest.execute(); + + List<ProjectQualityGate> qualityGates = getQualityGates(); + Assert.assertEquals(NUMBER_OF_PROJECTS_TO_INSERT, qualityGates.size()); + + //must not delete properties + int propertiesCount = dbTester.countRowsOfTable(PROPERTIES_TABLE_NAME); + Assert.assertEquals(5, propertiesCount); + + //should not fail if executed twice + underTest.execute(); + } + + private long insertQualityGate(String qualityGateUuid) { + dbTester.executeInsert( + QUALITY_GATES_TABLE_NAME, + "UUID", qualityGateUuid, + "NAME", "name_" + qualityGateUuid, + "IS_BUILT_IN", valueOf(true) + ); + return (long) dbTester.selectFirst("select id as \"ID\" from quality_gates where uuid='" + qualityGateUuid + "'").get("ID"); + } + + private long insertComponent(String uuid) { + dbTester.executeInsert( + PROJECTS_TABLE_NAME, + "ORGANIZATION_UUID", "org_" + uuid, + "SCOPE", "PRJ", + "QUALIFIER", "TRK", + "UUID", uuid, + "UUID_PATH", "path_" + uuid, + "ROOT_UUID", "root_" + uuid, + "PROJECT_UUID", uuid, + "PRIVATE", valueOf(false)); + return (long) dbTester.selectFirst("select id as \"ID\" from projects where uuid='" + uuid + "'").get("ID"); + } + + private void insertQualityGateProperty(Long projectId, Long qualityGateId) { + dbTester.executeInsert(PROPERTIES_TABLE_NAME, + "prop_key", "sonar.qualitygate", + "resource_id", projectId, + "is_empty", false, + "text_value", Long.toString(qualityGateId), + "created_at", Instant.now().toEpochMilli()); + } + + private List<ProjectQualityGate> getQualityGates() { + return dbTester.select("select pqg.project_uuid, pqg.quality_gate_uuid from project_qgates pqg " + + "join projects p on pqg.project_uuid = p.uuid " + + "join quality_gates qg on pqg.quality_gate_uuid = qg.uuid") + .stream() + .map(row -> { + String projectUuid = String.valueOf(row.get("PROJECT_UUID")); + String qualityGateUuid = String.valueOf(row.get("QUALITY_GATE_UUID")); + return new ProjectQualityGate(projectUuid, qualityGateUuid); + }) + .collect(Collectors.toList()); + } + + private static class ProjectQualityGate { + final String projectUuid; + final String qualityGateUuid; + + private ProjectQualityGate(String projectUuid, String qualityGateUuid) { + this.projectUuid = projectUuid; + this.qualityGateUuid = qualityGateUuid; + } + } +} diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v80/PopulateProjectQualityGatesTableTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v80/PopulateProjectQualityGatesTableTest/schema.sql new file mode 100644 index 00000000000..d4dceda5613 --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v80/PopulateProjectQualityGatesTableTest/schema.sql @@ -0,0 +1,77 @@ +CREATE TABLE "PROJECTS" ( + "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "ORGANIZATION_UUID" VARCHAR(40) NOT NULL, + "KEE" VARCHAR(400), + "UUID" VARCHAR(50) NOT NULL, + "UUID_PATH" VARCHAR(1500) NOT NULL, + "ROOT_UUID" VARCHAR(50) NOT NULL, + "PROJECT_UUID" VARCHAR(50) NOT NULL, + "MODULE_UUID" VARCHAR(50), + "MODULE_UUID_PATH" VARCHAR(1500), + "MAIN_BRANCH_PROJECT_UUID" VARCHAR(50), + "NAME" VARCHAR(2000), + "DESCRIPTION" VARCHAR(2000), + "PRIVATE" BOOLEAN NOT NULL, + "TAGS" VARCHAR(500), + "ENABLED" BOOLEAN NOT NULL DEFAULT TRUE, + "SCOPE" VARCHAR(3), + "QUALIFIER" VARCHAR(10), + "DEPRECATED_KEE" VARCHAR(400), + "PATH" VARCHAR(2000), + "LANGUAGE" VARCHAR(20), + "COPY_COMPONENT_UUID" VARCHAR(50), + "LONG_NAME" VARCHAR(2000), + "DEVELOPER_UUID" VARCHAR(50), + "CREATED_AT" TIMESTAMP, + "AUTHORIZATION_UPDATED_AT" BIGINT, + "B_CHANGED" BOOLEAN, + "B_COPY_COMPONENT_UUID" VARCHAR(50), + "B_DESCRIPTION" VARCHAR(2000), + "B_ENABLED" BOOLEAN, + "B_UUID_PATH" VARCHAR(1500), + "B_LANGUAGE" VARCHAR(20), + "B_LONG_NAME" VARCHAR(500), + "B_MODULE_UUID" VARCHAR(50), + "B_MODULE_UUID_PATH" VARCHAR(1500), + "B_NAME" VARCHAR(500), + "B_PATH" VARCHAR(2000), + "B_QUALIFIER" VARCHAR(10) +); +CREATE INDEX "PROJECTS_ORGANIZATION" ON "PROJECTS" ("ORGANIZATION_UUID"); +CREATE UNIQUE INDEX "PROJECTS_KEE" ON "PROJECTS" ("KEE"); +CREATE INDEX "PROJECTS_ROOT_UUID" ON "PROJECTS" ("ROOT_UUID"); +CREATE UNIQUE INDEX "PROJECTS_UUID" ON "PROJECTS" ("UUID"); +CREATE INDEX "PROJECTS_PROJECT_UUID" ON "PROJECTS" ("PROJECT_UUID"); +CREATE INDEX "PROJECTS_MODULE_UUID" ON "PROJECTS" ("MODULE_UUID"); +CREATE INDEX "PROJECTS_QUALIFIER" ON "PROJECTS" ("QUALIFIER"); + +CREATE TABLE "QUALITY_GATES" ( + "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "UUID" VARCHAR(40) NOT NULL, + "NAME" VARCHAR(100) NOT NULL, + "IS_BUILT_IN" BOOLEAN NOT NULL, + "CREATED_AT" TIMESTAMP, + "UPDATED_AT" TIMESTAMP, +); +CREATE UNIQUE INDEX "UNIQ_QUALITY_GATES_UUID" ON "QUALITY_GATES" ("UUID"); + + +CREATE TABLE "PROPERTIES" ( + "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "PROP_KEY" VARCHAR(512) NOT NULL, + "RESOURCE_ID" INTEGER, + "USER_ID" INTEGER, + "IS_EMPTY" BOOLEAN NOT NULL, + "TEXT_VALUE" VARCHAR(4000), + "CLOB_VALUE" CLOB, + "CREATED_AT" BIGINT +); +CREATE INDEX "PROPERTIES_KEY" ON "PROPERTIES" ("PROP_KEY"); + +CREATE TABLE "PROJECT_QGATES" ( +"PROJECT_UUID" VARCHAR(40) NOT NULL, +"QUALITY_GATE_UUID" VARCHAR(40) NOT NULL, + +CONSTRAINT "PK_PROJECT_QGATES" PRIMARY KEY ("PROJECT_UUID") +); +CREATE UNIQUE INDEX "UNIQ_PROJECT_QGATES" ON "PROJECT_QGATES" ("PROJECT_UUID", "QUALITY_GATE_UUID"); |