From 0856ba6fb98c2f32b1a10f47c40a9c5eabbc4a90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Lesaint?= Date: Fri, 10 Mar 2017 16:40:25 +0100 Subject: [PATCH] SONAR-8931 extend size of LOADED_TEMPLATES.TEMPLATE_TYPE --- .../org/sonar/db/version/rows-h2.sql | 4 + .../org/sonar/db/version/schema-h2.ddl | 3 +- .../v64/AddIndexLoadedTemplatesType.java | 46 ++++++ .../v64/CleanLoadedTemplateOrphans.java | 37 +++++ .../db/migration/version/v64/DbVersion64.java | 7 +- .../v64/ExtendLoadedTemplateTypeColumn.java | 44 +++++ ...UpgradeQualityTemplateLoadedTemplates.java | 69 ++++++++ .../v64/AddIndexLoadedTemplatesTypeTest.java | 54 ++++++ .../v64/CleanLoadedTemplateOrphansTest.java | 66 ++++++++ .../version/v64/DbVersion64Test.java | 2 +- .../ExtendLoadedTemplateTypeColumnTest.java | 78 +++++++++ ...adeQualityTemplateLoadedTemplatesTest.java | 156 ++++++++++++++++++ .../loaded_templates_without_index.sql | 5 + .../loaded-templates.sql | 5 + .../loaded-templates.sql | 5 + ...ternal_properties_and_loaded_templates.sql | 32 ++++ .../RegisterQualityProfiles.java | 40 +++-- .../RegisterQualityProfilesMediumTest.java | 27 ++- .../RegisterQualityProfilesTest.java | 33 ++-- 19 files changed, 679 insertions(+), 34 deletions(-) create mode 100644 server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/AddIndexLoadedTemplatesType.java create mode 100644 server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/CleanLoadedTemplateOrphans.java create mode 100644 server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/ExtendLoadedTemplateTypeColumn.java create mode 100644 server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/UpgradeQualityTemplateLoadedTemplates.java create mode 100644 server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/AddIndexLoadedTemplatesTypeTest.java create mode 100644 server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/CleanLoadedTemplateOrphansTest.java create mode 100644 server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/ExtendLoadedTemplateTypeColumnTest.java create mode 100644 server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/UpgradeQualityTemplateLoadedTemplatesTest.java create mode 100644 server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v64/AddIndexLoadedTemplatesTypeTest/loaded_templates_without_index.sql create mode 100644 server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v64/CleanLoadedTemplateOrphansTest/loaded-templates.sql create mode 100644 server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v64/ExtendLoadedTemplateTypeColumnTest/loaded-templates.sql create mode 100644 server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v64/UpgradeQualityTemplateLoadedTemplatesTest/organizations_internal_properties_and_loaded_templates.sql diff --git a/server/sonar-db-core/src/main/resources/org/sonar/db/version/rows-h2.sql b/server/sonar-db-core/src/main/resources/org/sonar/db/version/rows-h2.sql index 51b6f812af3..300a33d2194 100644 --- a/server/sonar-db-core/src/main/resources/org/sonar/db/version/rows-h2.sql +++ b/server/sonar-db-core/src/main/resources/org/sonar/db/version/rows-h2.sql @@ -543,6 +543,10 @@ INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1607'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1608'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1609'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1610'); +INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1611'); +INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1612'); +INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1613'); +INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1614'); INSERT INTO USERS(ID, LOGIN, NAME, EMAIL, EXTERNAL_IDENTITY, EXTERNAL_IDENTITY_PROVIDER, USER_LOCAL, CRYPTED_PASSWORD, SALT, IS_ROOT, CREATED_AT, UPDATED_AT) VALUES (1, 'admin', 'Administrator', '', 'admin', 'sonarqube', true, 'a373a0e667abb2604c1fd571eb4ad47fe8cc0878', '48bc4b0d93179b5103fd3885ea9119498e9d161b', false, '1418215735482', '1418215735482'); ALTER TABLE USERS ALTER COLUMN ID RESTART WITH 2; diff --git a/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl b/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl index 028846ad102..96468bb9352 100644 --- a/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl +++ b/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl @@ -406,8 +406,9 @@ CREATE UNIQUE INDEX "METRICS_UNIQUE_NAME" ON "METRICS" ("NAME"); CREATE TABLE "LOADED_TEMPLATES" ( "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), "KEE" VARCHAR(200), - "TEMPLATE_TYPE" VARCHAR(15) + "TEMPLATE_TYPE" VARCHAR(64) NOT NULL ); +CREATE INDEX "IX_LOADED_TEMPLATES_TYPE" ON "LOADED_TEMPLATES" ("TEMPLATE_TYPE"); CREATE TABLE "AUTHORS" ( diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/AddIndexLoadedTemplatesType.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/AddIndexLoadedTemplatesType.java new file mode 100644 index 00000000000..64e7ea7112f --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/AddIndexLoadedTemplatesType.java @@ -0,0 +1,46 @@ +/* + * 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.v64; + +import java.sql.SQLException; +import org.sonar.db.Database; +import org.sonar.server.platform.db.migration.def.VarcharColumnDef; +import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder; +import org.sonar.server.platform.db.migration.step.DdlChange; + +public class AddIndexLoadedTemplatesType extends DdlChange { + public AddIndexLoadedTemplatesType(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + context.execute( + new CreateIndexBuilder(getDialect()) + .setTable("loaded_templates") + .setName("ix_loaded_templates_type") + .addColumn(VarcharColumnDef.newVarcharColumnDefBuilder() + .setColumnName("template_type") + .setIsNullable(false) + .setLimit(64) + .build()) + .build()); + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/CleanLoadedTemplateOrphans.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/CleanLoadedTemplateOrphans.java new file mode 100644 index 00000000000..f36f088c27d --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/CleanLoadedTemplateOrphans.java @@ -0,0 +1,37 @@ +/* + * 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.v64; + +import java.sql.SQLException; +import org.sonar.db.Database; +import org.sonar.server.platform.db.migration.step.DataChange; + +public class CleanLoadedTemplateOrphans extends DataChange { + public CleanLoadedTemplateOrphans(Database db) { + super(db); + } + + @Override + protected void execute(Context context) throws SQLException { + context.prepareUpsert("delete from loaded_templates where template_type is null") + .execute() + .commit(); + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/DbVersion64.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/DbVersion64.java index 1b2722f7cf3..6e395c4d7a4 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/DbVersion64.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/DbVersion64.java @@ -41,6 +41,11 @@ public void addSteps(MigrationStepRegistry registry) { .add(1608, "Populate ORGANIZATION_MEMBERS table", PopulateOrganizationMembersTable.class) .add(1609, "Drop unique index on RULES_PROFILES.ORGANIZATION_UUID and KEE", DropUniqueIndexOnQualityProfileOrganizationUuidAndKey.class) - .add(1610, "Make RULES_PROFILES.KEE unique", MakeQualityProfileKeyUnique.class); + .add(1610, "Make RULES_PROFILES.KEE unique", MakeQualityProfileKeyUnique.class) + + .add(1611, "Clean LOADED_TEMPLATES rows without type", CleanLoadedTemplateOrphans.class) + .add(1612, "Extend size of column LOADED_TEMPLATES.TEMPLATE_TYPE", ExtendLoadedTemplateTypeColumn.class) + .add(1613, "Add index LOADED_TEMPLATES_TYPE", AddIndexLoadedTemplatesType.class) + .add(1614, "Upgrade loaded template entries for quality profiles", UpgradeQualityTemplateLoadedTemplates.class); } } diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/ExtendLoadedTemplateTypeColumn.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/ExtendLoadedTemplateTypeColumn.java new file mode 100644 index 00000000000..c1c490e75e4 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/ExtendLoadedTemplateTypeColumn.java @@ -0,0 +1,44 @@ +/* + * 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.v64; + +import java.sql.SQLException; +import org.sonar.db.Database; +import org.sonar.server.platform.db.migration.def.VarcharColumnDef; +import org.sonar.server.platform.db.migration.sql.AlterColumnsBuilder; +import org.sonar.server.platform.db.migration.step.DdlChange; + +public class ExtendLoadedTemplateTypeColumn extends DdlChange { + public ExtendLoadedTemplateTypeColumn(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + context.execute( + new AlterColumnsBuilder(getDialect(), "loaded_templates") + .updateColumn(VarcharColumnDef.newVarcharColumnDefBuilder() + .setColumnName("template_type") + .setIsNullable(false) + .setLimit(64) + .build()) + .build()); + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/UpgradeQualityTemplateLoadedTemplates.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/UpgradeQualityTemplateLoadedTemplates.java new file mode 100644 index 00000000000..712b97ba935 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/UpgradeQualityTemplateLoadedTemplates.java @@ -0,0 +1,69 @@ +/* + * 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.v64; + +import java.security.MessageDigest; +import java.sql.SQLException; +import org.apache.commons.codec.digest.DigestUtils; +import org.sonar.db.Database; +import org.sonar.server.platform.db.migration.step.DataChange; +import org.sonar.server.platform.db.migration.step.MassUpdate; +import org.sonar.server.platform.db.migration.version.v63.DefaultOrganizationUuid; + +import static java.lang.String.format; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.apache.commons.codec.binary.Hex.encodeHexString; + +public class UpgradeQualityTemplateLoadedTemplates extends DataChange { + private static final String QUALITY_PROFILE_TYPE = "QUALITY_PROFILE"; + + private final DefaultOrganizationUuid defaultOrganizationUuid; + + public UpgradeQualityTemplateLoadedTemplates(Database db, DefaultOrganizationUuid defaultOrganizationUuid) { + super(db); + this.defaultOrganizationUuid = defaultOrganizationUuid; + } + + @Override + protected void execute(Context context) throws SQLException { + String defaultOrganizationUuid = this.defaultOrganizationUuid.getAndCheck(context); + + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate + .select("select id,kee from loaded_templates where template_type=?") + .setString(1, QUALITY_PROFILE_TYPE); + massUpdate.rowPluralName("loaded templates for quality profiles"); + massUpdate.update("update loaded_templates set template_type=?,kee=? where id=?"); + MessageDigest md5Digest = DigestUtils.getMd5Digest(); + massUpdate.execute((row, update) -> { + int id = row.getInt(1); + String key = row.getString(2); + + update.setString(1, computeLoadedTemplateType(key, md5Digest)); + update.setString(2, defaultOrganizationUuid); + update.setInt(3, id); + return true; + }); + } + + private static String computeLoadedTemplateType(String currentKey, MessageDigest messageDigest) { + return format("%s.%s", QUALITY_PROFILE_TYPE, encodeHexString(messageDigest.digest(currentKey.getBytes(UTF_8)))); + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/AddIndexLoadedTemplatesTypeTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/AddIndexLoadedTemplatesTypeTest.java new file mode 100644 index 00000000000..45adeb94161 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/AddIndexLoadedTemplatesTypeTest.java @@ -0,0 +1,54 @@ +/* + * 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.v64; + +import java.sql.SQLException; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.db.CoreDbTester; + +public class AddIndexLoadedTemplatesTypeTest { + private static final String TABLE_LOADED_TEMPLATES = "loaded_templates"; + + @Rule + public CoreDbTester db = CoreDbTester.createForSchema(AddIndexLoadedTemplatesTypeTest.class, "loaded_templates_without_index.sql"); + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private AddIndexLoadedTemplatesType underTest = new AddIndexLoadedTemplatesType(db.database()); + + @Test + public void execute_adds_index_ix_loaded_templates_type() throws SQLException { + underTest.execute(); + + db.assertIndex(TABLE_LOADED_TEMPLATES, "ix_loaded_templates_type", "template_type"); + } + + @Test + public void execute_is_not_reentrant() throws SQLException { + underTest.execute(); + + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("Fail to execute"); + + underTest.execute(); + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/CleanLoadedTemplateOrphansTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/CleanLoadedTemplateOrphansTest.java new file mode 100644 index 00000000000..200aad66ca0 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/CleanLoadedTemplateOrphansTest.java @@ -0,0 +1,66 @@ +/* + * 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.v64; + +import java.sql.SQLException; +import javax.annotation.Nullable; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.db.CoreDbTester; + +import static org.assertj.core.api.Assertions.assertThat; + +public class CleanLoadedTemplateOrphansTest { + + private static final String TABLE_LOADED_TEMPLATES = "loaded_templates"; + + @Rule + public CoreDbTester db = CoreDbTester.createForSchema(CleanLoadedTemplateOrphansTest.class, "loaded-templates.sql"); + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private CleanLoadedTemplateOrphans underTest = new CleanLoadedTemplateOrphans(db.database()); + + @Test + public void execute_has_no_effect_on_empty_table() throws SQLException { + underTest.execute(); + } + + @Test + public void execute_deletes_rows_with_null_in_template_type_column() throws SQLException { + insertLoadedTemplate(null, "value1"); + insertLoadedTemplate("", "value2"); + insertLoadedTemplate("non_empty", "value3"); + + underTest.execute(); + + assertThat(db.select("select kee as \"value\" from loaded_templates")) + .extracting(s -> s.get("value")) + .containsOnly("value2", "value3"); + } + + private void insertLoadedTemplate(@Nullable String type, String key) { + db.executeInsert( + TABLE_LOADED_TEMPLATES, + "TEMPLATE_TYPE", type, + "KEE", key); + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/DbVersion64Test.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/DbVersion64Test.java index 290a9bb221b..8912d8fa023 100644 --- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/DbVersion64Test.java +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/DbVersion64Test.java @@ -35,7 +35,7 @@ public void migrationNumber_starts_at_1600() { @Test public void verify_migration_count() { - verifyMigrationCount(underTest, 11); + verifyMigrationCount(underTest, 15); } } diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/ExtendLoadedTemplateTypeColumnTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/ExtendLoadedTemplateTypeColumnTest.java new file mode 100644 index 00000000000..05c0270e26a --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/ExtendLoadedTemplateTypeColumnTest.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.v64; + +import java.sql.SQLException; +import java.sql.Types; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.db.CoreDbTester; + +public class ExtendLoadedTemplateTypeColumnTest { + + private static final String TABLE_LOADED_TEMPLATES = "loaded_templates"; + + @Rule + public CoreDbTester db = CoreDbTester.createForSchema(ExtendLoadedTemplateTypeColumnTest.class, "loaded-templates.sql"); + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private ExtendLoadedTemplateTypeColumn underTest = new ExtendLoadedTemplateTypeColumn(db.database()); + + @Test + public void extend_and_make_non_nullable_column_template_type() throws SQLException { + underTest.execute(); + + verifyColumnDefinitions(); + } + + @Test + public void migration_makes_analysis_uuid_not_nullable_on_populated_table() throws SQLException { + insertLoadedTemplate("type1", "val1"); + insertLoadedTemplate("type2", "val2"); + + underTest.execute(); + + verifyColumnDefinitions(); + } + + @Test + public void migration_fails_if_type_column_has_null_values() throws SQLException { + insertLoadedTemplate(null, "some value"); + + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("Fail to execute"); + + underTest.execute(); + } + + private void insertLoadedTemplate(String type, String key) { + db.executeInsert( + TABLE_LOADED_TEMPLATES, + "TEMPLATE_TYPE", type, + "KEE", key); + } + + private void verifyColumnDefinitions() { + db.assertColumnDefinition(TABLE_LOADED_TEMPLATES, "template_type", Types.VARCHAR, 64, false); + } + +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/UpgradeQualityTemplateLoadedTemplatesTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/UpgradeQualityTemplateLoadedTemplatesTest.java new file mode 100644 index 00000000000..345747f57e2 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/UpgradeQualityTemplateLoadedTemplatesTest.java @@ -0,0 +1,156 @@ +/* + * 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.v64; + +import java.sql.SQLException; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.db.CoreDbTester; +import org.sonar.server.platform.db.migration.version.v63.DefaultOrganizationUuidImpl; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.apache.commons.codec.digest.DigestUtils.md5Hex; +import static org.assertj.core.api.Assertions.assertThat; + +public class UpgradeQualityTemplateLoadedTemplatesTest { + + private static final String TABLE_ORGANIZATIONS = "organizations"; + private static final String DEFAULT_ORGANIZATION_UUID = "def-org"; + private static final String TABLE_LOADED_TEMPLATES = "loaded_templates"; + private static final String QUALITY_PROFILE_TYPE = "QUALITY_PROFILE"; + + @Rule + public CoreDbTester db = CoreDbTester.createForSchema(UpgradeQualityTemplateLoadedTemplatesTest.class, "organizations_internal_properties_and_loaded_templates.sql"); + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + private UpgradeQualityTemplateLoadedTemplates underTest = new UpgradeQualityTemplateLoadedTemplates(db.database(), new DefaultOrganizationUuidImpl()); + + @Test + public void fails_with_ISE_when_no_default_organization_is_set() throws SQLException { + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("Default organization uuid is missing"); + + underTest.execute(); + } + + @Test + public void fails_with_ISE_when_default_organization_does_not_exist_in_table_ORGANIZATIONS() throws SQLException { + insertDefaultOrganizationUuid("blabla"); + + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("Default organization with uuid 'blabla' does not exist in table ORGANIZATIONS"); + + underTest.execute(); + } + + @Test + public void execute_has_no_effect_if_loaded_templates_table_is_empty() throws Exception { + setupDefaultOrganization(); + + underTest.execute(); + + assertThat(db.countRowsOfTable(TABLE_LOADED_TEMPLATES)).isEqualTo(0); + } + + @Test + public void execute_has_no_effect_if_loaded_templates_has_no_row_with_type_QUALITY_PROFILE() throws Exception { + setupDefaultOrganization(); + insertLoadedTemplate("foo", "bar"); + + underTest.execute(); + + assertThat(loadedTemplateExists("foo", "bar")).isTrue(); + assertThat(db.countRowsOfTable(TABLE_LOADED_TEMPLATES)).isEqualTo(1); + } + + @Test + public void execute_updates_any_row_with_type_QUALITY_PROFILE_to_type_based_on_current_key_md5_and_default_organization_uuid_as_key() throws SQLException { + setupDefaultOrganization(); + + // put accents to ensure UTF-8 byte array of String is used + String key1 = "fé@è:bar"; + String key2 = "bar"; + insertLoadedTemplate(QUALITY_PROFILE_TYPE, key1); + // matching on type is case sensitive + insertLoadedTemplate(QUALITY_PROFILE_TYPE.toLowerCase(), key1); + insertLoadedTemplate(QUALITY_PROFILE_TYPE, key2); + insertLoadedTemplate("other type", key2); + + underTest.execute(); + + assertThat(loadedTemplateExists(QUALITY_PROFILE_TYPE + '.' + md5Hex(key1.getBytes(UTF_8)), DEFAULT_ORGANIZATION_UUID)).isTrue(); + assertThat(loadedTemplateExists(QUALITY_PROFILE_TYPE.toLowerCase(), key1)).isTrue(); + assertThat(loadedTemplateExists("other type", key2)).isTrue(); + assertThat(loadedTemplateExists(QUALITY_PROFILE_TYPE + '.' + md5Hex(key2.getBytes(UTF_8)), DEFAULT_ORGANIZATION_UUID)).isTrue(); + assertThat(db.countRowsOfTable(TABLE_LOADED_TEMPLATES)).isEqualTo(4); + } + + @Test + public void execute_is_reentrant() throws Exception { + setupDefaultOrganization(); + String key = "blabla"; + insertLoadedTemplate(QUALITY_PROFILE_TYPE, key); + + underTest.execute(); + + underTest.execute(); + + assertThat(loadedTemplateExists(QUALITY_PROFILE_TYPE + '.' + md5Hex(key.getBytes(UTF_8)), DEFAULT_ORGANIZATION_UUID)).isTrue(); + assertThat(db.countRowsOfTable(TABLE_LOADED_TEMPLATES)).isEqualTo(1); + } + + private void setupDefaultOrganization() { + insertDefaultOrganizationUuid(DEFAULT_ORGANIZATION_UUID); + insertOrganization(DEFAULT_ORGANIZATION_UUID); + } + + private void insertOrganization(String uuid) { + db.executeInsert( + TABLE_ORGANIZATIONS, + "UUID", uuid, + "KEE", uuid, + "NAME", uuid, + "GUARDED", String.valueOf(false), + "CREATED_AT", "1000", + "UPDATED_AT", "1000"); + } + + private void insertDefaultOrganizationUuid(String defaultOrganizationUuid) { + db.executeInsert( + "INTERNAL_PROPERTIES", + "KEE", "organization.default", + "IS_EMPTY", "false", + "TEXT_VALUE", defaultOrganizationUuid); + } + + private void insertLoadedTemplate(String type, String key) { + db.executeInsert( + TABLE_LOADED_TEMPLATES, + "TEMPLATE_TYPE", type, + "KEE", key); + } + + private boolean loadedTemplateExists(String type, String key) { + return !db.selectFirst(String.format("select id from loaded_templates where template_type='%s' and kee='%s'", type, key)).isEmpty(); + } + +} diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v64/AddIndexLoadedTemplatesTypeTest/loaded_templates_without_index.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v64/AddIndexLoadedTemplatesTypeTest/loaded_templates_without_index.sql new file mode 100644 index 00000000000..1670e896e7f --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v64/AddIndexLoadedTemplatesTypeTest/loaded_templates_without_index.sql @@ -0,0 +1,5 @@ +CREATE TABLE "LOADED_TEMPLATES" ( + "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "KEE" VARCHAR(200), + "TEMPLATE_TYPE" VARCHAR(64) NOT NULL +); diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v64/CleanLoadedTemplateOrphansTest/loaded-templates.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v64/CleanLoadedTemplateOrphansTest/loaded-templates.sql new file mode 100644 index 00000000000..a0de1fe51dd --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v64/CleanLoadedTemplateOrphansTest/loaded-templates.sql @@ -0,0 +1,5 @@ +CREATE TABLE "LOADED_TEMPLATES" ( + "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "KEE" VARCHAR(200), + "TEMPLATE_TYPE" VARCHAR(15) +); diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v64/ExtendLoadedTemplateTypeColumnTest/loaded-templates.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v64/ExtendLoadedTemplateTypeColumnTest/loaded-templates.sql new file mode 100644 index 00000000000..a0de1fe51dd --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v64/ExtendLoadedTemplateTypeColumnTest/loaded-templates.sql @@ -0,0 +1,5 @@ +CREATE TABLE "LOADED_TEMPLATES" ( + "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "KEE" VARCHAR(200), + "TEMPLATE_TYPE" VARCHAR(15) +); diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v64/UpgradeQualityTemplateLoadedTemplatesTest/organizations_internal_properties_and_loaded_templates.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v64/UpgradeQualityTemplateLoadedTemplatesTest/organizations_internal_properties_and_loaded_templates.sql new file mode 100644 index 00000000000..d6be8c2977a --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v64/UpgradeQualityTemplateLoadedTemplatesTest/organizations_internal_properties_and_loaded_templates.sql @@ -0,0 +1,32 @@ +CREATE TABLE "ORGANIZATIONS" ( + "UUID" VARCHAR(40) NOT NULL PRIMARY KEY, + "KEE" VARCHAR(32) NOT NULL, + "NAME" VARCHAR(64) NOT NULL, + "DESCRIPTION" VARCHAR(256), + "URL" VARCHAR(256), + "AVATAR_URL" VARCHAR(256), + "GUARDED" BOOLEAN NOT NULL, + "USER_ID" INTEGER, + "DEFAULT_PERM_TEMPLATE_PROJECT" VARCHAR(40), + "DEFAULT_PERM_TEMPLATE_VIEW" VARCHAR(40), + "CREATED_AT" BIGINT NOT NULL, + "UPDATED_AT" BIGINT NOT NULL +); +CREATE UNIQUE INDEX "PK_ORGANIZATIONS" ON "ORGANIZATIONS" ("UUID"); +CREATE UNIQUE INDEX "ORGANIZATION_KEY" ON "ORGANIZATIONS" ("KEE"); + +CREATE TABLE "INTERNAL_PROPERTIES" ( + "KEE" VARCHAR(50) NOT NULL PRIMARY KEY, + "IS_EMPTY" BOOLEAN NOT NULL, + "TEXT_VALUE" VARCHAR(4000), + "CLOB_VALUE" CLOB, + "CREATED_AT" BIGINT +); +CREATE UNIQUE INDEX "UNIQ_INTERNAL_PROPERTIES" ON "INTERNAL_PROPERTIES" ("KEE"); + +CREATE TABLE "LOADED_TEMPLATES" ( + "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "KEE" VARCHAR(200), + "TEMPLATE_TYPE" VARCHAR(64) NOT NULL +); +CREATE INDEX "IX_LOADED_TEMPLATES_TYPE" ON "LOADED_TEMPLATES" ("TEMPLATE_TYPE"); diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/RegisterQualityProfiles.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/RegisterQualityProfiles.java index 93b6f4f116a..1f3770b9d9b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/RegisterQualityProfiles.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/RegisterQualityProfiles.java @@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ListMultimap; import com.google.common.collect.Multimaps; +import java.security.MessageDigest; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -33,6 +34,7 @@ import java.util.Optional; import java.util.Set; import javax.annotation.Nullable; +import org.apache.commons.codec.digest.DigestUtils; import org.sonar.api.profiles.ProfileDefinition; import org.sonar.api.profiles.RulesProfile; import org.sonar.api.resources.Languages; @@ -54,8 +56,12 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; +import static java.lang.String.format; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.apache.commons.codec.binary.Hex.encodeHexString; import static org.apache.commons.lang.StringUtils.isNotEmpty; import static org.apache.commons.lang.StringUtils.lowerCase; +import static org.sonar.db.loadedtemplate.LoadedTemplateDto.QUALITY_PROFILE_TYPE; /** * Synchronize Quality profiles during server startup @@ -226,12 +232,15 @@ private static List toQualityProfiles(List builder.build(md5Digest)) + .collect(Collectors.toList(builders.size())); } private void registerProfilesForLanguage(DbSession session, OrganizationDto organization, List qualityProfiles, List changes) { qualityProfiles.stream() - .filter(qp -> shouldRegister(qp.getQProfileName(), session)) + .filter(qp -> shouldRegister(session, qp, organization.getUuid())) .forEach(qp -> register(session, organization, qp, changes)); session.commit(); } @@ -254,32 +263,35 @@ private void register(DbSession session, OrganizationDto organization, QualityPr changes.addAll(ruleActivator.activate(session, activation, newQProfileDto)); } - LoadedTemplateDto template = new LoadedTemplateDto(templateKey(qualityProfile.getQProfileName()), LoadedTemplateDto.QUALITY_PROFILE_TYPE); + LoadedTemplateDto template = new LoadedTemplateDto(organization.getUuid(), qualityProfile.getLoadedTemplateType()); dbClient.loadedTemplateDao().insert(template, session); session.commit(); } - private boolean shouldRegister(QProfileName key, DbSession session) { + private boolean shouldRegister(DbSession session, QualityProfile qualityProfile, String organizationUuid) { // check if the profile was already registered in the past return dbClient.loadedTemplateDao() - .countByTypeAndKey(LoadedTemplateDto.QUALITY_PROFILE_TYPE, templateKey(key), session) == 0; - } - - static String templateKey(QProfileName key) { - return lowerCase(key.getLanguage(), Locale.ENGLISH) + ":" + key.getName(); + .countByTypeAndKey(qualityProfile.getLoadedTemplateType(), organizationUuid, session) == 0; } private static final class QualityProfile { private final QProfileName qProfileName; private final boolean isDefault; + private final String loadedTemplateType; private final List activeRules; - public QualityProfile(Builder builder) { + public QualityProfile(Builder builder, MessageDigest messageDigest) { this.qProfileName = new QProfileName(builder.getLanguage(), builder.getName()); this.isDefault = builder.declaredDefault || builder.computedDefault; + this.loadedTemplateType = computeLoadedTemplateType(this.qProfileName, messageDigest); this.activeRules = ImmutableList.copyOf(builder.activeRules); } + private static String computeLoadedTemplateType(QProfileName qProfileName, MessageDigest messageDigest) { + String qpIdentifier = lowerCase(qProfileName.getLanguage(), Locale.ENGLISH) + ":" + qProfileName.getName(); + return format("%s.%s", QUALITY_PROFILE_TYPE, encodeHexString(messageDigest.digest(qpIdentifier.getBytes(UTF_8)))); + } + public String getName() { return qProfileName.getName(); } @@ -296,6 +308,10 @@ public boolean isDefault() { return isDefault; } + public String getLoadedTemplateType() { + return loadedTemplateType; + } + public List getActiveRules() { return activeRules; } @@ -344,8 +360,8 @@ Builder addRules(List rules) { return this; } - QualityProfile build() { - return new QualityProfile(this); + QualityProfile build(MessageDigest messageDigest) { + return new QualityProfile(this, messageDigest); } } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/RegisterQualityProfilesMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/RegisterQualityProfilesMediumTest.java index ca16c967b25..1988912a568 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/RegisterQualityProfilesMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/RegisterQualityProfilesMediumTest.java @@ -20,6 +20,7 @@ package org.sonar.server.qualityprofile; import java.util.Map; +import org.apache.commons.codec.digest.DigestUtils; import org.junit.After; import org.junit.Test; import org.sonar.api.profiles.ProfileDefinition; @@ -34,7 +35,6 @@ import org.sonar.api.utils.ValidationMessages; import org.sonar.db.DbClient; import org.sonar.db.DbSession; -import org.sonar.db.loadedtemplate.LoadedTemplateDto; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.qualityprofile.ActiveRuleDao; import org.sonar.db.qualityprofile.ActiveRuleDto; @@ -50,8 +50,12 @@ import org.sonar.server.tester.ServerTester; import static com.google.common.collect.Lists.newArrayList; +import static java.lang.String.format; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.apache.commons.lang.StringUtils.lowerCase; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.guava.api.Assertions.assertThat; +import static org.sonar.db.loadedtemplate.LoadedTemplateDto.QUALITY_PROFILE_TYPE; import static org.sonar.server.qualityprofile.QProfileTesting.getDefaultOrganization; // TODO replace this MediumTest by DbTester and EsTester @@ -145,8 +149,7 @@ public void register_profile_definitions() { assertThat(activeRule.getSeverityString()).isEqualTo(Severity.CRITICAL); // Check ActiveRuleParameters in DB - Map params = - ActiveRuleParamDto.groupByKey(activeRuleDao.selectParamsByActiveRuleId(dbSession, activeRule.getId())); + Map params = ActiveRuleParamDto.groupByKey(activeRuleDao.selectParamsByActiveRuleId(dbSession, activeRule.getId())); assertThat(params).hasSize(2); // set by profile assertThat(params.get("acceptWhitespace").getValue()).isEqualTo("true"); @@ -190,7 +193,8 @@ public void mark_profile_as_default() { @Test public void use_sonar_way_as_default_profile_if_none_are_marked_as_default() { - tester = new ServerTester().withEsIndexes().withStartupTasks().addXoo().addComponents(new SimpleProfileDefinition("Sonar way", false), new SimpleProfileDefinition("Other way", false)); + tester = new ServerTester().withEsIndexes().withStartupTasks().addXoo().addComponents(new SimpleProfileDefinition("Sonar way", false), + new SimpleProfileDefinition("Other way", false)); tester.start(); dbSession = dbClient().openSession(false); @@ -204,11 +208,10 @@ public void do_not_reset_default_profile_if_still_valid() { tester.start(); dbSession = dbClient().openSession(false); - DefaultOrganizationProvider defaultOrganizationProvider = tester.get(DefaultOrganizationProvider.class); OrganizationDto organization = QProfileTesting.getDefaultOrganization(tester, dbClient(), dbSession); QualityProfileDao dao = dbClient().qualityProfileDao(); - dao.update(dbSession, dao.selectDefaultProfile(dbSession, organization,"xoo") + dao.update(dbSession, dao.selectDefaultProfile(dbSession, organization, "xoo") .setDefault(false)); dao.update(dbSession, dao.selectByNameAndLanguage(organization, "two", "xoo", dbSession) .setDefault(true)); @@ -230,10 +233,11 @@ public void clean_up_profiles_if_missing_loaded_template() { tester.start(); dbSession = dbClient().openSession(false); - String templateKey = RegisterQualityProfiles.templateKey(new QProfileName("xoo", "Basic")); - dbClient().loadedTemplateDao().delete(dbSession, LoadedTemplateDto.QUALITY_PROFILE_TYPE, templateKey); + String defaultOrganizationUuid = tester.getContainer().getComponentByType(DefaultOrganizationProvider.class).get().getUuid(); + String loadedTemplateType = computeLoadedTemplateType(new QProfileName("xoo", "Basic")); + dbClient().loadedTemplateDao().delete(dbSession, loadedTemplateType, defaultOrganizationUuid); dbSession.commit(); - assertThat(dbClient().loadedTemplateDao().countByTypeAndKey(LoadedTemplateDto.QUALITY_PROFILE_TYPE, templateKey, dbSession)).isEqualTo(0); + assertThat(dbClient().loadedTemplateDao().countByTypeAndKey(loadedTemplateType, defaultOrganizationUuid, dbSession)).isEqualTo(0); dbSession.close(); tester.get(Platform.class).restart(); @@ -307,4 +311,9 @@ public RulesProfile createProfile(ValidationMessages validation) { return profile; } } + + private String computeLoadedTemplateType(QProfileName qProfileName) { + String qpIdentifier = lowerCase(qProfileName.getLanguage()) + ":" + qProfileName.getName(); + return format("%s.%s", QUALITY_PROFILE_TYPE, DigestUtils.md5Hex(qpIdentifier.getBytes(UTF_8))); + } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/RegisterQualityProfilesTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/RegisterQualityProfilesTest.java index 3224f02cea9..792bbf3c098 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/RegisterQualityProfilesTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/RegisterQualityProfilesTest.java @@ -24,6 +24,7 @@ import java.util.Collections; import java.util.Date; import java.util.List; +import org.apache.commons.codec.digest.DigestUtils; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -44,7 +45,10 @@ import org.sonar.server.qualityprofile.index.ActiveRuleIndexer; import org.sonar.server.tester.UserSessionRule; +import static java.lang.String.format; +import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Arrays.asList; +import static org.apache.commons.lang.StringUtils.lowerCase; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verifyZeroInteractions; @@ -57,7 +61,7 @@ public class RegisterQualityProfilesTest { private static final DummyLanguage BAR_LANGUAGE = new DummyLanguage("bar"); private static final String TABLE_RULES_PROFILES = "RULES_PROFILES"; private static final String TYPE_QUALITY_PROFILE = "QUALITY_PROFILE"; - public static final String SONAR_WAY_QP_NAME = "Sonar way"; + private static final String SONAR_WAY_QP_NAME = "Sonar way"; @Rule public DbTester dbTester = DbTester.create(System2.INSTANCE); @@ -117,13 +121,16 @@ public void start_throws_IAE_if_profileDefinition_creates_RulesProfile_with_empt public void start_creates_qp_if_language_exists_and_store_flag_in_loaded_templates() { String uuid = "generated uuid"; long now = 2_456_789; - RegisterQualityProfiles underTest = mockedEs(Collections.singletonList(new DummyProfileDefinition("foo", "foo1", false)), new Languages(FOO_LANGUAGE)); + DummyProfileDefinition qpDefinition = new DummyProfileDefinition("foo", "foo1", false); + RegisterQualityProfiles underTest = mockedEs(Collections.singletonList(qpDefinition), new Languages(FOO_LANGUAGE)); mockForSingleQPInsert(uuid, now); underTest.start(); + OrganizationDto organization = dbTester.getDefaultOrganization(); QualityProfileDto dto = getPersistedQP(dbTester.getDefaultOrganization(), FOO_LANGUAGE, "foo1"); assertThat(dto.getId()).isNotNull(); + assertThat(dto.getOrganizationUuid()).isEqualTo(organization.getUuid()); assertThat(dto.getLanguage()).isEqualTo(FOO_LANGUAGE.getKey()); assertThat(dto.getName()).isEqualTo("foo1"); assertThat(dto.getKee()).isEqualTo(uuid); @@ -135,7 +142,7 @@ public void start_creates_qp_if_language_exists_and_store_flag_in_loaded_templat assertThat(dto.isDefault()).isTrue(); assertThat(dbTester.countRowsOfTable(dbTester.getSession(), TABLE_RULES_PROFILES)).isEqualTo(1); - assertThat(dbClient.loadedTemplateDao().countByTypeAndKey(TYPE_QUALITY_PROFILE, "foo:foo1", dbTester.getSession())) + assertThat(dbClient.loadedTemplateDao().countByTypeAndKey(computeLoadedTemplateType(qpDefinition), organization.getUuid(), dbTester.getSession())) .isEqualTo(1); } @@ -178,9 +185,10 @@ public void start_makes_first_qp_of_a_language_default_when_none_flagged_as_so() @Test public void start_does_not_create_sq_if_loaded_profile_already_exists() { - dbClient.loadedTemplateDao().insert(new LoadedTemplateDto("foo:foo1", TYPE_QUALITY_PROFILE), dbTester.getSession()); + DummyProfileDefinition qpDefinition = new DummyProfileDefinition("foo", "foo1", false); + dbClient.loadedTemplateDao().insert(new LoadedTemplateDto(dbTester.getDefaultOrganization().getUuid(), computeLoadedTemplateType(qpDefinition)), dbTester.getSession()); dbTester.commit(); - RegisterQualityProfiles underTest = mockedEs(Collections.singletonList(new DummyProfileDefinition("foo", "foo1", false)), new Languages(FOO_LANGUAGE)); + RegisterQualityProfiles underTest = mockedEs(Collections.singletonList(qpDefinition), new Languages(FOO_LANGUAGE)); underTest.start(); @@ -220,9 +228,9 @@ public void start_creates_different_qp_and_their_loaded_templates_if_several_pro long date2 = 4_231_654L; String name = "doh"; - RegisterQualityProfiles underTest = mockedEs( - asList(new DummyProfileDefinition("foo", name, true), new DummyProfileDefinition("bar", name, true)), - new Languages(FOO_LANGUAGE, BAR_LANGUAGE)); + DummyProfileDefinition qpDefinition1 = new DummyProfileDefinition("foo", name, true); + DummyProfileDefinition qpDefinition2 = new DummyProfileDefinition("bar", name, true); + RegisterQualityProfiles underTest = mockedEs(asList(qpDefinition1, qpDefinition2), new Languages(FOO_LANGUAGE, BAR_LANGUAGE)); when(mockedUuidFactory.create()).thenReturn(uuid1).thenReturn(uuid2).thenThrow(new UnsupportedOperationException("uuidFactory should be called only twice")); when(mockedSystem2.now()).thenReturn(date1).thenReturn(date2).thenThrow(new UnsupportedOperationException("now should be called only twice")); @@ -256,9 +264,9 @@ public void start_creates_different_qp_and_their_loaded_templates_if_several_pro assertThat(dto.isDefault()).isTrue(); assertThat(dbTester.countRowsOfTable(dbTester.getSession(), TABLE_RULES_PROFILES)).isEqualTo(2); - assertThat(dbClient.loadedTemplateDao().countByTypeAndKey(TYPE_QUALITY_PROFILE, "foo:doh", dbTester.getSession())) + assertThat(dbClient.loadedTemplateDao().countByTypeAndKey(computeLoadedTemplateType(qpDefinition1), organization.getUuid(), dbTester.getSession())) .isEqualTo(1); - assertThat(dbClient.loadedTemplateDao().countByTypeAndKey(TYPE_QUALITY_PROFILE, "bar:doh", dbTester.getSession())) + assertThat(dbClient.loadedTemplateDao().countByTypeAndKey(computeLoadedTemplateType(qpDefinition2), organization.getUuid(), dbTester.getSession())) .isEqualTo(1); } @@ -410,4 +418,9 @@ public String[] getFileSuffixes() { return new String[] {key}; } } + + private String computeLoadedTemplateType(DummyProfileDefinition qpDefinition) { + String qpIdentifier = lowerCase(qpDefinition.getLanguage()) + ":" + qpDefinition.getName(); + return format("%s.%s", TYPE_QUALITY_PROFILE, DigestUtils.md5Hex(qpIdentifier.getBytes(UTF_8))); + } }