From 08f35fbca44838befd61d3d13ee1448d156eb69f Mon Sep 17 00:00:00 2001 From: Belen Pruvost Date: Thu, 15 Apr 2021 17:20:48 +0200 Subject: [PATCH] SONAR-14691 - Fix 4122 DB migration for repeated projects in apps --- ...rateApplicationDefinitionsFromXmlToDb.java | 13 +++---- ...ApplicationDefinitionsFromXmlToDbTest.java | 36 +++++++++++++++++++ 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/MigrateApplicationDefinitionsFromXmlToDb.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/MigrateApplicationDefinitionsFromXmlToDb.java index e5f6fd50618..2dce4e79808 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/MigrateApplicationDefinitionsFromXmlToDb.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/MigrateApplicationDefinitionsFromXmlToDb.java @@ -29,6 +29,7 @@ import java.sql.SQLException; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -122,15 +123,15 @@ public class MigrateApplicationDefinitionsFromXmlToDb extends DataChange { // skip migration if: // - application only exists in xml and not in the db. It will be removed from the xml at later stage of the migration. // - application contains no projects- it's already in a valid db state - List projects = app.getProjects(); - if (applicationUuid == null || projects.isEmpty()) { + Set uniqueProjects = new HashSet<>(app.getProjects()); + if (applicationUuid == null || uniqueProjects.isEmpty()) { return; } List alreadyAddedProjects = context.prepareSelect(SELECT_PROJECTS_BY_APP).setString(1, applicationUuid) .list(r -> r.getString(1)); - String queryParam = projects.stream().map(uuid -> "'" + uuid + "'").collect(Collectors.joining(",")); + String queryParam = uniqueProjects.stream().map(uuid -> "'" + uuid + "'").collect(Collectors.joining(",")); Map projectUuidsByKeys = context.prepareSelect(format(SELECT_PROJECTS_BY_KEYS, queryParam)) .list(r -> new AbstractMap.SimpleEntry<>(r.getString(1), r.getString(2))) .stream() @@ -138,7 +139,7 @@ public class MigrateApplicationDefinitionsFromXmlToDb extends DataChange { .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue)); if (!projectUuidsByKeys.isEmpty()) { - insertApplicationProjects(context, app, applicationUuid, projectUuidsByKeys, now); + insertApplicationProjects(context, app, applicationUuid, projectUuidsByKeys, uniqueProjects, now); } if (!app.getApplicationBranches().isEmpty()) { insertApplicationBranchesProjects(context, app, applicationUuid, projectUuidsByKeys, now); @@ -146,11 +147,11 @@ public class MigrateApplicationDefinitionsFromXmlToDb extends DataChange { } private void insertApplicationProjects(Context context, ViewXml.ViewDef app, String applicationUuid, - Map projectUuidsByKeys, long createdTime) throws SQLException { + Map projectUuidsByKeys, Set uniqueProjects, long createdTime) throws SQLException { Upsert insertApplicationProjectsQuery = context.prepareUpsert("insert into " + "app_projects(uuid, application_uuid, project_uuid, created_at) " + "values (?, ?, ?, ?)"); - for (String projectKey : app.getProjects()) { + for (String projectKey : uniqueProjects) { String applicationProjectUuid = uuidFactory.create(); String projectUuid = projectUuidsByKeys.get(projectKey); diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/MigrateApplicationDefinitionsFromXmlToDbTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/MigrateApplicationDefinitionsFromXmlToDbTest.java index 794a438a47f..0d083a13f65 100644 --- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/MigrateApplicationDefinitionsFromXmlToDbTest.java +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/MigrateApplicationDefinitionsFromXmlToDbTest.java @@ -78,6 +78,22 @@ public class MigrateApplicationDefinitionsFromXmlToDbTest { " \n" + ""; + private static final String APP_WITH_DUPLICATED_PROJECTS_XML = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + "

proj1-key

\n" + + "

proj1-key

\n" + + " \n" + + "

proj1-key

\n" + + "
\n" + + " \n" + + "

proj1-key

\n" + + "
\n" + + "
\n" + + "
"; + private static final String COMPLEX_XML_BEFORE = "\n" + " \n" + " \n" + @@ -433,6 +449,26 @@ public class MigrateApplicationDefinitionsFromXmlToDbTest { tuple(APP_2_UUID, PROJECT_2_UUID, APP_2_BRANCH_1_UUID, PROJECT_2_BRANCH_1_UUID)); } + @Test + public void migrates_applications_handling_project_duplications_to_new_tables() throws SQLException { + setupProjectsAndApps(); + insertViewsDefInternalProperty(APP_WITH_DUPLICATED_PROJECTS_XML); + + underTest.execute(); + + assertThat(db.select("select uuid from projects")) + .extracting(r -> r.get("UUID")) + .containsExactlyInAnyOrder(PROJECT_1_UUID, PROJECT_2_UUID, APP_1_UUID, APP_2_UUID); + assertThat(db.select("select application_uuid, project_uuid from app_projects")) + .extracting(r -> r.get("APPLICATION_UUID"), r -> r.get("PROJECT_UUID")) + .containsExactly(tuple(APP_1_UUID, PROJECT_1_UUID)); + assertThat(db.select("select application_uuid, project_uuid, application_branch_uuid, project_branch_uuid from app_branch_project_branch")) + .extracting(r -> r.get("APPLICATION_UUID"), r -> r.get("PROJECT_UUID"), r -> r.get("APPLICATION_BRANCH_UUID"), r -> r.get("PROJECT_BRANCH_UUID")) + .containsExactlyInAnyOrder( + tuple(APP_1_UUID, PROJECT_1_UUID, APP_1_BRANCH_1_UUID, PROJECT_1_BRANCH_1_UUID), + tuple(APP_1_UUID, PROJECT_1_UUID, APP_1_BRANCH_2_UUID, PROJECT_1_BRANCH_2_UUID)); + } + @Test public void migration_is_resilient() throws SQLException { setupProjectsAndApps(); -- 2.39.5