From: Julien Lancelot Date: Mon, 27 Feb 2017 16:42:52 +0000 (+0100) Subject: SONAR-8847 Set COPY_COMPONENT_UUID of local views X-Git-Tag: 6.4-RC1~851 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=0b2660c4879ddc964e804705b0ce98141239ab92;p=sonarqube.git SONAR-8847 Set COPY_COMPONENT_UUID of local views --- 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 138d9eaa3cf..ca981dba0c0 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 @@ -533,6 +533,7 @@ INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1516'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1517'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1600'); +INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1601'); 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-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 bfd2f9bc27f..2eb29b7bcec 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 @@ -27,6 +27,7 @@ public class DbVersion64 implements DbVersion { @Override public void addSteps(MigrationStepRegistry registry) { registry - .add(1600, "Add Projects.TAGS", AddTagsToProjects.class); + .add(1600, "Add Projects.TAGS", AddTagsToProjects.class) + .add(1601, "Set PROJECTS.COPY_COMPONENT_UUID on local views", SetCopyComponentUuidOnLocalViews.class); } } diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/SetCopyComponentUuidOnLocalViews.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/SetCopyComponentUuidOnLocalViews.java new file mode 100644 index 00000000000..dcc3fa53fdb --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/SetCopyComponentUuidOnLocalViews.java @@ -0,0 +1,99 @@ +/* + * 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.util.HashMap; +import java.util.Map; +import org.sonar.db.Database; +import org.sonar.server.platform.db.migration.step.DataChange; +import org.sonar.server.platform.db.migration.step.MassUpdate; + +/** + * Set PROJECTS#COPY_COMPONENT_UUID on local views. + * + * Here's how local sub views are detected : + * + */ +public class SetCopyComponentUuidOnLocalViews extends DataChange { + + private static final String QUALIFIER_SUB_VIEW = "SVW"; + private static final String QUALIFIER_VIEW = "VW"; + private static final String SCOPE_PROJECT = "PRJ"; + + public SetCopyComponentUuidOnLocalViews(Database db) { + super(db); + } + + @Override + protected void execute(Context context) throws SQLException { + Map rootUuidsByKeys = selectRootUuidsByKeys(context); + + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate.select("SELECT p.kee, p.uuid FROM projects p " + + "WHERE p.qualifier = ? " + + "AND p.scope = ? " + + "AND p.copy_component_uuid IS NULL " + + "AND p.enabled = ? ") + .setString(1, QUALIFIER_SUB_VIEW) + .setString(2, SCOPE_PROJECT) + .setBoolean(3, true); + + massUpdate.update("UPDATE projects set " + + "copy_component_uuid=? " + + "WHERE uuid=?"); + massUpdate.execute((row, update) -> { + String subViewKey = row.getString(1); + int lastViewKeyIndex = subViewKey.lastIndexOf(':'); + if (lastViewKeyIndex < 0) { + return false; + } + String possibleRootViewUuid = subViewKey.substring(lastViewKeyIndex + 1); + String rootUuid = rootUuidsByKeys.get(possibleRootViewUuid); + if (rootUuid == null) { + return false; + } + update.setString(1, rootUuid); + update.setString(2, row.getString(2)); + return true; + }); + } + + private static Map selectRootUuidsByKeys(Context context) throws SQLException { + Map rootUuids = new HashMap<>(); + context.prepareSelect("SELECT p.kee, p.uuid FROM projects p " + + "WHERE p.qualifier = ? " + + "AND p.scope = ? " + + "AND p.enabled = ? ") + .setString(1, QUALIFIER_VIEW) + .setString(2, SCOPE_PROJECT) + .setBoolean(3, true) + .scroll(row -> { + String key = row.getString(1); + String uuid = row.getString(2); + rootUuids.put(key, uuid); + }); + return rootUuids; + } +} 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 f5b75feaa96..cbfdc7cd069 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 class DbVersion64Test { @Test public void verify_migration_count() { - verifyMigrationCount(underTest, 1); + verifyMigrationCount(underTest, 2); } } diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/SetCopyComponentUuidOnLocalViewsTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/SetCopyComponentUuidOnLocalViewsTest.java new file mode 100644 index 00000000000..82168dc834c --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/SetCopyComponentUuidOnLocalViewsTest.java @@ -0,0 +1,210 @@ +/* + * 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 com.google.common.collect.ImmutableMap; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.core.util.stream.Collectors; +import org.sonar.db.CoreDbTester; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SetCopyComponentUuidOnLocalViewsTest { + + private static final String TABLE_PROJECTS = "projects"; + + private static final String QUALIFIER_SUB_VIEW = "SVW"; + private static final String QUALIFIER_VIEW = "VW"; + private static final String SCOPE_PROJECT = "PRJ"; + + @Rule + public CoreDbTester db = CoreDbTester.createForSchema(SetCopyComponentUuidOnLocalViewsTest.class, "in_progress_projects.sql"); + + private SetCopyComponentUuidOnLocalViews underTest = new SetCopyComponentUuidOnLocalViews(db.database()); + + @Test + public void migration_has_no_effect_on_empty_tables() throws SQLException { + underTest.execute(); + + assertThat(db.countRowsOfTable(TABLE_PROJECTS)).isZero(); + } + + @Test + public void set_copy_component_uuid_on_sub_views() throws Exception { + insertProject("VIEW1", "VIEW1_UUID", null, QUALIFIER_VIEW, SCOPE_PROJECT); + insertProject("VIEW2", "VIEW2_UUID", null, QUALIFIER_VIEW, SCOPE_PROJECT); + insertProject("VIEW1:VIEW2", "LOCAL_VIEW2_UUID", null, QUALIFIER_SUB_VIEW, SCOPE_PROJECT); + + underTest.execute(); + + verifyOnlyOneComponentHasCopyComponentUuid("LOCAL_VIEW2_UUID", "VIEW2_UUID"); + } + + @Test + public void set_copy_component_uuid_on_imbricated_sub_views() throws Exception { + insertProject("VIEW1", "VIEW1_UUID", null, QUALIFIER_VIEW, SCOPE_PROJECT); + insertProject("VIEW2", "VIEW2_UUID", null, QUALIFIER_VIEW, SCOPE_PROJECT); + insertProject("VIEW3", "VIEW3_UUID", null, QUALIFIER_VIEW, SCOPE_PROJECT); + insertProject("VIEW1:VIEW2", "LOCAL_VIEW2_UUID", null, QUALIFIER_SUB_VIEW, SCOPE_PROJECT); + insertProject("VIEW2:VIEW3", "LOCAL_VIEW3_UUID", null, QUALIFIER_SUB_VIEW, SCOPE_PROJECT); + insertProject("VIEW1:VIEW2:VIEW3", "LOCAL_VIEW3_BIS_UUID", null, QUALIFIER_SUB_VIEW, SCOPE_PROJECT); + + underTest.execute(); + + verifyComponentsHavingCopyComponentUuid( + new Component("LOCAL_VIEW2_UUID", "VIEW2_UUID"), + new Component("LOCAL_VIEW3_UUID", "VIEW3_UUID"), + new Component("LOCAL_VIEW3_BIS_UUID", "VIEW3_UUID")); + } + + @Test + public void migration_doest_not_update_copy_component_uuid_if_already_exists() throws Exception { + insertProject("VIEW1", "VIEW1_UUID", null, QUALIFIER_VIEW, SCOPE_PROJECT); + insertProject("VIEW2", "VIEW2_UUID", null, QUALIFIER_VIEW, SCOPE_PROJECT); + insertProject("VIEW1:VIEW2", "LOCAL_VIEW2_UUID", "ALREADY_EXISTING", QUALIFIER_SUB_VIEW, SCOPE_PROJECT); + + underTest.execute(); + + verifyOnlyOneComponentHasCopyComponentUuid("LOCAL_VIEW2_UUID", "ALREADY_EXISTING"); + } + + @Test + public void migration_ignores_sub_view_having_bad_key_format() throws Exception { + insertProject("VIEW1", "VIEW1_UUID", null, QUALIFIER_VIEW, SCOPE_PROJECT); + insertProject("VIEW2", "VIEW2_UUID", null, QUALIFIER_VIEW, SCOPE_PROJECT); + // Missing ':' in the key + insertProject("VIEW1_VIEW2", "LOCAL_VIEW2_UUID", null, QUALIFIER_SUB_VIEW, SCOPE_PROJECT); + + underTest.execute(); + + verifyComponentsHavingCopyComponentUuid(); + } + + @Test + public void migration_does_nothing_when_no_root_views() throws Exception { + insertProject("VIEW1:VIEW2", "LOCAL_VIEW2_UUID", null, QUALIFIER_SUB_VIEW, SCOPE_PROJECT, false); + + underTest.execute(); + + verifyComponentsHavingCopyComponentUuid(); + } + + @Test + public void migration_ignores_disabled_views() throws Exception { + insertProject("VIEW1", "VIEW1_UUID", null, QUALIFIER_VIEW, SCOPE_PROJECT, false); + insertProject("VIEW2", "VIEW2_UUID", null, QUALIFIER_VIEW, SCOPE_PROJECT, false); + insertProject("VIEW1:VIEW2", "LOCAL_VIEW2_UUID", null, QUALIFIER_SUB_VIEW, SCOPE_PROJECT, false); + + underTest.execute(); + + verifyComponentsHavingCopyComponentUuid(); + } + + @Test + public void migration_is_re_entrant() throws Exception { + insertProject("VIEW1", "VIEW1_UUID", null, QUALIFIER_VIEW, SCOPE_PROJECT); + insertProject("VIEW2", "VIEW2_UUID", null, QUALIFIER_VIEW, SCOPE_PROJECT); + insertProject("VIEW1:VIEW2", "LOCAL_VIEW2_UUID", null, QUALIFIER_SUB_VIEW, SCOPE_PROJECT); + + underTest.execute(); + verifyOnlyOneComponentHasCopyComponentUuid("LOCAL_VIEW2_UUID", "VIEW2_UUID"); + + underTest.execute(); + verifyOnlyOneComponentHasCopyComponentUuid("LOCAL_VIEW2_UUID", "VIEW2_UUID"); + } + + + private void insertProject(String key, String uuid, @Nullable String copyComponentUuid, String qualifier, String scope) { + insertProject(key, uuid, copyComponentUuid, qualifier, scope, true); + } + + private void insertProject(String key, String uuid, @Nullable String copyComponentUuid, String qualifier, String scope, boolean enabled) { + Map values = new HashMap<>(ImmutableMap.builder() + .put("KEE", key) + .put("QUALIFIER", qualifier) + .put("SCOPE", scope) + .put("UUID", uuid) + .put("UUID_PATH", "UUID_PATH") + .put("ROOT_UUID", "ROOT_UUID") + .put("PROJECT_UUID", "ROOT_UUID") + .put("ORGANIZATION_UUID", "ORGANIZATION_UUID") + .put("ENABLED", enabled) + .build()); + if (copyComponentUuid != null) { + values.put("COPY_COMPONENT_UUID", copyComponentUuid); + } + db.executeInsert(TABLE_PROJECTS, values); + } + + private void verifyOnlyOneComponentHasCopyComponentUuid(String uuid, String copyComponentUuid) { + verifyComponentsHavingCopyComponentUuid(new Component(uuid, copyComponentUuid)); + } + + private void verifyComponentsHavingCopyComponentUuid(Component... expectedComponents) { + Map expectedComponentsByUuid = Arrays.stream(expectedComponents).collect(Collectors.uniqueIndex(Component::getUuid)); + + List> rows = db.select("SELECT uuid, copy_component_uuid FROM " + TABLE_PROJECTS + " WHERE copy_component_uuid IS NOT NULL"); + Map components = rows.stream() + .map(map -> new Component((String) map.get("UUID"), (String) map.get("COPY_COMPONENT_UUID"))) + .collect(Collectors.uniqueIndex(Component::getUuid)); + assertThat(components.keySet()).containsExactlyElementsOf(expectedComponentsByUuid.keySet()); + components.entrySet().forEach(entry -> { + Component expectedComponent = expectedComponentsByUuid.get(entry.getKey()); + assertThat(expectedComponent.getUuid()).isEqualTo(entry.getKey()); + assertThat(expectedComponent.getCopyComponentUuid()).isEqualTo(entry.getValue().getCopyComponentUuid()); + }); + } + + private static class Component { + private final String uuid; + private final String copyComponentUuid; + + Component(String uuid, @Nullable String copyComponentUuid) { + this.uuid = uuid; + this.copyComponentUuid = copyComponentUuid; + } + + String getUuid() { + return uuid; + } + + @CheckForNull + String getCopyComponentUuid() { + return copyComponentUuid; + } + + @Override + public String toString() { + return "Component{" + + "uuid='" + uuid + '\'' + + ", copyComponentUuid='" + copyComponentUuid + '\'' + + '}'; + } + } +} diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v64/SetCopyComponentUuidOnLocalViewsTest/in_progress_projects.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v64/SetCopyComponentUuidOnLocalViewsTest/in_progress_projects.sql new file mode 100644 index 00000000000..c8113a8215c --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v64/SetCopyComponentUuidOnLocalViewsTest/in_progress_projects.sql @@ -0,0 +1,43 @@ +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), + "MODULE_UUID" VARCHAR(50), + "MODULE_UUID_PATH" VARCHAR(1500), + "NAME" VARCHAR(2000), + "DESCRIPTION" VARCHAR(2000), + "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");