aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--build.gradle2
-rw-r--r--server/sonar-ce-common/src/main/java/org/sonar/ce/queue/CeTaskSubmit.java14
-rw-r--r--server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java2
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java2
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java7
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java2
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/component/ApplicationProjectsDao.java108
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/component/ApplicationProjectsMapper.java69
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDao.java13
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchMapper.java1
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/component/SelectionMode.java27
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/component/SnapshotDao.java5
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectDao.java12
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectDto.java1
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectMapper.java4
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java17
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java2
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java8
-rw-r--r--server/sonar-db-dao/src/main/resources/org/sonar/db/component/ApplicationProjectsMapper.xml196
-rw-r--r--server/sonar-db-dao/src/main/resources/org/sonar/db/component/BranchMapper.xml7
-rw-r--r--server/sonar-db-dao/src/main/resources/org/sonar/db/project/ProjectMapper.xml22
-rw-r--r--server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml27
-rw-r--r--server/sonar-db-dao/src/schema/schema-sq.ddl26
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/component/ApplicationProjectsDaoTest.java157
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/measure/ProjectMeasuresIndexerIteratorTest.java4
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java78
-rw-r--r--server/sonar-db-dao/src/testFixtures/java/org/sonar/db/component/ComponentDbTester.java61
-rw-r--r--server/sonar-db-dao/src/testFixtures/java/org/sonar/db/component/ComponentTesting.java2
-rw-r--r--server/sonar-db-migration/build.gradle2
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/AddIndexToApplicationBranchProjs.java68
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/AddIndexToApplicationProjects.java56
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/AddPkToApplicationBranchProjs.java38
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/AddPkToApplicationProjects.java38
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/CreateApplicationBranchProjs.java48
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/CreateApplicationProjectsTable.java45
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/DbVersion86.java10
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/MigrateApplicationDefinitionsFromXmlToDb.java732
-rw-r--r--server/sonar-db-migration/src/main/resources/static/views.xsd85
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/AddIndexToApplicationBranchProjsTest.java44
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/AddIndexToApplicationProjectsTest.java42
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/AddPkToApplicationBranchProjsTest.java40
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/AddPkToApplicationProjectsTest.java40
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/CreateApplicationProjectsTableTest.java43
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/MigrateApplicationDefinitionsFromXmlToDbTest.java717
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/AddIndexToApplicationBranchProjsTest/schema.sql9
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/AddIndexToApplicationProjectsTest/schema.sql7
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/AddPkToApplicationBranchProjsTest/schema.sql8
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/AddPkToApplicationProjectsTest/schema.sql6
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/MigrateApplicationDefinitionsFromXmlToDbTest/schema.sql66
-rw-r--r--server/sonar-webserver-api/src/main/java/org/sonar/server/project/Visibility.java2
-rw-r--r--server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/tester/UserSessionRule.java7
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ComponentCleanerService.java7
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ComponentUpdater.java9
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/NewComponent.java15
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/queue/ReportSubmitterTest.java3
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/component/ComponentCleanerServiceTest.java69
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/component/ComponentUpdaterTest.java3
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/project/ws/CreateActionTest.java3
58 files changed, 3090 insertions, 48 deletions
diff --git a/build.gradle b/build.gradle
index 767692f9a90..b1c5a7ed5aa 100644
--- a/build.gradle
+++ b/build.gradle
@@ -295,6 +295,8 @@ subprojects {
dependency 'javax.xml.bind:jaxb-api:2.3.0'
dependency 'junit:junit:4.13'
dependency 'org.junit.jupiter:junit-jupiter-api:5.6.0'
+ dependency 'org.xmlunit:xmlunit-core:2.6.4'
+ dependency 'org.xmlunit:xmlunit-matchers:2.6.4'
dependency 'net.jpountz.lz4:lz4:1.3.0'
dependency 'net.lightbody.bmp:littleproxy:1.1.0-beta-bmp-17'
dependency 'org.awaitility:awaitility:4.0.2'
diff --git a/server/sonar-ce-common/src/main/java/org/sonar/ce/queue/CeTaskSubmit.java b/server/sonar-ce-common/src/main/java/org/sonar/ce/queue/CeTaskSubmit.java
index 71e7b38bf5f..9d7db56caf3 100644
--- a/server/sonar-ce-common/src/main/java/org/sonar/ce/queue/CeTaskSubmit.java
+++ b/server/sonar-ce-common/src/main/java/org/sonar/ce/queue/CeTaskSubmit.java
@@ -25,7 +25,9 @@ import java.util.Optional;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
+import org.sonar.db.component.BranchDto;
import org.sonar.db.component.ComponentDto;
+import org.sonar.db.project.ProjectDto;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Strings.emptyToNull;
@@ -128,6 +130,18 @@ public final class CeTaskSubmit {
return new Component(uuid, firstNonNull(dto.getMainBranchProjectUuid(), uuid));
}
+ public static Component fromDto(ProjectDto dto) {
+ return new Component(dto.getUuid(), dto.getUuid());
+ }
+
+ public static Component fromDto(BranchDto dto) {
+ return new Component(dto.getUuid(), dto.getProjectUuid());
+ }
+
+ public static Component fromProjectUuid(String projectUuid) {
+ return new Component(projectUuid, projectUuid);
+ }
+
public String getUuid() {
return uuid;
}
diff --git a/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java b/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java
index 4c705ec451d..3cb3fff44c5 100644
--- a/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java
+++ b/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java
@@ -39,6 +39,8 @@ public final class SqTables {
"alm_settings",
"alm_pats",
"analysis_properties",
+ "app_branch_project_branch",
+ "app_projects",
"ce_activity",
"ce_queue",
"ce_task_characteristics",
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java b/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java
index 205d02d18e2..a76ef07ffc6 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java
@@ -35,6 +35,7 @@ import org.sonar.db.ce.CeTaskCharacteristicDao;
import org.sonar.db.ce.CeTaskInputDao;
import org.sonar.db.ce.CeTaskMessageDao;
import org.sonar.db.component.AnalysisPropertiesDao;
+import org.sonar.db.component.ApplicationProjectsDao;
import org.sonar.db.component.BranchDao;
import org.sonar.db.component.ComponentDao;
import org.sonar.db.component.ComponentKeyUpdaterDao;
@@ -101,6 +102,7 @@ public class DaoModule extends Module {
ActiveRuleDao.class,
AnalysisPropertiesDao.class,
AuthorizationDao.class,
+ ApplicationProjectsDao.class,
BranchDao.class,
CeActivityDao.class,
CeQueueDao.class,
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java b/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java
index 78d717373ed..bd6c3c878d6 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java
@@ -33,6 +33,7 @@ import org.sonar.db.ce.CeTaskCharacteristicDao;
import org.sonar.db.ce.CeTaskInputDao;
import org.sonar.db.ce.CeTaskMessageDao;
import org.sonar.db.component.AnalysisPropertiesDao;
+import org.sonar.db.component.ApplicationProjectsDao;
import org.sonar.db.component.BranchDao;
import org.sonar.db.component.ComponentDao;
import org.sonar.db.component.ComponentKeyUpdaterDao;
@@ -166,6 +167,7 @@ public class DbClient {
private final SessionTokensDao sessionTokensDao;
private final SamlMessageIdDao samlMessageIdDao;
private final UserDismissedMessagesDao userDismissedMessagesDao;
+ private final ApplicationProjectsDao applicationProjectsDao;
public DbClient(Database database, MyBatis myBatis, DBSessions dbSessions, Dao... daos) {
this.database = database;
@@ -245,6 +247,7 @@ public class DbClient {
sessionTokensDao = getDao(map, SessionTokensDao.class);
samlMessageIdDao = getDao(map, SamlMessageIdDao.class);
userDismissedMessagesDao = getDao(map, UserDismissedMessagesDao.class);
+ applicationProjectsDao = getDao(map, ApplicationProjectsDao.class);
}
public DbSession openSession(boolean batch) {
@@ -267,6 +270,10 @@ public class DbClient {
return almPatDao;
}
+ public ApplicationProjectsDao applicationProjectsDao() {
+ return applicationProjectsDao;
+ }
+
public ProjectAlmSettingDao projectAlmSettingDao() {
return projectAlmSettingDao;
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java
index 8c1f4a3b487..77205277bb5 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java
@@ -47,6 +47,7 @@ import org.sonar.db.ce.CeTaskCharacteristicMapper;
import org.sonar.db.ce.CeTaskInputMapper;
import org.sonar.db.ce.CeTaskMessageMapper;
import org.sonar.db.component.AnalysisPropertiesMapper;
+import org.sonar.db.component.ApplicationProjectsMapper;
import org.sonar.db.component.BranchMapper;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentDtoWithSnapshotId;
@@ -231,6 +232,7 @@ public class MyBatis implements Startable {
AlmPatMapper.class,
AlmSettingMapper.class,
AnalysisPropertiesMapper.class,
+ ApplicationProjectsMapper.class,
AuthorizationMapper.class,
BranchMapper.class,
CeActivityMapper.class,
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ApplicationProjectsDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ApplicationProjectsDao.java
new file mode 100644
index 00000000000..713d67dd5dd
--- /dev/null
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ApplicationProjectsDao.java
@@ -0,0 +1,108 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.db.component;
+
+import java.util.Collection;
+import java.util.Set;
+import org.sonar.api.utils.System2;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.db.Dao;
+import org.sonar.db.DbSession;
+import org.sonar.db.project.ProjectDto;
+
+public class ApplicationProjectsDao implements Dao {
+ private final System2 system2;
+ private final UuidFactory uuidFactory;
+
+ public ApplicationProjectsDao(System2 system2, UuidFactory uuidFactory) {
+ this.system2 = system2;
+ this.uuidFactory = uuidFactory;
+ }
+
+ public void addProject(DbSession dbSession, String applicationUuid, String projectUuid) {
+ getMapper(dbSession).addProject(uuidFactory.create(), applicationUuid, projectUuid, system2.now());
+ }
+
+ public void removeApplicationProjectsByApplicationAndProject(DbSession dbSession, String applicationUuid, String projectUuid) {
+ getMapper(dbSession).removeApplicationBranchProjectBranchesByApplicationAndProject(applicationUuid, projectUuid);
+ getMapper(dbSession).removeApplicationProjectsByApplicationAndProject(applicationUuid, projectUuid);
+ }
+
+ public int countApplicationProjects(DbSession dbSession, String applicationUuid) {
+ return getMapper(dbSession).countApplicationProjects(applicationUuid);
+ }
+
+ public Set<ProjectDto> selectProjects(DbSession dbSession, String applicationUuid) {
+ return getMapper(dbSession).selectProjects(applicationUuid);
+ }
+
+ public void remove(DbSession dbSession, String applicationUuid) {
+ getMapper(dbSession).removeApplicationBranchProjectBranchesByApplication(applicationUuid);
+ getMapper(dbSession).removeApplicationProjectsByApplication(applicationUuid);
+ }
+
+ public void addProjectBranchToAppBranch(DbSession dbSession, BranchDto applicationBranch, BranchDto projectBranch) {
+ getMapper(dbSession).addProjectBranchToAppBranch(
+ uuidFactory.create(),
+ applicationBranch.getProjectUuid(),
+ applicationBranch.getUuid(),
+ projectBranch.getProjectUuid(),
+ projectBranch.getUuid(),
+ system2.now());
+ }
+
+ public void addProjectBranchToAppBranch(DbSession dbSession, String applicationUuid, String applicationBranchUuid, String projectUuid, String projectBranchUuid) {
+ getMapper(dbSession).addProjectBranchToAppBranch(
+ uuidFactory.create(),
+ applicationUuid,
+ applicationBranchUuid,
+ projectUuid,
+ projectBranchUuid,
+ system2.now());
+ }
+
+ public void removeProjectBranchFromAppBranch(DbSession dbSession, String applicationBranchUuid, String projectBranchUuid) {
+ getMapper(dbSession).removeProjectBranchFromAppBranch(applicationBranchUuid, projectBranchUuid);
+ }
+
+ public Set<BranchDto> selectProjectBranchesFromAppBranch(DbSession dbSession, String applicationBranchUuid) {
+ return getMapper(dbSession).selectProjectBranchesFromAppBranch(applicationBranchUuid);
+ }
+
+ public Set<ProjectDto> selectApplicationsFromProjectBranch(DbSession dbSession, String projectUuid, String branchKey) {
+ return getMapper(dbSession).selectApplicationsFromProjectBranch(projectUuid, branchKey);
+ }
+
+ public Set<ProjectDto> selectApplicationsFromProjects(DbSession dbSession, Collection<String> projectUuids) {
+ return getMapper(dbSession).selectApplicationsFromProjects(projectUuids);
+ }
+
+ private static ApplicationProjectsMapper getMapper(DbSession session) {
+ return session.getMapper(ApplicationProjectsMapper.class);
+ }
+
+ public void updateApplicationBranchName(DbSession dbSession, String applicationBranchUuid, String newName) {
+ getMapper(dbSession).updateApplicationBranchName(applicationBranchUuid, newName);
+ }
+
+ public void removeAllProjectBranchesOfAppBranch(DbSession dbSession, String applicationBranchUuid) {
+ getMapper(dbSession).removeAllProjectBranchesOfAppBranch(applicationBranchUuid);
+ }
+}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ApplicationProjectsMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ApplicationProjectsMapper.java
new file mode 100644
index 00000000000..a45cb6529bc
--- /dev/null
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ApplicationProjectsMapper.java
@@ -0,0 +1,69 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.db.component;
+
+import java.util.Collection;
+import java.util.Set;
+import org.apache.ibatis.annotations.Param;
+import org.sonar.db.project.ProjectDto;
+
+public interface ApplicationProjectsMapper {
+ void addProject(
+ @Param("uuid") String uuid,
+ @Param("applicationUuid") String applicationUuid,
+ @Param("projectUuid") String projectUuid,
+ @Param("now") long now);
+
+ void removeApplicationBranchProjectBranchesByApplicationAndProject(
+ @Param("applicationUuid") String applicationUuid,
+ @Param("projectUuid") String projectUuid);
+
+ void removeApplicationProjectsByApplicationAndProject(
+ @Param("applicationUuid") String applicationUuid,
+ @Param("projectUuid") String projectUuid);
+
+ Set<ProjectDto> selectProjects(@Param("applicationUuid") String applicationUuid);
+
+ void removeApplicationProjectsByApplication(String applicationUuid);
+
+ void removeApplicationBranchProjectBranchesByApplication(String applicationUuid);
+
+ void addProjectBranchToAppBranch(
+ @Param("uuid") String uuid,
+ @Param("applicationUuid") String applicationUuid,
+ @Param("applicationBranchUuid") String applicationBranchUuid,
+ @Param("projectUuid") String projectUuid,
+ @Param("projectBranchUuid") String projectBranchUuid,
+ @Param("now") long now);
+
+ void removeProjectBranchFromAppBranch(@Param("applicationBranchUuid") String applicationBranchUuid, @Param("projectBranchUuid") String projectBranchUuid);
+
+ Set<BranchDto> selectProjectBranchesFromAppBranch(@Param("applicationBranchUuid") String applicationBranchUuid);
+
+ int countApplicationProjects(@Param("applicationUuid") String applicationUuid);
+
+ void updateApplicationBranchName(@Param("uuid") String uuid, @Param("newName") String newName);
+
+ Set<ProjectDto> selectApplicationsFromProjectBranch(@Param("projectUuid") String projectUuid, @Param("branchKey") String branchKey);
+
+ Set<ProjectDto> selectApplicationsFromProjects(@Param("projectUuids") Collection<String> projectUuids);
+
+ void removeAllProjectBranchesOfAppBranch(@Param("applicationBranchUuid") String applicationBranchUuid);
+}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDao.java
index eea97147e49..3abeac13c01 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDao.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDao.java
@@ -20,7 +20,6 @@
package org.sonar.db.component;
import java.util.Collection;
-import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -30,6 +29,7 @@ import org.sonar.db.Dao;
import org.sonar.db.DbSession;
import org.sonar.db.project.ProjectDto;
+import static java.util.Collections.emptyList;
import static org.sonar.db.DatabaseUtils.executeLargeInputs;
public class BranchDao implements Dao {
@@ -68,7 +68,7 @@ public class BranchDao implements Dao {
public List<BranchDto> selectByBranchKeys(DbSession dbSession, Map<String, String> branchKeyByProjectUuid) {
if (branchKeyByProjectUuid.isEmpty()) {
- return Collections.emptyList();
+ return emptyList();
}
return mapper(dbSession).selectByBranchKeys(branchKeyByProjectUuid);
}
@@ -94,6 +94,9 @@ public class BranchDao implements Dao {
}
public List<BranchDto> selectByUuids(DbSession session, Collection<String> uuids) {
+ if (uuids.isEmpty()) {
+ return emptyList();
+ }
return executeLargeInputs(uuids, mapper(session)::selectByUuids);
}
@@ -103,7 +106,7 @@ public class BranchDao implements Dao {
public List<String> selectProjectUuidsWithIssuesNeedSync(DbSession session, Collection<String> uuids) {
if (uuids.isEmpty()) {
- return Collections.emptyList();
+ return emptyList();
}
return executeLargeInputs(uuids, mapper(session)::selectProjectUuidsWithIssuesNeedSync);
@@ -146,6 +149,10 @@ public class BranchDao implements Dao {
return mapper(dbSession).updateNeedIssueSync(branchUuid, needIssueSync, now);
}
+ public void deleteBranch(DbSession dbSession, String projectUuid, String branchKey) {
+ mapper(dbSession).deleteBranch(projectUuid, branchKey);
+ }
+
public boolean doAnyOfComponentsNeedIssueSync(DbSession session, List<String> components) {
if (!components.isEmpty()) {
List<Boolean> result = new LinkedList<>();
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchMapper.java
index c44507015fd..db9667426f7 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchMapper.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchMapper.java
@@ -68,4 +68,5 @@ public interface BranchMapper {
short doAnyOfComponentsNeedIssueSync(@Param("componentKeys") List<String> components);
+ void deleteBranch(@Param("projectUuid") String projectUuid, @Param("branchKey") String branchKey);
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/SelectionMode.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/SelectionMode.java
new file mode 100644
index 00000000000..6a636de8542
--- /dev/null
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/SelectionMode.java
@@ -0,0 +1,27 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.db.component;
+
+public enum SelectionMode {
+ MANUAL_MEASURE,
+ REGEXP,
+ TAGS,
+ REMAINING_PROJECTS
+}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/SnapshotDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/SnapshotDao.java
index f70760d6541..0f62ef98e7d 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/SnapshotDao.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/SnapshotDao.java
@@ -34,7 +34,6 @@ import org.sonar.db.Dao;
import org.sonar.db.DbSession;
import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.FluentIterable.from;
import static java.util.Objects.requireNonNull;
import static org.sonar.db.DatabaseUtils.executeLargeInputs;
@@ -125,9 +124,7 @@ public class SnapshotDao implements Dao {
*/
@CheckForNull
public ViewsSnapshotDto selectSnapshotBefore(String componentUuid, long date, DbSession dbSession) {
- return from(mapper(dbSession).selectSnapshotBefore(componentUuid, date))
- .first()
- .orNull();
+ return mapper(dbSession).selectSnapshotBefore(componentUuid, date).stream().findFirst().orElse(null);
}
private static SnapshotMapper mapper(DbSession session) {
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectDao.java
index 46dd8218e9a..8d2509cf449 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectDao.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectDao.java
@@ -50,6 +50,10 @@ public class ProjectDao implements Dao {
return Optional.ofNullable(mapper(session).selectProjectOrAppByKey(key));
}
+ public List<ProjectDto> selectAllApplications(DbSession session){
+ return mapper(session).selectAllApplications();
+ }
+
public List<ProjectDto> selectProjectsByKeys(DbSession session, Set<String> keys) {
if (keys.isEmpty()) {
return Collections.emptyList();
@@ -57,6 +61,13 @@ public class ProjectDao implements Dao {
return mapper(session).selectProjectsByKeys(keys);
}
+ public List<ProjectDto> selectApplicationsByKeys(DbSession session, Set<String> keys) {
+ if (keys.isEmpty()) {
+ return Collections.emptyList();
+ }
+ return mapper(session).selectApplicationsByKeys(keys);
+ }
+
public List<ProjectDto> selectProjects(DbSession session) {
return mapper(session).selectProjects();
}
@@ -99,4 +110,5 @@ public class ProjectDao implements Dao {
private static ProjectMapper mapper(DbSession session) {
return session.getMapper(ProjectMapper.class);
}
+
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectDto.java
index e01902c2eb5..656fe8e67ae 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectDto.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectDto.java
@@ -181,4 +181,5 @@ public class ProjectDto {
public int hashCode() {
return uuid != null ? uuid.hashCode() : 0;
}
+
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectMapper.java
index 01ff1c11e97..c2fafe6a8f3 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectMapper.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/project/ProjectMapper.java
@@ -57,4 +57,8 @@ public interface ProjectMapper {
List<ProjectDto> selectProjectsByOrganizationUuid(String organizationUuid);
void updateVisibility(@Param("uuid") String uuid, @Param("isPrivate") boolean isPrivate, @Param("updatedAt") long updatedAt);
+
+ List<ProjectDto> selectAllApplications();
+
+ List<ProjectDto> selectApplicationsByKeys(@Param("kees") Collection<String> kees);
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java
index b11c7febd7f..6ab82d82e1b 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java
@@ -49,7 +49,6 @@ class PurgeCommands {
this.system2 = system2;
}
- @VisibleForTesting
PurgeCommands(DbSession session, PurgeProfiler profiler, System2 system2) {
this(session, session.getMapper(PurgeMapper.class), profiler, system2);
}
@@ -406,6 +405,21 @@ class PurgeCommands {
profiler.stop();
}
+ void deleteApplicationProjects(String applicationUuid) {
+ profiler.start("deleteApplicationProjects (app_projects)");
+ purgeMapper.deleteApplicationBranchProjectBranchesByApplicationUuid(applicationUuid);
+ purgeMapper.deleteApplicationProjectsByApplicationUuid(applicationUuid);
+ session.commit();
+ profiler.stop();
+ }
+
+ void deleteApplicationBranchProjects(String applicationBranchUuid) {
+ profiler.start("deleteApplicationBranchProjects (app_branch_project_branch)");
+ purgeMapper.deleteApplicationBranchProjects(applicationBranchUuid);
+ session.commit();
+ profiler.stop();
+ }
+
public void deleteProjectAlmSettings(String rootUuid) {
profiler.start("deleteProjectAlmSettings (project_alm_settings)");
purgeMapper.deleteProjectAlmSettingsByProjectUuid(rootUuid);
@@ -415,6 +429,7 @@ class PurgeCommands {
void deleteBranch(String rootUuid) {
profiler.start("deleteBranch (project_branches)");
+ purgeMapper.deleteApplicationBranchProjectBranchesByProjectBranchUuid(rootUuid);
purgeMapper.deleteBranchByUuid(rootUuid);
session.commit();
profiler.stop();
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java
index bb39d91a389..3498ad1f49d 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java
@@ -221,6 +221,8 @@ public class PurgeDao implements Dao {
commands.deletePermissions(rootUuid);
commands.deleteNewCodePeriods(rootUuid);
commands.deleteBranch(rootUuid);
+ commands.deleteApplicationBranchProjects(rootUuid);
+ commands.deleteApplicationProjects(rootUuid);
commands.deleteComponents(rootUuid);
commands.deleteComponentsByMainBranchProjectUuid(rootUuid);
commands.deleteProject(rootUuid);
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java
index 840533516f9..57ca6f26a3e 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java
@@ -141,6 +141,14 @@ public interface PurgeMapper {
void deleteProjectMappingsByProjectUuid(@Param("projectUuid") String projectUuid);
+ void deleteApplicationProjectsByApplicationUuid(@Param("applicationUuid") String applicationUuid);
+
+ void deleteApplicationBranchProjectBranchesByApplicationUuid(@Param("applicationUuid") String applicationUuid);
+
+ void deleteApplicationBranchProjects(@Param("branchUuid") String applicationBranchUuid);
+
+ void deleteApplicationBranchProjectBranchesByProjectBranchUuid(@Param("projectBranchUuid") String projectBranchUuid);
+
void deleteBranchByUuid(@Param("uuid") String uuid);
void deleteLiveMeasuresByProjectUuid(@Param("projectUuid") String projectUuid);
diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ApplicationProjectsMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ApplicationProjectsMapper.xml
new file mode 100644
index 00000000000..9b7533340f0
--- /dev/null
+++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ApplicationProjectsMapper.xml
@@ -0,0 +1,196 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd">
+<mapper namespace="org.sonar.db.component.ApplicationProjectsMapper">
+
+ <sql id="appColumns">
+ app.uuid as uuid
+ </sql>
+
+ <sql id="branchColumns">
+ pb.uuid as uuid,
+ pb.project_uuid as projectUuid,
+ pb.kee as kee,
+ pb.branch_type as branchType,
+ pb.merge_branch_uuid as mergeBranchUuid,
+ pb.pull_request_binary as pullRequestBinary,
+ pb.exclude_from_purge as excludeFromPurge,
+ pb.need_issue_sync as needIssueSync
+ </sql>
+
+ <sql id="projectColumns">
+ p.uuid as uuid,
+ p.organization_uuid as organizationUuid,
+ p.kee as kee,
+ p.qualifier as qualifier,
+ p.name as name,
+ p.description as description,
+ p.tags as tagsString,
+ p.private as isPrivate,
+ p.created_at as createdAt,
+ p.updated_at as updatedAt
+ </sql>
+
+ <select id="selectProjects" parameterType="String" resultType="Project">
+ SELECT
+ <include refid="projectColumns"/>
+ FROM
+ app_projects appProj
+ INNER JOIN
+ projects p
+ on
+ p.uuid = appProj.project_uuid
+ WHERE
+ appProj.application_uuid=#{applicationUuid,jdbcType=VARCHAR}
+ </select>
+
+ <update id="addProject" parameterType="map">
+ INSERT INTO app_projects (
+ uuid,
+ application_uuid,
+ project_uuid,
+ created_at
+ )
+ VALUES (
+ #{uuid,jdbcType=VARCHAR},
+ #{applicationUuid,jdbcType=VARCHAR},
+ #{projectUuid,jdbcType=VARCHAR},
+ #{now,jdbcType=BIGINT}
+ )
+ </update>
+
+ <delete id="removeApplicationBranchProjectBranchesByApplicationAndProject" parameterType="map">
+ DELETE FROM
+ app_branch_project_branch
+ WHERE
+ app_branch_project_branch.application_uuid = #{applicationUuid,jdbcType=VARCHAR}
+ AND
+ app_branch_project_branch.project_uuid=#{projectUuid,jdbcType=VARCHAR}
+ </delete>
+
+ <delete id="removeApplicationProjectsByApplicationAndProject" parameterType="map">
+ DELETE FROM
+ app_projects
+ WHERE
+ application_uuid=#{applicationUuid,jdbcType=VARCHAR}
+ AND
+ project_uuid=#{projectUuid,jdbcType=VARCHAR}
+ </delete>
+
+ <delete id="removeApplicationBranchProjectBranchesByApplication" parameterType="String">
+ DELETE FROM
+ app_branch_project_branch
+ where
+ application_uuid=#{applicationUuid,jdbcType=VARCHAR}
+ </delete>
+
+ <delete id="removeApplicationProjectsByApplication" parameterType="String">
+ DELETE FROM
+ app_projects
+ WHERE
+ application_uuid=#{applicationUuid,jdbcType=VARCHAR}
+ </delete>
+
+ <insert id="addProjectBranchToAppBranch" parameterType="map">
+ INSERT INTO app_branch_project_branch (
+ uuid,
+ application_uuid,
+ application_branch_uuid,
+ project_uuid,
+ project_branch_uuid,
+ created_at
+ )
+ VALUES (
+ #{uuid,jdbcType=VARCHAR},
+ #{applicationUuid,jdbcType=VARCHAR},
+ #{applicationBranchUuid,jdbcType=VARCHAR},
+ #{projectUuid,jdbcType=VARCHAR},
+ #{projectBranchUuid,jdbcType=VARCHAR},
+ #{now,jdbcType=BIGINT}
+ )
+ </insert>
+
+ <delete id="removeProjectBranchFromAppBranch" parameterType="map">
+ DELETE FROM
+ app_branch_project_branch
+ WHERE
+ app_branch_project_branch.application_branch_uuid = #{applicationBranchUuid,jdbcType=VARCHAR}
+ AND
+ app_branch_project_branch.project_branch_uuid = #{projectBranchUuid,jdbcType=VARCHAR}
+ </delete>
+
+ <select id="selectProjectBranchesFromAppBranch" parameterType="String" resultType="org.sonar.db.component.BranchDto">
+ SELECT
+ <include refid="branchColumns"/>
+ FROM
+ app_branch_project_branch
+ INNER JOIN
+ project_branches pb
+ ON
+ app_branch_project_branch.project_branch_uuid = pb.uuid
+ WHERE
+ app_branch_project_branch.application_branch_uuid = #{applicationBranchUuid,jdbcType=VARCHAR}
+ </select>
+
+ <select id="countApplicationProjects" parameterType="String" resultType="int">
+ select
+ count(1)
+ FROM
+ app_projects ap
+ WHERE
+ ap.application_uuid = #{applicationUuid,jdbcType=VARCHAR}
+ </select>
+
+ <update id="updateApplicationBranchName" parameterType="String">
+ UPDATE
+ project_branches
+ SET
+ kee = #{newName,jdbcType=VARCHAR}
+ WHERE
+ uuid = #{uuid,jdbcType=VARCHAR}
+ </update>
+
+ <select id="selectApplicationsFromProjectBranch" parameterType="String" resultType="Project">
+ SELECT
+ <include refid="projectColumns"/>
+ FROM
+ projects p
+ WHERE p.uuid IN (
+ SELECT
+ abp.application_uuid
+ FROM
+ app_branch_project_branch abp
+ INNER JOIN
+ project_branches pb
+ ON
+ abp.project_branch_uuid = pb.uuid
+ WHERE
+ pb.kee = #{branchKey,jdbcType=VARCHAR}
+ AND
+ abp.project_uuid = #{projectUuid,jdbcType=VARCHAR}
+ )
+ </select>
+
+ <select id="selectApplicationsFromProjects" resultType="Project">
+ SELECT
+ <include refid="projectColumns"/>
+ FROM
+ app_projects ap
+ INNER JOIN
+ projects p
+ ON
+ ap.application_uuid = p.uuid
+ WHERE
+ ap.project_uuid in
+ <foreach collection="projectUuids" open="(" close=")" item="uuid" separator=",">
+ #{uuid,jdbcType=VARCHAR}
+ </foreach>
+ </select>
+
+ <delete id="removeAllProjectBranchesOfAppBranch" parameterType="String">
+ DELETE FROM
+ app_branch_project_branch
+ WHERE
+ application_branch_uuid=#{applicationBranchUuid,jdbcType=VARCHAR}
+ </delete>
+
+</mapper>
diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/BranchMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/BranchMapper.xml
index dccb832be36..8cdc4b70ddc 100644
--- a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/BranchMapper.xml
+++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/BranchMapper.xml
@@ -48,6 +48,13 @@
uuid = #{projectUuid, jdbcType=VARCHAR}
</update>
+ <delete id="deleteBranch" parameterType="string">
+ delete from project_branches
+ where
+ project_uuid = #{projectUuid, jdbcType=VARCHAR},
+ kee = #{branchKey, jdbcType=VARCHAR}
+ </delete>
+
<update id="updateExcludeFromPurge">
update project_branches
set
diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/project/ProjectMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/project/ProjectMapper.xml
index d3903f6567a..dfc9c9d51fc 100644
--- a/server/sonar-db-dao/src/main/resources/org/sonar/db/project/ProjectMapper.xml
+++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/project/ProjectMapper.xml
@@ -46,6 +46,18 @@
</foreach>
</select>
+ <select id="selectApplicationsByKeys" resultType="Project">
+ select
+ <include refid="projectColumns"/>
+ from projects p
+ where
+ p.qualifier='APP' and
+ p.kee in
+ <foreach collection="kees" open="(" close=")" item="k" separator=",">
+ #{k,jdbcType=VARCHAR}
+ </foreach>
+ </select>
+
<select id="selectProjects" resultType="Project">
select
<include refid="projectColumns"/>
@@ -62,7 +74,7 @@
p.organization_uuid=#{organizationUuid,jdbcType=VARCHAR}
</select>
- <select id="selectProjectsByOrganizationUuid" parameterType="String" resultType="Project">
+ <select id="selectProjectsByOrganizationUuid" parameterType="String" resultType="Project">
select
<include refid="projectColumns"/>
from projects p
@@ -71,6 +83,14 @@
p.organization_uuid=#{organizationUuid,jdbcType=VARCHAR}
</select>
+ <select id="selectAllApplications" resultType="Project">
+ select
+ <include refid="projectColumns"/>
+ from projects p
+ where
+ p.qualifier='APP'
+ </select>
+
<select id="selectProjectByKey" parameterType="String" resultType="Project">
SELECT
<include refid="projectColumns"/>
diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml
index e4b71806473..828a957dae3 100644
--- a/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml
+++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml
@@ -329,6 +329,33 @@
</foreach>
</delete>
+ <delete id="deleteApplicationProjectsByApplicationUuid" parameterType="map">
+ DELETE
+ FROM app_projects
+ where
+ application_uuid=#{applicationUuid,jdbcType=VARCHAR}
+ </delete>
+
+ <delete id="deleteApplicationBranchProjectBranchesByApplicationUuid" parameterType="map">
+ DELETE
+ FROM app_branch_project_branch
+ where
+ application_uuid=#{applicationUuid,jdbcType=VARCHAR}
+ </delete>
+
+ <delete id="deleteApplicationBranchProjects" parameterType="String">
+ DELETE
+ FROM app_branch_project_branch
+ where
+ application_branch_uuid=#{branchUuid,jdbcType=VARCHAR}
+ </delete>
+
+ <delete id="deleteApplicationBranchProjectBranchesByProjectBranchUuid" parameterType="String">
+ DELETE
+ FROM app_branch_project_branch
+ where project_branch_uuid=#{projectBranchUuid,jdbcType=VARCHAR}
+ </delete>
+
<delete id="deleteIssueChangesFromIssueKeys" parameterType="map">
DELETE FROM issue_changes
WHERE issue_key IN
diff --git a/server/sonar-db-dao/src/schema/schema-sq.ddl b/server/sonar-db-dao/src/schema/schema-sq.ddl
index eba030939d8..e4effd33c0b 100644
--- a/server/sonar-db-dao/src/schema/schema-sq.ddl
+++ b/server/sonar-db-dao/src/schema/schema-sq.ddl
@@ -89,6 +89,32 @@ CREATE TABLE "ANALYSIS_PROPERTIES"(
ALTER TABLE "ANALYSIS_PROPERTIES" ADD CONSTRAINT "PK_ANALYSIS_PROPERTIES" PRIMARY KEY("UUID");
CREATE INDEX "ANALYSIS_PROPERTIES_ANALYSIS" ON "ANALYSIS_PROPERTIES"("ANALYSIS_UUID");
+CREATE TABLE "APP_BRANCH_PROJECT_BRANCH"(
+ "UUID" VARCHAR(40) NOT NULL,
+ "APPLICATION_UUID" VARCHAR(40) NOT NULL,
+ "APPLICATION_BRANCH_UUID" VARCHAR(40) NOT NULL,
+ "PROJECT_UUID" VARCHAR(40) NOT NULL,
+ "PROJECT_BRANCH_UUID" VARCHAR(40) NOT NULL,
+ "CREATED_AT" BIGINT NOT NULL
+);
+ALTER TABLE "APP_BRANCH_PROJECT_BRANCH" ADD CONSTRAINT "PK_APP_BRANCH_PROJECT_BRANCH" PRIMARY KEY("UUID");
+CREATE UNIQUE INDEX "UNIQ_APP_BRANCH_PROJ" ON "APP_BRANCH_PROJECT_BRANCH"("APPLICATION_BRANCH_UUID", "PROJECT_BRANCH_UUID");
+CREATE INDEX "IDX_ABPB_APP_UUID" ON "APP_BRANCH_PROJECT_BRANCH"("APPLICATION_UUID");
+CREATE INDEX "IDX_ABPB_APP_BRANCH_UUID" ON "APP_BRANCH_PROJECT_BRANCH"("APPLICATION_BRANCH_UUID");
+CREATE INDEX "IDX_ABPB_PROJ_UUID" ON "APP_BRANCH_PROJECT_BRANCH"("PROJECT_UUID");
+CREATE INDEX "IDX_ABPB_PROJ_BRANCH_UUID" ON "APP_BRANCH_PROJECT_BRANCH"("PROJECT_BRANCH_UUID");
+
+CREATE TABLE "APP_PROJECTS"(
+ "UUID" VARCHAR(40) NOT NULL,
+ "APPLICATION_UUID" VARCHAR(40) NOT NULL,
+ "PROJECT_UUID" VARCHAR(40) NOT NULL,
+ "CREATED_AT" BIGINT NOT NULL
+);
+ALTER TABLE "APP_PROJECTS" ADD CONSTRAINT "PK_APP_PROJECTS" PRIMARY KEY("UUID");
+CREATE UNIQUE INDEX "UNIQ_APP_PROJECTS" ON "APP_PROJECTS"("APPLICATION_UUID", "PROJECT_UUID");
+CREATE INDEX "IDX_APP_PROJ_APPLICATION_UUID" ON "APP_PROJECTS"("APPLICATION_UUID");
+CREATE INDEX "IDX_APP_PROJ_PROJECT_UUID" ON "APP_PROJECTS"("PROJECT_UUID");
+
CREATE TABLE "CE_ACTIVITY"(
"UUID" VARCHAR(40) NOT NULL,
"TASK_TYPE" VARCHAR(15) NOT NULL,
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ApplicationProjectsDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ApplicationProjectsDaoTest.java
new file mode 100644
index 00000000000..8ba95953066
--- /dev/null
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ApplicationProjectsDaoTest.java
@@ -0,0 +1,157 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.db.component;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.impl.utils.TestSystem2;
+import org.sonar.api.utils.System2;
+import org.sonar.core.util.UuidFactoryFast;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.project.ProjectDto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ApplicationProjectsDaoTest {
+ @Rule
+ public DbTester db = DbTester.create(System2.INSTANCE);
+
+ private UuidFactoryFast uuids = UuidFactoryFast.getInstance();
+ private TestSystem2 system2 = new TestSystem2();
+ private DbSession dbSession = db.getSession();
+ private ApplicationProjectsDao underTest = new ApplicationProjectsDao(system2, uuids);
+
+ @Before
+ public void before() {
+ system2.setNow(1000L);
+ }
+
+ @Test
+ public void select_projects() {
+ insertApplicationProject("uuid2", "p1");
+ insertApplicationProject("uuid2", "p2");
+
+ assertThat(underTest.selectProjects(dbSession, "uuid")).isEmpty();
+ assertThat(underTest.selectProjects(dbSession, "uuid2")).extracting(ProjectDto::getUuid).containsOnly("p1", "p2");
+ }
+
+ @Test
+ public void select_projects_from_non_existing_app_is_empty() {
+ insertApplicationProject("uuid", "p1");
+ assertThat(underTest.selectProjects(dbSession, "does_not_exist")).isEmpty();
+ }
+
+ @Test
+ public void add_project() {
+ insertProject("p1");
+ underTest.addProject(dbSession, "uuid", "p1");
+ assertThat(underTest.selectProjects(dbSession, "uuid")).extracting(ProjectDto::getUuid).containsOnly("p1");
+ }
+
+ @Test
+ public void add_project_branch_to_application_branch() {
+ insertProject("p1");
+ insertBranch("p1", "b1");
+ insertApplication("app1");
+ insertBranch("app1", "app-b1");
+ underTest.addProjectBranchToAppBranch(dbSession, "app1", "app-b1", "p1", "b1");
+ assertThat(underTest.selectProjectBranchesFromAppBranch(dbSession, "app-b1")).extracting(BranchDto::getUuid).containsOnly("b1");
+ }
+
+ @Test
+ public void remove_project() {
+ insertApplicationProject("uuid", "p1");
+ insertApplicationProject("uuid", "p2");
+ assertThat(underTest.selectProjects(dbSession, "uuid")).extracting(ProjectDto::getUuid).contains("p1");
+ underTest.removeApplicationProjectsByApplicationAndProject(dbSession, "uuid", "p1");
+ assertThat(underTest.selectProjects(dbSession, "uuid")).extracting(ProjectDto::getUuid).containsOnly("p2");
+ }
+
+ @Test
+ public void remove_project_from_non_existing_app_is_no_op() {
+ insertApplicationProject("uuid", "p1");
+ underTest.removeApplicationProjectsByApplicationAndProject(dbSession, "non_existing", "p1");
+ assertThat(underTest.selectProjects(dbSession, "uuid")).extracting(ProjectDto::getUuid).containsOnly("p1");
+ }
+
+ @Test
+ public void remove_non_existing_project_from_app_is_no_op() {
+ insertApplicationProject("uuid", "p1");
+ underTest.removeApplicationProjectsByApplicationAndProject(dbSession, "uuid", "non_existing");
+ assertThat(underTest.selectProjects(dbSession, "uuid")).extracting(ProjectDto::getUuid).containsOnly("p1");
+ }
+
+ @Test
+ public void remove() {
+ insertApplicationProject("uuid", "p1");
+ insertApplicationProject("uuid", "p2");
+
+ underTest.remove(dbSession, "uuid");
+ assertThat(underTest.selectProjects(dbSession, "uuid")).isEmpty();
+ }
+
+ private String insertApplicationProject(String applicationUuid, String projectUuid) {
+ String uuid = uuids.create();
+ db.executeInsert(
+ "app_projects",
+ "uuid", uuid,
+ "application_uuid", applicationUuid,
+ "project_uuid", projectUuid,
+ "created_at", 1000L);
+ insertProject(projectUuid);
+ return uuid;
+ }
+
+ private void insertProject(String projectUuid) {
+ db.executeInsert("projects",
+ "uuid", projectUuid,
+ "kee", projectUuid,
+ "qualifier", "TRK",
+ "ORGANIZATION_UUID", "ORGANIZATION_UUID",
+ "private", true,
+ "updated_at", 1000L,
+ "created_at", 1000L);
+ }
+
+ private void insertApplication(String appUuid) {
+ db.executeInsert("projects",
+ "uuid", appUuid,
+ "kee", appUuid,
+ "qualifier", "APP",
+ "ORGANIZATION_UUID", "ORGANIZATION_UUID",
+ "private", true,
+ "updated_at", 1000L,
+ "created_at", 1000L);
+ }
+
+ private void insertBranch(String projectUuid, String branchKey) {
+ db.executeInsert("project_branches",
+ "uuid", branchKey,
+ "branch_type", "BRANCH",
+ "project_uuid", projectUuid,
+ "kee", branchKey,
+ "NEED_ISSUE_SYNC", true,
+ "updated_at", 1000L,
+ "created_at", 1000L);
+ }
+
+}
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/measure/ProjectMeasuresIndexerIteratorTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/measure/ProjectMeasuresIndexerIteratorTest.java
index 1dbdd3aa301..28117efbf7d 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/measure/ProjectMeasuresIndexerIteratorTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/measure/ProjectMeasuresIndexerIteratorTest.java
@@ -86,9 +86,7 @@ public class ProjectMeasuresIndexerIteratorTest {
@Test
public void return_application_measure() {
- OrganizationDto organization = dbTester.organizations().insert();
- ComponentDto project = dbTester.components().insertPrivateApplication(organization,
- c -> c.setDbKey("App-Key").setName("App Name"));
+ ComponentDto project = dbTester.components().insertPrivateApplication(c -> c.setDbKey("App-Key").setName("App Name"));
SnapshotDto analysis = dbTester.components().insertSnapshot(project);
MetricDto metric1 = dbTester.measures().insertMetric(m -> m.setValueType(INT.name()).setKey("ncloc"));
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java
index cf3dcdb411d..db459e897fd 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java
@@ -536,6 +536,84 @@ public class PurgeDaoTest {
}
@Test
+ public void delete_application() {
+ MetricDto metric = db.measures().insertMetric();
+ ComponentDto project = db.components().insertPrivateProject();
+ BranchDto projectBranch = db.getDbClient().branchDao().selectByUuid(db.getSession(), project.uuid()).get();
+ RuleDefinitionDto rule = db.rules().insert();
+
+ ComponentDto app = db.components().insertPrivateApplication();
+ ComponentDto appBranch = db.components().insertProjectBranch(app);
+ ComponentDto otherApp = db.components().insertPrivateApplication();
+ ComponentDto otherAppBranch = db.components().insertProjectBranch(otherApp);
+
+ SnapshotDto appAnalysis = db.components().insertSnapshot(app);
+ SnapshotDto appBranchAnalysis = db.components().insertSnapshot(appBranch);
+ SnapshotDto otherAppAnalysis = db.components().insertSnapshot(otherApp);
+ SnapshotDto otherAppBranchAnalysis = db.components().insertSnapshot(otherAppBranch);
+
+ MeasureDto appMeasure = db.measures().insertMeasure(app, appAnalysis, metric);
+ MeasureDto appBranchMeasure = db.measures().insertMeasure(appBranch, appBranchAnalysis, metric);
+ MeasureDto otherAppMeasure = db.measures().insertMeasure(otherApp, otherAppAnalysis, metric);
+ MeasureDto otherAppBranchMeasure = db.measures().insertMeasure(otherAppBranch, otherAppBranchAnalysis, metric);
+
+ db.components().addApplicationProject(app, project);
+ db.components().addApplicationProject(otherApp, project);
+ db.components().addProjectBranchToApplicationBranch(dbClient.branchDao().selectByUuid(dbSession, appBranch.uuid()).get(), projectBranch);
+ db.components().addProjectBranchToApplicationBranch(dbClient.branchDao().selectByUuid(dbSession, otherAppBranch.uuid()).get(), projectBranch);
+
+ underTest.deleteProject(dbSession, app.uuid());
+ dbSession.commit();
+
+ assertThat(uuidsIn("components")).containsOnly(project.uuid(), otherApp.uuid(), otherAppBranch.uuid());
+ assertThat(uuidsIn("projects")).containsOnly(project.uuid(), otherApp.uuid());
+ assertThat(uuidsIn("snapshots")).containsOnly(otherAppAnalysis.getUuid(), otherAppBranchAnalysis.getUuid());
+ assertThat(uuidsIn("project_branches")).containsOnly(project.uuid(), otherApp.uuid(), otherAppBranch.uuid());
+ assertThat(uuidsIn("project_measures")).containsOnly(otherAppMeasure.getUuid(), otherAppBranchMeasure.getUuid());
+ assertThat(uuidsIn("app_projects", "application_uuid")).containsOnly(otherApp.uuid());
+ assertThat(uuidsIn("app_branch_project_branch", "application_branch_uuid")).containsOnly(otherAppBranch.uuid());
+ }
+
+ @Test
+ public void delete_application_branch() {
+ MetricDto metric = db.measures().insertMetric();
+ ComponentDto project = db.components().insertPrivateProject();
+ BranchDto projectBranch = db.getDbClient().branchDao().selectByUuid(db.getSession(), project.uuid()).get();
+ RuleDefinitionDto rule = db.rules().insert();
+
+ ComponentDto app = db.components().insertPrivateApplication();
+ ComponentDto appBranch = db.components().insertProjectBranch(app);
+ ComponentDto otherApp = db.components().insertPrivateApplication();
+ ComponentDto otherAppBranch = db.components().insertProjectBranch(otherApp);
+
+ SnapshotDto appAnalysis = db.components().insertSnapshot(app);
+ SnapshotDto appBranchAnalysis = db.components().insertSnapshot(appBranch);
+ SnapshotDto otherAppAnalysis = db.components().insertSnapshot(otherApp);
+ SnapshotDto otherAppBranchAnalysis = db.components().insertSnapshot(otherAppBranch);
+
+ MeasureDto appMeasure = db.measures().insertMeasure(app, appAnalysis, metric);
+ MeasureDto appBranchMeasure = db.measures().insertMeasure(appBranch, appBranchAnalysis, metric);
+ MeasureDto otherAppMeasure = db.measures().insertMeasure(otherApp, otherAppAnalysis, metric);
+ MeasureDto otherAppBranchMeasure = db.measures().insertMeasure(otherAppBranch, otherAppBranchAnalysis, metric);
+
+ db.components().addApplicationProject(app, project);
+ db.components().addApplicationProject(otherApp, project);
+ db.components().addProjectBranchToApplicationBranch(dbClient.branchDao().selectByUuid(dbSession, appBranch.uuid()).get(), projectBranch);
+ db.components().addProjectBranchToApplicationBranch(dbClient.branchDao().selectByUuid(dbSession, otherAppBranch.uuid()).get(), projectBranch);
+
+ underTest.deleteBranch(dbSession, appBranch.uuid());
+ dbSession.commit();
+
+ assertThat(uuidsIn("components")).containsOnly(project.uuid(), app.uuid(), otherApp.uuid(), otherAppBranch.uuid());
+ assertThat(uuidsIn("projects")).containsOnly(project.uuid(), app.uuid(), otherApp.uuid());
+ assertThat(uuidsIn("snapshots")).containsOnly(otherAppAnalysis.getUuid(), appAnalysis.getUuid(), otherAppBranchAnalysis.getUuid());
+ assertThat(uuidsIn("project_branches")).containsOnly(project.uuid(), app.uuid(), otherApp.uuid(), otherAppBranch.uuid());
+ assertThat(uuidsIn("project_measures")).containsOnly(appMeasure.getUuid(), otherAppMeasure.getUuid(), otherAppBranchMeasure.getUuid());
+ assertThat(uuidsIn("app_projects", "application_uuid")).containsOnly(app.uuid(), otherApp.uuid());
+ assertThat(uuidsIn("app_branch_project_branch", "application_branch_uuid")).containsOnly(otherAppBranch.uuid());
+ }
+
+ @Test
public void delete_webhooks_from_project() {
OrganizationDto organization = db.organizations().insert();
ProjectDto project1 = db.components().insertPrivateProjectDto(organization);
diff --git a/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/component/ComponentDbTester.java b/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/component/ComponentDbTester.java
index b46a7eb5e3a..2ee5805eb4c 100644
--- a/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/component/ComponentDbTester.java
+++ b/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/component/ComponentDbTester.java
@@ -149,7 +149,6 @@ public class ComponentDbTester {
return getProjectDto(componentDto);
}
-
public final ProjectDto insertPublicProjectDto(OrganizationDto organization, Consumer<ComponentDto> dtoPopulator) {
ComponentDto componentDto = insertPublicProject(organization, dtoPopulator);
return getProjectDto(componentDto);
@@ -186,6 +185,11 @@ public class ComponentDbTester {
return insertComponentAndBranchAndProject(ComponentTesting.newPrivateProjectDto(organizationDto, uuid), true, defaults(), dtoPopulator);
}
+
+ public final ComponentDto insertPrivateProjectWithCustomBranch(String branchKey) {
+ return insertPrivateProjectWithCustomBranch(db.getDefaultOrganization(), b -> b.setBranchType(BRANCH).setKey(branchKey), defaults());
+ }
+
public final ComponentDto insertPrivateProjectWithCustomBranch(OrganizationDto organizationDto, Consumer<BranchDto> branchPopulator) {
return insertPrivateProjectWithCustomBranch(organizationDto, branchPopulator, defaults());
}
@@ -279,13 +283,37 @@ public class ComponentDbTester {
public final ComponentDto insertPrivateApplication() {
return insertPrivateApplication(db.getDefaultOrganization());
}
-
- public final ComponentDto insertPrivateApplication(OrganizationDto organization) {
- return insertPrivateApplication(organization, defaults());
+
+ public final ProjectDto insertPrivateApplicationDto() {
+ return getProjectDto(insertPrivateApplication(db.getDefaultOrganization()));
+ }
+
+ public final ProjectDto insertPublicApplicationDto() {
+ return getProjectDto(insertPublicApplication(db.getDefaultOrganization()));
+ }
+
+ public final ProjectDto insertPrivateApplicationDto(Consumer<ComponentDto> dtoPopulator) {
+ return getProjectDto(insertPrivateApplication(db.getDefaultOrganization(), dtoPopulator, defaults()));
+ }
+
+ public final ProjectDto insertPrivateApplicationDto(Consumer<ComponentDto> dtoPopulator, Consumer<ProjectDto> appPopulator) {
+ return getProjectDto(insertPrivateApplication(db.getDefaultOrganization(), dtoPopulator, appPopulator));
+ }
+
+ public final ComponentDto insertPrivateApplication(Consumer<ComponentDto> dtoPopulator) {
+ return insertPrivateApplication(db.getDefaultOrganization(), dtoPopulator, defaults());
}
public final ComponentDto insertPrivateApplication(OrganizationDto organization, Consumer<ComponentDto> dtoPopulator) {
- return insertComponentAndBranchAndProject(ComponentTesting.newApplication(organization).setPrivate(true), true, defaults(), dtoPopulator);
+ return insertPrivateApplication(db.getDefaultOrganization(), dtoPopulator, defaults());
+ }
+
+ public final ComponentDto insertPrivateApplication(OrganizationDto organization) {
+ return insertPrivateApplication(organization, defaults(), defaults());
+ }
+
+ public final ComponentDto insertPrivateApplication(OrganizationDto organization, Consumer<ComponentDto> dtoPopulator, Consumer<ProjectDto> projectPopulator) {
+ return insertComponentAndBranchAndProject(ComponentTesting.newApplication(organization).setPrivate(true), true, defaults(), dtoPopulator, projectPopulator);
}
public final ComponentDto insertSubView(ComponentDto view) {
@@ -313,6 +341,27 @@ public class ComponentDbTester {
return component;
}
+ public void addApplicationProject(ComponentDto application, ComponentDto... projects) {
+ for(ComponentDto project : projects){
+ dbClient.applicationProjectsDao().addProject(dbSession, application.uuid(), project.uuid());
+ }
+ db.commit();
+ }
+
+ public void addApplicationProject(ProjectDto application, ProjectDto... projects) {
+ for(ProjectDto project : projects){
+ dbClient.applicationProjectsDao().addProject(dbSession, application.getUuid(), project.getUuid());
+ }
+ db.commit();
+ }
+
+ public void addProjectBranchToApplicationBranch(BranchDto applicationBranch, BranchDto... projectBranches) {
+ for(BranchDto projectBranch : projectBranches){
+ dbClient.applicationProjectsDao().addProjectBranchToAppBranch(dbSession, applicationBranch, projectBranch);
+ }
+ db.commit();
+ }
+
private ComponentDto insertComponentAndBranchAndProject(ComponentDto component, @Nullable Boolean isPrivate, Consumer<BranchDto> branchPopulator,
Consumer<ComponentDto> componentDtoPopulator) {
return insertComponentAndBranchAndProject(component, isPrivate, branchPopulator, componentDtoPopulator, defaults());
@@ -421,7 +470,7 @@ public class ComponentDbTester {
}
// TODO temporary constructor to quickly create project from previous project component.
- private ProjectDto toProjectDto(ComponentDto componentDto, long createTime) {
+ public static ProjectDto toProjectDto(ComponentDto componentDto, long createTime) {
return new ProjectDto()
.setUuid(componentDto.uuid())
.setKey(componentDto.getDbKey())
diff --git a/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/component/ComponentTesting.java b/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/component/ComponentTesting.java
index a5e4a1cc1d5..eaf1028f7f1 100644
--- a/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/component/ComponentTesting.java
+++ b/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/component/ComponentTesting.java
@@ -286,7 +286,7 @@ public class ComponentTesting {
.setLongName(project.getName())
.setDescription(project.getDescription())
.setScope(Scopes.PROJECT)
- .setQualifier(Qualifiers.PROJECT)
+ .setQualifier(project.getQualifier())
.setPath(null)
.setLanguage(null)
.setEnabled(true)
diff --git a/server/sonar-db-migration/build.gradle b/server/sonar-db-migration/build.gradle
index de715638b1e..ebdb837087e 100644
--- a/server/sonar-db-migration/build.gradle
+++ b/server/sonar-db-migration/build.gradle
@@ -25,6 +25,8 @@ dependencies {
testCompile 'org.assertj:assertj-core'
testCompile 'org.mindrot:jbcrypt'
testCompile 'org.mockito:mockito-core'
+ testCompile 'org.xmlunit:xmlunit-core'
+ testCompile 'org.xmlunit:xmlunit-matchers'
testCompile project(':sonar-scanner-protocol')
testCompile project(':sonar-testing-harness')
testCompile testFixtures(project(':server:sonar-db-core'))
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/AddIndexToApplicationBranchProjs.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/AddIndexToApplicationBranchProjs.java
new file mode 100644
index 00000000000..621ef6bf9e5
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/AddIndexToApplicationBranchProjs.java
@@ -0,0 +1,68 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.v86;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+public class AddIndexToApplicationBranchProjs extends DdlChange {
+ private static final String TABLE = "app_branch_project_branch";
+
+ public AddIndexToApplicationBranchProjs(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(new CreateIndexBuilder()
+ .setTable(TABLE)
+ .setName("uniq_app_branch_proj")
+ .addColumn("application_branch_uuid")
+ .addColumn("project_branch_uuid")
+ .setUnique(true)
+ .build());
+
+ context.execute(new CreateIndexBuilder()
+ .setTable(TABLE)
+ .setName("idx_abpb_app_uuid")
+ .addColumn("application_uuid")
+ .build());
+
+ context.execute(new CreateIndexBuilder()
+ .setTable(TABLE)
+ .setName("idx_abpb_app_branch_uuid")
+ .addColumn("application_branch_uuid")
+ .build());
+
+ context.execute(new CreateIndexBuilder()
+ .setTable(TABLE)
+ .setName("idx_abpb_proj_uuid")
+ .addColumn("project_uuid")
+ .build());
+
+ context.execute(new CreateIndexBuilder()
+ .setTable(TABLE)
+ .setName("idx_abpb_proj_branch_uuid")
+ .addColumn("project_branch_uuid")
+ .build());
+ }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/AddIndexToApplicationProjects.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/AddIndexToApplicationProjects.java
new file mode 100644
index 00000000000..d047c67544e
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/AddIndexToApplicationProjects.java
@@ -0,0 +1,56 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.v86;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+public class AddIndexToApplicationProjects extends DdlChange {
+ private static final String TABLE = "app_projects";
+
+ public AddIndexToApplicationProjects(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(new CreateIndexBuilder()
+ .setTable(TABLE)
+ .setName("uniq_app_projects")
+ .addColumn("application_uuid")
+ .addColumn("project_uuid")
+ .setUnique(true)
+ .build());
+
+ context.execute(new CreateIndexBuilder()
+ .setTable(TABLE)
+ .setName("idx_app_proj_application_uuid")
+ .addColumn("application_uuid")
+ .build());
+
+ context.execute(new CreateIndexBuilder()
+ .setTable(TABLE)
+ .setName("idx_app_proj_project_uuid")
+ .addColumn("project_uuid")
+ .build());
+ }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/AddPkToApplicationBranchProjs.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/AddPkToApplicationBranchProjs.java
new file mode 100644
index 00000000000..615b03f4144
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/AddPkToApplicationBranchProjs.java
@@ -0,0 +1,38 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.v86;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+import org.sonar.server.platform.db.migration.version.v84.util.AddPrimaryKeyBuilder;
+
+public class AddPkToApplicationBranchProjs extends DdlChange {
+ private static final String TABLE = "app_branch_project_branch";
+
+ public AddPkToApplicationBranchProjs(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(new AddPrimaryKeyBuilder(TABLE, "uuid").build());
+ }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/AddPkToApplicationProjects.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/AddPkToApplicationProjects.java
new file mode 100644
index 00000000000..0aa5bb6bf23
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/AddPkToApplicationProjects.java
@@ -0,0 +1,38 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.v86;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+import org.sonar.server.platform.db.migration.version.v84.util.AddPrimaryKeyBuilder;
+
+public class AddPkToApplicationProjects extends DdlChange {
+ private static final String TABLE = "app_projects";
+
+ public AddPkToApplicationProjects(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(new AddPrimaryKeyBuilder(TABLE, "uuid").build());
+ }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/CreateApplicationBranchProjs.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/CreateApplicationBranchProjs.java
new file mode 100644
index 00000000000..5711c4384e3
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/CreateApplicationBranchProjs.java
@@ -0,0 +1,48 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.v86;
+
+import java.sql.SQLException;
+import org.sonar.core.util.Uuids;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.sql.CreateTableBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+import static org.sonar.server.platform.db.migration.def.BigIntegerColumnDef.newBigIntegerColumnDefBuilder;
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+public class CreateApplicationBranchProjs extends DdlChange {
+ public CreateApplicationBranchProjs(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(new CreateTableBuilder(getDialect(), "app_branch_project_branch")
+ .addColumn(newVarcharColumnDefBuilder().setColumnName("uuid").setIsNullable(false).setLimit(Uuids.MAX_LENGTH).build())
+ .addColumn(newVarcharColumnDefBuilder().setColumnName("application_uuid").setIsNullable(false).setLimit(Uuids.MAX_LENGTH).build())
+ .addColumn(newVarcharColumnDefBuilder().setColumnName("application_branch_uuid").setIsNullable(false).setLimit(Uuids.MAX_LENGTH).build())
+ .addColumn(newVarcharColumnDefBuilder().setColumnName("project_uuid").setIsNullable(false).setLimit(Uuids.MAX_LENGTH).build())
+ .addColumn(newVarcharColumnDefBuilder().setColumnName("project_branch_uuid").setIsNullable(false).setLimit(Uuids.MAX_LENGTH).build())
+ .addColumn(newBigIntegerColumnDefBuilder().setColumnName("created_at").setIsNullable(false).build())
+ .build());
+ }
+
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/CreateApplicationProjectsTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/CreateApplicationProjectsTable.java
new file mode 100644
index 00000000000..7ef8f8eee22
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/CreateApplicationProjectsTable.java
@@ -0,0 +1,45 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.v86;
+
+import java.sql.SQLException;
+import org.sonar.core.util.Uuids;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.sql.CreateTableBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+import static org.sonar.server.platform.db.migration.def.BigIntegerColumnDef.newBigIntegerColumnDefBuilder;
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+public class CreateApplicationProjectsTable extends DdlChange {
+ public CreateApplicationProjectsTable(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(new CreateTableBuilder(getDialect(), "app_projects")
+ .addColumn(newVarcharColumnDefBuilder().setColumnName("uuid").setIsNullable(false).setLimit(Uuids.MAX_LENGTH).build())
+ .addColumn(newVarcharColumnDefBuilder().setColumnName("application_uuid").setIsNullable(false).setLimit(Uuids.MAX_LENGTH).build())
+ .addColumn(newVarcharColumnDefBuilder().setColumnName("project_uuid").setIsNullable(false).setLimit(Uuids.MAX_LENGTH).build())
+ .addColumn(newBigIntegerColumnDefBuilder().setColumnName("created_at").setIsNullable(false).build())
+ .build());
+ }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/DbVersion86.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/DbVersion86.java
index 9429fc93a39..f826e79eb6e 100644
--- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/DbVersion86.java
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/DbVersion86.java
@@ -43,6 +43,16 @@ public class DbVersion86 implements DbVersion {
.add(4113, "Make 'name' column in 'groups' table unique", AddUniqueIndexOnNameColumnOfGroupsTable.class)
.add(4114, "Move default permission templates to internal properties", MoveDefaultTemplatesToInternalProperties.class)
.add(4115, "Set 'sonar.forceAuthentication' to false for upgraded instances", SetForceAuthenticationSettings.class)
+
+ .add(4116, "Create table 'app_projects'", CreateApplicationProjectsTable.class)
+ .add(4117, "Create primary key for 'app_projects'", AddPkToApplicationProjects.class)
+ .add(4118, "Create index for 'app_projects'", AddIndexToApplicationProjects.class)
+
+ .add(4119, "Create table 'app_branch_project_branch'", CreateApplicationBranchProjs.class)
+ .add(4120, "Create primary key for 'app_branch_project_branch'", AddPkToApplicationBranchProjs.class)
+ .add(4121, "Create index for 'app_branch_project_branch'", AddIndexToApplicationBranchProjs.class)
+
+ .add(4122, "Migrate view definitions from xml to db", MigrateApplicationDefinitionsFromXmlToDb.class)
;
}
}
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
new file mode 100644
index 00000000000..72675effcc2
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v86/MigrateApplicationDefinitionsFromXmlToDb.java
@@ -0,0 +1,732 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.v86;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.sql.SQLException;
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import javax.xml.XMLConstants;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.validation.SchemaFactory;
+import org.apache.commons.lang.StringUtils;
+import org.codehaus.staxmate.SMInputFactory;
+import org.codehaus.staxmate.in.SMHierarchicCursor;
+import org.codehaus.staxmate.in.SMInputCursor;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.utils.System2;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.step.DataChange;
+import org.sonar.server.platform.db.migration.step.Select;
+import org.sonar.server.platform.db.migration.step.Upsert;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import static java.lang.String.format;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING;
+import static org.apache.commons.io.IOUtils.toInputStream;
+import static org.apache.commons.lang.StringEscapeUtils.escapeXml;
+import static org.apache.commons.lang.StringUtils.trim;
+
+public class MigrateApplicationDefinitionsFromXmlToDb extends DataChange {
+ static final int TEXT_VALUE_MAX_LENGTH = 4000;
+ private static final String SELECT_APPLICATION_UUID_BY_KEY = "select uuid from projects where kee = ? and qualifier = 'APP'";
+ private static final String SELECT_PROJECTS_BY_KEYS = "select kee,uuid from projects where kee in (%s) and qualifier = 'TRK'";
+ private static final String UPDATE_INTERNAL_PROP_TEXT_VALUE = "update internal_properties set text_value = ?, clob_value = NULL where kee = ?";
+ private static final String UPDATE_INTERNAL_PROP_CLOB_VALUE = "update internal_properties set clob_value = ?, text_value = NULL where kee = ?";
+ private static final String VIEWS_DEF_KEY = "views.def";
+
+ private final UuidFactory uuidFactory;
+ private final System2 system;
+
+ public MigrateApplicationDefinitionsFromXmlToDb(Database db, UuidFactory uuidFactory, System2 system) {
+ super(db);
+
+ this.uuidFactory = uuidFactory;
+ this.system = system;
+ }
+
+ @Override
+ protected void execute(Context context) throws SQLException {
+ String xml = getViewsDefinition(context);
+ // skip migration if `views.def` does not exist in the db
+ if (xml == null) {
+ return;
+ }
+
+ try {
+ Map<String, ViewXml.ViewDef> defs = ViewXml.parse(xml);
+ List<ViewXml.ViewDef> applications = defs.values()
+ .stream()
+ .filter(v -> Qualifiers.APP.equals(v.getQualifier()))
+ .collect(Collectors.toList());
+ for (ViewXml.ViewDef app : applications) {
+ insertApplication(context, app);
+ }
+ cleanUpViewsDefinitionsXml(context, defs.values());
+ } catch (Exception e) {
+ throw new IllegalStateException("Failed to migrate views definitions property.", e);
+ }
+
+ }
+
+ private void insertApplication(Context context, ViewXml.ViewDef app) throws SQLException {
+ long now = system.now();
+ String applicationUuid = context.prepareSelect(SELECT_APPLICATION_UUID_BY_KEY)
+ .setString(1, app.getKey())
+ .get(r -> r.getString(1));
+
+ // ignore if application only exists in xml and not in the db. It will be removed from the xml at later stage of the migration.
+ if (applicationUuid == null) {
+ return;
+ }
+
+ String queryParam = app.getProjects().stream().map(uuid -> "'" + uuid + "'").collect(Collectors.joining(","));
+ Map<String, String> projectUuidsByKeys = context.prepareSelect(format(SELECT_PROJECTS_BY_KEYS, queryParam))
+ .list(r -> new AbstractMap.SimpleEntry<>(r.getString(1), r.getString(2)))
+ .stream()
+ .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));
+
+ insertApplicationProjects(context, app, applicationUuid, projectUuidsByKeys, now);
+ if (!app.getApplicationBranches().isEmpty()) {
+ insertApplicationBranchesProjects(context, app, applicationUuid, projectUuidsByKeys, now);
+ }
+ }
+
+ private void insertApplicationProjects(Context context, ViewXml.ViewDef app, String applicationUuid,
+ Map<String, String> projectUuidsByKeys, 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()) {
+ String applicationProjectUuid = uuidFactory.create();
+ String projectUuid = projectUuidsByKeys.get(projectKey);
+
+ // ignore project if it does not exist anymore
+ if (projectUuid == null) {
+ continue;
+ }
+
+ insertApplicationProjectsQuery
+ .setString(1, applicationProjectUuid)
+ .setString(2, applicationUuid)
+ .setString(3, projectUuid)
+ .setLong(4, createdTime)
+ .addBatch();
+ }
+
+ insertApplicationProjectsQuery.execute().commit();
+ }
+
+ private void insertApplicationBranchesProjects(Context context, ViewXml.ViewDef app, String applicationUuid,
+ Map<String, String> projectUuidsByKeys, long createdTime) throws SQLException {
+ Map<String, String> appBranchUuidByKey = context.prepareSelect("select kee,uuid from project_branches where project_uuid = ?")
+ .setString(1, applicationUuid)
+ .list(r -> new AbstractMap.SimpleEntry<>(r.getString(1), r.getString(2)))
+ .stream()
+ .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));
+
+ Upsert insertApplicationsBranchProjectsQuery = context.prepareUpsert("insert into " +
+ "app_branch_project_branch(uuid, application_uuid, application_branch_uuid, project_uuid, project_branch_uuid, created_at) " +
+ "values (?, ?, ?, ?, ?, ?)");
+ boolean insert = false;
+ for (ViewXml.ApplicationBranchDef branch : app.getApplicationBranches()) {
+ String applicationBranchUuid = appBranchUuidByKey.get(branch.getKey());
+ // ignore application branch if it does not exist in the DB anymore
+ if (applicationBranchUuid == null) {
+ continue;
+ }
+
+ if (insertApplicationBranchProjects(context, branch, applicationUuid, applicationBranchUuid, projectUuidsByKeys, createdTime,
+ insertApplicationsBranchProjectsQuery)) {
+ insert = true;
+ }
+ }
+
+ if (insert) {
+ insertApplicationsBranchProjectsQuery.execute().commit();
+ }
+ }
+
+ private boolean insertApplicationBranchProjects(Context context, ViewXml.ApplicationBranchDef branch, String applicationUuid,
+ String applicationBranchUuid, Map<String, String> projectUuidsByKeys, long createdTime,
+ Upsert insertApplicationsBranchProjectsQuery) throws SQLException {
+
+ boolean insert = false;
+ for (ViewXml.ApplicationProjectDef appProjDef : branch.getProjects()) {
+ String projectUuid = projectUuidsByKeys.get(appProjDef.getKey());
+
+ // ignore projects that do not exist in the DB anymore
+ if (projectUuid != null) {
+ String projectBranchUuid = context.prepareSelect("select uuid from project_branches where project_uuid = ? and kee = ?")
+ .setString(1, projectUuid)
+ .setString(2, appProjDef.getBranch())
+ .get(r -> r.getString(1));
+
+ // ignore project branches that do not exist in the DB anymore
+ if (projectBranchUuid != null) {
+ String applicationBranchProjectUuid = uuidFactory.create();
+ insertApplicationsBranchProjectsQuery
+ .setString(1, applicationBranchProjectUuid)
+ .setString(2, applicationUuid)
+ .setString(3, applicationBranchUuid)
+ .setString(4, projectUuid)
+ .setString(5, projectBranchUuid)
+ .setLong(6, createdTime)
+ .addBatch();
+ insert = true;
+ }
+ }
+ }
+
+ return insert;
+ }
+
+ @CheckForNull
+ private static String getViewsDefinition(DataChange.Context context) throws SQLException {
+ Select select = context.prepareSelect("select text_value,clob_value from internal_properties where kee=?");
+ select.setString(1, VIEWS_DEF_KEY);
+ return select.get(row -> {
+ String v = row.getString(1);
+ if (v != null) {
+ return v;
+ } else {
+ return row.getString(2);
+ }
+ });
+ }
+
+ private static void cleanUpViewsDefinitionsXml(Context context, Collection<ViewXml.ViewDef> definitions) throws SQLException, IOException {
+ definitions = definitions.stream()
+ .filter(d -> !"APP".equals(d.getQualifier()))
+ .collect(Collectors.toList());
+
+ StringWriter output = new StringWriter();
+ new ViewXml.ViewDefinitionsSerializer().write(definitions, output);
+ String value = output.toString();
+ String statement = UPDATE_INTERNAL_PROP_TEXT_VALUE;
+ if (mustBeStoredInClob(value)) {
+ statement = UPDATE_INTERNAL_PROP_CLOB_VALUE;
+ }
+
+ context.prepareUpsert(statement)
+ .setString(1, output.toString())
+ .setString(2, VIEWS_DEF_KEY)
+ .execute()
+ .commit();
+ }
+
+ private static boolean mustBeStoredInClob(String value) {
+ return value.length() > TEXT_VALUE_MAX_LENGTH;
+ }
+
+ private static class ViewXml {
+ static final String SCHEMA_VIEWS = "/static/views.xsd";
+ static final String VIEWS_HEADER_BARE = "<views>";
+ static final Pattern VIEWS_HEADER_BARE_PATTERN = Pattern.compile(VIEWS_HEADER_BARE);
+ static final String VIEWS_HEADER_FQ = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ + "<views xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://sonarsource.com/schema/views\">";
+
+ private ViewXml() {
+ // nothing to do here
+ }
+
+ private static Map<String, ViewDef> parse(String xml) throws ParserConfigurationException, SAXException, IOException, XMLStreamException {
+ if (StringUtils.isEmpty(xml)) {
+ return new LinkedHashMap<>(0);
+ }
+
+ List<ViewDef> views;
+ validate(xml);
+ SMInputFactory inputFactory = initStax();
+ SMHierarchicCursor rootC = inputFactory.rootElementCursor(new StringReader(xml));
+ rootC.advance(); // <views>
+ SMInputCursor cursor = rootC.childElementCursor();
+ views = parseViewDefinitions(cursor);
+
+ Map<String, ViewDef> result = new LinkedHashMap<>(views.size());
+ for (ViewDef def : views) {
+ result.put(def.getKey(), def);
+ }
+
+ return result;
+ }
+
+ private static void validate(String xml) throws IOException, SAXException, ParserConfigurationException {
+ // Replace bare, namespace unaware header with fully qualified header (with schema declaration)
+ String fullyQualifiedXml = VIEWS_HEADER_BARE_PATTERN.matcher(xml).replaceFirst(VIEWS_HEADER_FQ);
+ try (InputStream xsd = MigrateApplicationDefinitionsFromXmlToDb.class.getResourceAsStream(SCHEMA_VIEWS)) {
+ InputSource viewsDefinition = new InputSource(new InputStreamReader(toInputStream(fullyQualifiedXml, UTF_8), UTF_8));
+
+ SchemaFactory saxSchemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+ saxSchemaFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
+ saxSchemaFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
+
+ SAXParserFactory parserFactory = SAXParserFactory.newInstance();
+ parserFactory.setFeature(FEATURE_SECURE_PROCESSING, true);
+ parserFactory.setNamespaceAware(true);
+ parserFactory.setSchema(saxSchemaFactory.newSchema(new SAXSource(new InputSource(xsd))));
+
+ SAXParser saxParser = parserFactory.newSAXParser();
+ saxParser.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
+ saxParser.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
+ saxParser.parse(viewsDefinition, new ViewsValidator());
+ }
+ }
+
+ private static List<ViewDef> parseViewDefinitions(SMInputCursor viewsCursor) throws XMLStreamException {
+ List<ViewDef> views = new ArrayList<>();
+ while (viewsCursor.getNext() != null) {
+ ViewDef viewDef = new ViewDef();
+ viewDef.setKey(viewsCursor.getAttrValue("key"));
+ viewDef.setDef(Boolean.parseBoolean(viewsCursor.getAttrValue("def")));
+ viewDef.setParent(viewsCursor.getAttrValue("parent"));
+ viewDef.setRoot(viewsCursor.getAttrValue("root"));
+ SMInputCursor viewCursor = viewsCursor.childElementCursor();
+ while (viewCursor.getNext() != null) {
+ String nodeName = viewCursor.getLocalName();
+ parseChildElement(viewDef, viewCursor, nodeName);
+ }
+ views.add(viewDef);
+ }
+ return views;
+ }
+
+ private static void parseChildElement(ViewDef viewDef, SMInputCursor viewCursor, String nodeName) throws XMLStreamException {
+ if (StringUtils.equals(nodeName, "name")) {
+ viewDef.setName(trim(viewCursor.collectDescendantText()));
+ } else if (StringUtils.equals(nodeName, "desc")) {
+ viewDef.setDesc(trim(viewCursor.collectDescendantText()));
+ } else if (StringUtils.equals(nodeName, "regexp")) {
+ viewDef.setRegexp(trim(viewCursor.collectDescendantText()));
+ } else if (StringUtils.equals(nodeName, "language")) {
+ viewDef.setLanguage(trim(viewCursor.collectDescendantText()));
+ } else if (StringUtils.equals(nodeName, "tag_key")) {
+ viewDef.setTagKey(trim(viewCursor.collectDescendantText()));
+ } else if (StringUtils.equals(nodeName, "tag_value")) {
+ viewDef.setTagValue(trim(viewCursor.collectDescendantText()));
+ } else if (StringUtils.equals(nodeName, "p")) {
+ viewDef.addProject(trim(viewCursor.collectDescendantText()));
+ } else if (StringUtils.equals(nodeName, "vw-ref")) {
+ viewDef.addReference(trim(viewCursor.collectDescendantText()));
+ } else if (StringUtils.equals(nodeName, "qualifier")) {
+ viewDef.setQualifier(trim(viewCursor.collectDescendantText()));
+ } else if (StringUtils.equals(nodeName, "branch")) {
+ parseBranch(viewDef, viewCursor);
+ } else if (StringUtils.equals(nodeName, "tagsAssociation")) {
+ parseTagsAssociation(viewDef, viewCursor);
+ }
+ }
+
+ private static void parseBranch(ViewDef def, SMInputCursor viewCursor) throws XMLStreamException {
+ List<ApplicationProjectDef> projects = new ArrayList<>();
+ String key = viewCursor.getAttrValue("key");
+ SMInputCursor projectCursor = viewCursor.childElementCursor();
+ while (projectCursor.getNext() != null) {
+ if (Objects.equals(projectCursor.getLocalName(), "p")) {
+ String branch = projectCursor.getAttrValue("branch");
+ String projectKey = trim(projectCursor.collectDescendantText());
+ projects.add(new ApplicationProjectDef().setKey(projectKey).setBranch(branch));
+ }
+ }
+ def.getApplicationBranches().add(new ApplicationBranchDef()
+ .setKey(key)
+ .setProjects(projects));
+ }
+
+ private static void parseTagsAssociation(ViewDef def, SMInputCursor viewCursor) throws XMLStreamException {
+ SMInputCursor projectCursor = viewCursor.childElementCursor();
+ while (projectCursor.getNext() != null) {
+ def.addTagAssociation(trim(projectCursor.collectDescendantText()));
+ }
+ }
+
+ private static SMInputFactory initStax() {
+ XMLInputFactory xmlFactory = XMLInputFactory.newInstance();
+ xmlFactory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE);
+ xmlFactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.FALSE);
+ // just so it won't try to load DTD in if there's DOCTYPE
+ xmlFactory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
+ xmlFactory.setProperty(XMLInputFactory.IS_VALIDATING, Boolean.FALSE);
+ return new SMInputFactory(xmlFactory);
+ }
+
+ private static class ViewDef {
+ String key = null;
+
+ String parent = null;
+
+ String root = null;
+
+ boolean def = false;
+
+ List<String> p = new ArrayList<>();
+
+ List<String> vwRef = new ArrayList<>();
+
+ String name = null;
+
+ String desc = null;
+
+ String regexp = null;
+
+ String language = null;
+
+ String tagKey = null;
+
+ String tagValue = null;
+
+ String qualifier = null;
+
+ List<ApplicationBranchDef> applicationBranches = new ArrayList<>();
+
+ Set<String> tagsAssociation = new TreeSet<>();
+
+ public String getKey() {
+ return key;
+ }
+
+ public String getParent() {
+ return parent;
+ }
+
+ @CheckForNull
+ public String getRoot() {
+ return root;
+ }
+
+ public boolean isDef() {
+ return def;
+ }
+
+ public List<String> getProjects() {
+ return p;
+ }
+
+ public List<String> getReferences() {
+ return vwRef;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ @CheckForNull
+ public String getRegexp() {
+ return regexp;
+ }
+
+ @CheckForNull
+ public String getLanguage() {
+ return language;
+ }
+
+ @CheckForNull
+ public String getTagKey() {
+ return tagKey;
+ }
+
+ @CheckForNull
+ public String getTagValue() {
+ return tagValue;
+ }
+
+ @CheckForNull
+ public String getQualifier() {
+ return qualifier;
+ }
+
+ public List<ApplicationBranchDef> getApplicationBranches() {
+ return applicationBranches;
+ }
+
+ public Set<String> getTagsAssociation() {
+ return tagsAssociation;
+ }
+
+ public ViewDef setKey(String key) {
+ this.key = key;
+ return this;
+ }
+
+ public ViewDef setParent(String parent) {
+ this.parent = parent;
+ return this;
+ }
+
+ public ViewDef setRoot(@Nullable String root) {
+ this.root = root;
+ return this;
+ }
+
+ public ViewDef setDef(boolean def) {
+ this.def = def;
+ return this;
+ }
+
+ public ViewDef setProjects(List<String> projects) {
+ this.p = projects;
+ return this;
+ }
+
+ public ViewDef addProject(String project) {
+ this.p.add(project);
+ return this;
+ }
+
+ public ViewDef removeProject(String project) {
+ this.p.remove(project);
+ return this;
+ }
+
+ public ViewDef setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public ViewDef setDesc(@Nullable String desc) {
+ this.desc = desc;
+ return this;
+ }
+
+ public ViewDef setRegexp(@Nullable String regexp) {
+ this.regexp = regexp;
+ return this;
+ }
+
+ public ViewDef setLanguage(@Nullable String language) {
+ this.language = language;
+ return this;
+ }
+
+ public ViewDef setTagKey(@Nullable String tagKey) {
+ this.tagKey = tagKey;
+ return this;
+ }
+
+ public ViewDef setTagValue(@Nullable String tagValue) {
+ this.tagValue = tagValue;
+ return this;
+ }
+
+ public ViewDef addReference(String reference) {
+ this.vwRef.add(reference);
+ return this;
+ }
+
+ public ViewDef removeReference(String reference) {
+ this.vwRef.remove(reference);
+ return this;
+ }
+
+ public ViewDef setReferences(List<String> vwRef) {
+ this.vwRef = vwRef;
+ return this;
+ }
+
+ public ViewDef setQualifier(@Nullable String qualifier) {
+ this.qualifier = qualifier;
+ return this;
+ }
+
+ public ViewDef setApplicationBranches(List<ApplicationBranchDef> branches) {
+ this.applicationBranches = branches;
+ return this;
+ }
+
+ public ViewDef addTagAssociation(String tag) {
+ this.tagsAssociation.add(tag);
+ return this;
+ }
+
+ public ViewDef setTagsAssociation(Set<String> tagsAssociation) {
+ this.tagsAssociation = tagsAssociation;
+ return this;
+ }
+ }
+
+ private static class ApplicationProjectDef {
+ private String key = null;
+ private String branch = null;
+
+ public String getKey() {
+ return key;
+ }
+
+ public ApplicationProjectDef setKey(String key) {
+ this.key = key;
+ return this;
+ }
+
+ @CheckForNull
+ public String getBranch() {
+ return branch;
+ }
+
+ public ApplicationProjectDef setBranch(@Nullable String branch) {
+ this.branch = branch;
+ return this;
+ }
+ }
+
+ private static class ApplicationBranchDef {
+
+ private String key = null;
+ private List<ApplicationProjectDef> p = new ArrayList<>();
+
+ public String getKey() {
+ return key;
+ }
+
+ public ApplicationBranchDef setKey(String key) {
+ this.key = key;
+ return this;
+ }
+
+ public List<ApplicationProjectDef> getProjects() {
+ return p;
+ }
+
+ public ApplicationBranchDef setProjects(List<ApplicationProjectDef> p) {
+ this.p = p;
+ return this;
+ }
+ }
+
+ private static final class ViewsValidator extends DefaultHandler {
+ @Override
+ public void error(SAXParseException exception) throws SAXException {
+ throw exception;
+ }
+
+ @Override
+ public void warning(SAXParseException exception) throws SAXException {
+ throw exception;
+ }
+
+ @Override
+ public void fatalError(SAXParseException exception) throws SAXException {
+ throw exception;
+ }
+ }
+
+ static class ViewDefinitionsSerializer {
+
+ public void write(Collection<ViewDef> definitions, Writer writer) throws IOException {
+ writer.append(VIEWS_HEADER_BARE);
+
+ for (ViewDef def : definitions) {
+ writer.append("<vw");
+ writer.append(" key=\"").append(escapeXml(def.getKey())).append("\"");
+ writer.append(" def=\"").append(Boolean.toString(def.isDef())).append("\"");
+ String parent = def.getParent();
+ if (parent != null) {
+ writer.append(" root=\"").append(escapeXml(def.getRoot())).append("\"");
+ writer.append(" parent=\"").append(escapeXml(parent)).append("\"");
+ }
+ writer.append(">");
+
+ writer.append("<name><![CDATA[").append(def.getName()).append("]]></name>");
+ writeOptionalElements(writer, def);
+
+ for (String project : def.getProjects()) {
+ writer.append("<p>").append(project).append("</p>");
+ }
+ for (String ref : def.getReferences()) {
+ writer.append("<vw-ref><![CDATA[").append(ref).append("]]></vw-ref>");
+ }
+ writeTagsAssociation(writer, def);
+ writer.append("</vw>");
+ }
+
+ writer.append("</views>");
+ }
+
+ private static void writeOptionalElements(Writer writer, ViewDef def) throws IOException {
+ String description = def.getDesc();
+ if (description != null) {
+ writer.append("<desc><![CDATA[").append(description).append("]]></desc>");
+ }
+ String regexp = def.getRegexp();
+ if (regexp != null) {
+ writer.append("<regexp><![CDATA[").append(regexp).append("]]></regexp>");
+ }
+ String language = def.getLanguage();
+ if (language != null) {
+ writer.append("<language><![CDATA[").append(language).append("]]></language>");
+ }
+ String customMeasureKey = def.getTagKey();
+ if (customMeasureKey != null) {
+ writer.append("<tag_key><![CDATA[").append(customMeasureKey).append("]]></tag_key>");
+ }
+ String customMeasureValue = def.getTagValue();
+ if (customMeasureValue != null) {
+ writer.append("<tag_value><![CDATA[").append(customMeasureValue).append("]]></tag_value>");
+ }
+ String qualifier = def.getQualifier();
+ if (qualifier != null) {
+ writer.append("<qualifier><![CDATA[").append(qualifier).append("]]></qualifier>");
+ }
+ }
+
+ private static void writeTagsAssociation(Writer writer, ViewDef definition) throws IOException {
+ Set<String> tagsAssociation = definition.getTagsAssociation();
+ if (tagsAssociation.isEmpty()) {
+ return;
+ }
+ writer.append("<tagsAssociation>");
+ for (String tag : tagsAssociation) {
+ writer.append("<tag>").append(tag).append("</tag>");
+ }
+ writer.append("</tagsAssociation>");
+ }
+
+ }
+ }
+}
diff --git a/server/sonar-db-migration/src/main/resources/static/views.xsd b/server/sonar-db-migration/src/main/resources/static/views.xsd
new file mode 100644
index 00000000000..952ae224cb3
--- /dev/null
+++ b/server/sonar-db-migration/src/main/resources/static/views.xsd
@@ -0,0 +1,85 @@
+<?xml version="1.0"?>
+<schema targetNamespace="http://sonarsource.com/schema/views"
+ elementFormDefault="qualified" xmlns="http://www.w3.org/2001/XMLSchema"
+ xmlns:tns="http://sonarsource.com/schema/views">
+ <element name="views">
+ <complexType>
+ <sequence>
+ <element name="vw" type="tns:vwType" maxOccurs="unbounded" minOccurs="0"/>
+ </sequence>
+ </complexType>
+ </element>
+ <complexType name="vwType">
+ <sequence>
+ <choice minOccurs="1" maxOccurs="unbounded">
+ <element name="name" maxOccurs="1" minOccurs="1" type="tns:nameType"/>
+ <element name="desc" maxOccurs="1" minOccurs="0" type="tns:descType"/>
+ <element name="regexp" maxOccurs="1" minOccurs="0" type="string"/>
+ <element name="language" maxOccurs="1" minOccurs="0" type="string"/>
+ <element name="tag_key" maxOccurs="1" minOccurs="0" type="string"/>
+ <element name="tag_value" maxOccurs="1" minOccurs="0" type="string"/>
+ <element name="p" maxOccurs="unbounded" minOccurs="0" type="tns:keyType"/>
+ <element name="remoteView" maxOccurs="unbounded" minOccurs="0" type="tns:remoteViewType"/>
+ <element name="vw-ref" maxOccurs="unbounded" minOccurs="0" type="tns:keyType"/>
+ <element name="qualifier" maxOccurs="1" minOccurs="0" type="string"/>
+ <element name="branch" maxOccurs="unbounded" minOccurs="0" type="tns:branchType"/>
+ <element name="tagsAssociation" maxOccurs="unbounded" minOccurs="0" type="tns:tagsAssociationType"/>
+ </choice>
+ </sequence>
+ <attribute name="key" use="required" type="tns:keyType"/>
+ <attribute name="def" type="boolean"/>
+ <attribute name="root" type="tns:keyType"/>
+ <attribute name="parent" type="tns:keyType"/>
+ </complexType>
+ <complexType name="tagsAssociationType">
+ <sequence>
+ <element name="tag" maxOccurs="unbounded" minOccurs="1" type="string"/>
+ </sequence>
+ </complexType>
+ <complexType name="branchType">
+ <sequence>
+ <choice minOccurs="1" maxOccurs="unbounded">
+ <element name="p" maxOccurs="unbounded" minOccurs="1" type="tns:branchProject"/>
+ </choice>
+ </sequence>
+ <attribute name="key" use="required" type="tns:branchKeyType"/>
+ </complexType>
+ <complexType name="branchProject">
+ <simpleContent>
+ <extension base="tns:keyType">
+ <attribute name="branch" type="tns:branchKeyType" />
+ </extension>
+ </simpleContent>
+ </complexType>
+ <complexType name="remoteViewType">
+ <sequence>
+ <choice minOccurs="1" maxOccurs="unbounded">
+ <element name="key" minOccurs="1" maxOccurs="1" type="tns:keyType"/>
+ <element name="remoteKey" minOccurs="1" maxOccurs="1" type="tns:keyType"/>
+ <element name="name" minOccurs="1" maxOccurs="1" type="tns:nameType"/>
+ <element name="desc" minOccurs="0" maxOccurs="1" type="tns:descType"/>
+ <element name="server" minOccurs="1" maxOccurs="1" type="string"/>
+ </choice>
+ </sequence>
+ </complexType>
+ <simpleType name="keyType">
+ <restriction base="string">
+ <maxLength value="400"/>
+ </restriction>
+ </simpleType>
+ <simpleType name="nameType">
+ <restriction base="string">
+ <maxLength value="256"/>
+ </restriction>
+ </simpleType>
+ <simpleType name="descType">
+ <restriction base="string">
+ <maxLength value="256"/>
+ </restriction>
+ </simpleType>
+ <simpleType name="branchKeyType">
+ <restriction base="string">
+ <maxLength value="255"/>
+ </restriction>
+ </simpleType>
+</schema>
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/AddIndexToApplicationBranchProjsTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/AddIndexToApplicationBranchProjsTest.java
new file mode 100644
index 00000000000..b9e522a0911
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/AddIndexToApplicationBranchProjsTest.java
@@ -0,0 +1,44 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.v86;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+import org.sonar.server.platform.db.migration.step.MigrationStep;
+
+public class AddIndexToApplicationBranchProjsTest {
+ @Rule
+ public CoreDbTester db = CoreDbTester.createForSchema(AddIndexToApplicationBranchProjsTest.class, "schema.sql");
+
+ private final MigrationStep underTest = new AddIndexToApplicationBranchProjs(db.database());
+
+ @Test
+ public void execute() throws SQLException {
+ underTest.execute();
+
+ db.assertUniqueIndex("app_branch_project_branch", "uniq_app_branch_proj", "application_branch_uuid", "project_branch_uuid");
+ db.assertIndex("app_branch_project_branch", "idx_abpb_app_uuid", "application_uuid");
+ db.assertIndex("app_branch_project_branch", "idx_abpb_app_branch_uuid", "application_branch_uuid");
+ db.assertIndex("app_branch_project_branch", "idx_abpb_proj_uuid", "project_uuid");
+ db.assertIndex("app_branch_project_branch", "idx_abpb_proj_branch_uuid", "project_branch_uuid");
+ }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/AddIndexToApplicationProjectsTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/AddIndexToApplicationProjectsTest.java
new file mode 100644
index 00000000000..3b876b2421c
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/AddIndexToApplicationProjectsTest.java
@@ -0,0 +1,42 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.v86;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+import org.sonar.server.platform.db.migration.step.MigrationStep;
+
+public class AddIndexToApplicationProjectsTest {
+ @Rule
+ public CoreDbTester db = CoreDbTester.createForSchema(AddIndexToApplicationProjectsTest.class, "schema.sql");
+
+ private final MigrationStep underTest = new AddIndexToApplicationProjects(db.database());
+
+ @Test
+ public void execute() throws SQLException {
+ underTest.execute();
+
+ db.assertUniqueIndex("app_projects", "uniq_app_projects", "application_uuid", "project_uuid");
+ db.assertIndex("app_projects", "idx_app_proj_application_uuid", "application_uuid");
+ db.assertIndex("app_projects", "idx_app_proj_project_uuid", "project_uuid");
+ }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/AddPkToApplicationBranchProjsTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/AddPkToApplicationBranchProjsTest.java
new file mode 100644
index 00000000000..a459e988743
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/AddPkToApplicationBranchProjsTest.java
@@ -0,0 +1,40 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.v86;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+import org.sonar.server.platform.db.migration.step.MigrationStep;
+
+public class AddPkToApplicationBranchProjsTest {
+ @Rule
+ public CoreDbTester db = CoreDbTester.createForSchema(AddPkToApplicationBranchProjsTest.class, "schema.sql");
+
+ private MigrationStep underTest = new AddPkToApplicationBranchProjs(db.database());
+
+ @Test
+ public void execute() throws SQLException {
+ db.assertNoPrimaryKey("app_branch_project_branch");
+ underTest.execute();
+ db.assertPrimaryKey("app_branch_project_branch", "pk_app_branch_project_branch", "uuid");
+ }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/AddPkToApplicationProjectsTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/AddPkToApplicationProjectsTest.java
new file mode 100644
index 00000000000..f5621117ea7
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/AddPkToApplicationProjectsTest.java
@@ -0,0 +1,40 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.v86;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+import org.sonar.server.platform.db.migration.step.MigrationStep;
+
+public class AddPkToApplicationProjectsTest {
+ @Rule
+ public CoreDbTester db = CoreDbTester.createForSchema(AddPkToApplicationProjectsTest.class, "schema.sql");
+
+ private MigrationStep underTest = new AddPkToApplicationProjects(db.database());
+
+ @Test
+ public void execute() throws SQLException {
+ db.assertNoPrimaryKey("app_projects");
+ underTest.execute();
+ db.assertPrimaryKey("app_projects", "pk_app_projects", "uuid");
+ }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/CreateApplicationProjectsTableTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/CreateApplicationProjectsTableTest.java
new file mode 100644
index 00000000000..e21cae3874c
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/CreateApplicationProjectsTableTest.java
@@ -0,0 +1,43 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.v86;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+import org.sonar.server.platform.db.migration.step.MigrationStep;
+import org.sonar.server.platform.db.migration.version.v86.CreateApplicationProjectsTable;
+
+public class CreateApplicationProjectsTableTest {
+ private final static String TABLE_NAME = "app_projects";
+
+ @Rule
+ public CoreDbTester db = CoreDbTester.createEmpty();
+
+ private MigrationStep underTest = new CreateApplicationProjectsTable(db.database());
+
+ @Test
+ public void execute() throws SQLException {
+ db.assertTableDoesNotExist(TABLE_NAME);
+ underTest.execute();
+ db.assertTableExists(TABLE_NAME);
+ }
+}
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
new file mode 100644
index 00000000000..ad79409fd34
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v86/MigrateApplicationDefinitionsFromXmlToDbTest.java
@@ -0,0 +1,717 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.v86;
+
+import java.sql.SQLException;
+import java.util.Map;
+import javax.annotation.Nullable;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.impl.utils.TestSystem2;
+import org.sonar.api.utils.System2;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.core.util.UuidFactoryFast;
+import org.sonar.db.CoreDbTester;
+import org.sonar.server.platform.db.migration.step.MigrationStep;
+import org.xmlunit.builder.DiffBuilder;
+import org.xmlunit.builder.Input;
+import org.xmlunit.diff.Diff;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.assertj.core.api.Assertions.tuple;
+import static org.sonar.server.platform.db.migration.version.v86.MigrateApplicationDefinitionsFromXmlToDb.TEXT_VALUE_MAX_LENGTH;
+
+public class MigrateApplicationDefinitionsFromXmlToDbTest {
+ private static final String QUALIFIER_PROJECT = "TRK";
+ private static final String QUALIFIER_APP = "APP";
+ private static final long NOW = 100_000_000_000L;
+
+ private static final String PROJECT_1_UUID = "proj1-uuid";
+ private static final String PROJECT_1_MASTER_BRANCH_UUID = "proj1-master-uuid";
+ private static final String PROJECT_1_BRANCH_1_UUID = "proj1-branch1-uuid";
+ private static final String PROJECT_1_BRANCH_2_UUID = "proj1-branch2-uuid";
+ private static final String PROJECT_2_UUID = "proj2-uuid";
+ private static final String PROJECT_2_MASTER_BRANCH_UUID = "proj2-master-uuid";
+ private static final String PROJECT_2_BRANCH_1_UUID = "proj2-branch1-uuid";
+ private static final String APP_1_UUID = "app1-uuid";
+ private static final String APP_1_MASTER_BRANCH_UUID = "app1-master-uuid";
+ private static final String APP_1_BRANCH_1_UUID = "app1-branch1-uuid";
+ private static final String APP_1_BRANCH_2_UUID = "app1-branch2-uuid";
+ private static final String APP_2_UUID = "app2-uuid";
+ private static final String APP_2_MASTER_BRANCH_UUID = "app2-master-uuid";
+ private static final String APP_2_BRANCH_1_UUID = "app2-branch1-uuid";
+ private static final String EMPTY_XML = "<views></views>";
+
+ private static final String EMPTY_APP_XML = "<views>\n" +
+ " <vw key=\"app1\" def=\"false\">\n" +
+ " <name><![CDATA[app1]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <qualifier><![CDATA[APP]]></qualifier>\n" +
+ " </vw>\n" +
+ "</views>";
+
+ private static final String APP_WITH_NO_BRANCHES_XML = "<views>\n" +
+ " <vw key=\"app1-key\" def=\"false\">\n" +
+ " <name><![CDATA[app1-key]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <qualifier><![CDATA[APP]]></qualifier>\n" +
+ " <p>proj1-key</p>\n" +
+ " <p>proj2-key</p>\n" +
+ " </vw>\n" +
+ "</views>";
+
+ private static final String COMPLEX_XML_BEFORE = "<views>\n" +
+ " <vw key=\"app1-key\" def=\"false\">\n" +
+ " <name><![CDATA[app1-key]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <qualifier><![CDATA[APP]]></qualifier>\n" +
+ " <p>proj1-key</p>\n" +
+ " <p>proj2-key</p>\n" +
+ " <branch key=\"app1-branch1\">\n" +
+ " <p branch=\"proj1-branch-1\">proj1-key</p>\n" +
+ " <p branch=\"m1\">proj2-key</p>\n" +
+ " </branch>\n" +
+ " <branch key=\"app1-branch2\">\n" +
+ " <p branch=\"proj1-branch-2\">proj1-key</p>\n" +
+ " <p branch=\"m1\">proj2-key</p>\n" +
+ " </branch>\n" +
+ " </vw>\n" +
+ " <vw key=\"app2-key\" def=\"false\">\n" +
+ " <name><![CDATA[app2-key]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <qualifier><![CDATA[APP]]></qualifier>\n" +
+ " <p>proj1-key</p>\n" +
+ " <p>proj2-key</p>\n" +
+ " <branch key=\"m1\">\n" +
+ " <p branch=\"proj1-branch-1\">proj1-key</p>\n" +
+ " <p branch=\"m1\">proj2-key</p>\n" +
+ " </branch>\n" +
+ " </vw>\n" +
+ " <vw key=\"port1\" def=\"true\">\n" +
+ " <name><![CDATA[port1]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " </vw>\n" +
+ " <vw key=\"port2\" def=\"false\">\n" +
+ " <name><![CDATA[port2]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " <vw-ref><![CDATA[app1-key]]></vw-ref>\n" +
+ " <vw-ref><![CDATA[port1]]></vw-ref>\n" +
+ " </vw>\n" +
+ " <vw key=\"port3\" def=\"false\">\n" +
+ " <name><![CDATA[port3]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " <p>proj1-key</p>\n" +
+ " </vw>\n" +
+ " <vw key=\"port4\" def=\"false\">\n" +
+ " <name><![CDATA[port4]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " </vw>\n" +
+ " <vw key=\"portx\" def=\"false\" root=\"port2\" parent=\"port2\">\n" +
+ " <name><![CDATA[portx]]></name>\n" +
+ " </vw>\n" +
+ " <vw key=\"port5\" def=\"false\">\n" +
+ " <name><![CDATA[port5]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " <tagsAssociation>\n" +
+ " <tag>tag1</tag>\n" +
+ " </tagsAssociation>\n" +
+ " </vw>\n" +
+ " <vw key=\"port6\" def=\"false\">\n" +
+ " <name><![CDATA[port6]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <regexp><![CDATA[.*oj.*]]></regexp>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " </vw>\n" +
+ " <vw key=\"port7\" def=\"false\">\n" +
+ " <name><![CDATA[port7]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <tag_key><![CDATA[business_value]]></tag_key>\n" +
+ " <tag_value><![CDATA[12]]></tag_value>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " </vw>\n" +
+ "</views>";
+
+ private static final String COMPLEX_XML_AFTER = "<views>\n" +
+ " <vw key=\"port1\" def=\"true\">\n" +
+ " <name><![CDATA[port1]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " </vw>\n" +
+ " <vw key=\"port2\" def=\"false\">\n" +
+ " <name><![CDATA[port2]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " <vw-ref><![CDATA[app1-key]]></vw-ref>\n" +
+ " <vw-ref><![CDATA[port1]]></vw-ref>\n" +
+ " </vw>\n" +
+ " <vw key=\"port3\" def=\"false\">\n" +
+ " <name><![CDATA[port3]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " <p>proj1-key</p>\n" +
+ " </vw>\n" +
+ " <vw key=\"port4\" def=\"false\">\n" +
+ " <name><![CDATA[port4]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " </vw>\n" +
+ " <vw key=\"portx\" def=\"false\" root=\"port2\" parent=\"port2\">\n" +
+ " <name><![CDATA[portx]]></name>\n" +
+ " </vw>\n" +
+ " <vw key=\"port5\" def=\"false\">\n" +
+ " <name><![CDATA[port5]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " <tagsAssociation>\n" +
+ " <tag>tag1</tag>\n" +
+ " </tagsAssociation>\n" +
+ " </vw>\n" +
+ " <vw key=\"port6\" def=\"false\">\n" +
+ " <name><![CDATA[port6]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <regexp><![CDATA[.*oj.*]]></regexp>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " </vw>\n" +
+ " <vw key=\"port7\" def=\"false\">\n" +
+ " <name><![CDATA[port7]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <tag_key><![CDATA[business_value]]></tag_key>\n" +
+ " <tag_value><![CDATA[12]]></tag_value>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " </vw>\n" +
+ "</views>";
+
+ private static final String LARGE_XML_BEFORE_AND_AFTER = "<views>\n" +
+ " <vw key=\"port1\" def=\"true\">\n" +
+ " <name><![CDATA[port1]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " </vw>\n" +
+ " <vw key=\"port2\" def=\"false\">\n" +
+ " <name><![CDATA[port2]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " <vw-ref><![CDATA[app1-key]]></vw-ref>\n" +
+ " <vw-ref><![CDATA[port1]]></vw-ref>\n" +
+ " </vw>\n" +
+ " <vw key=\"port3\" def=\"false\">\n" +
+ " <name><![CDATA[port3]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " <p>proj1-key</p>\n" +
+ " </vw>\n" +
+ " <vw key=\"port4\" def=\"false\">\n" +
+ " <name><![CDATA[port4]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " </vw>\n" +
+ " <vw key=\"portx\" def=\"false\" root=\"port2\" parent=\"port2\">\n" +
+ " <name><![CDATA[portx]]></name>\n" +
+ " </vw>\n" +
+ " <vw key=\"port5\" def=\"false\">\n" +
+ " <name><![CDATA[port5]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " <tagsAssociation>\n" +
+ " <tag>tag1</tag>\n" +
+ " </tagsAssociation>\n" +
+ " </vw>\n" +
+ " <vw key=\"port6\" def=\"false\">\n" +
+ " <name><![CDATA[port6]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <regexp><![CDATA[.*oj.*]]></regexp>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " </vw>\n" +
+ " <vw key=\"port7\" def=\"false\">\n" +
+ " <name><![CDATA[port7]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <tag_key><![CDATA[business_value]]></tag_key>\n" +
+ " <tag_value><![CDATA[12]]></tag_value>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " </vw>\n" +
+ " <vw key=\"port8\" def=\"false\">\n" +
+ " <name><![CDATA[port7]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <tag_key><![CDATA[business_value]]></tag_key>\n" +
+ " <tag_value><![CDATA[12]]></tag_value>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " </vw>\n" +
+ " <vw key=\"port9\" def=\"false\">\n" +
+ " <name><![CDATA[port7]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <tag_key><![CDATA[business_value]]></tag_key>\n" +
+ " <tag_value><![CDATA[12]]></tag_value>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " </vw>\n" +
+ " <vw key=\"port10\" def=\"false\">\n" +
+ " <name><![CDATA[port7]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <tag_key><![CDATA[business_value]]></tag_key>\n" +
+ " <tag_value><![CDATA[12]]></tag_value>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " </vw>\n" +
+ " <vw key=\"port11\" def=\"false\">\n" +
+ " <name><![CDATA[port7]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <tag_key><![CDATA[business_value]]></tag_key>\n" +
+ " <tag_value><![CDATA[12]]></tag_value>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " </vw>\n" +
+ " <vw key=\"port12\" def=\"false\">\n" +
+ " <name><![CDATA[port7]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <tag_key><![CDATA[business_value]]></tag_key>\n" +
+ " <tag_value><![CDATA[12]]></tag_value>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " </vw>\n" +
+ " <vw key=\"port13\" def=\"false\">\n" +
+ " <name><![CDATA[port7]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <tag_key><![CDATA[business_value]]></tag_key>\n" +
+ " <tag_value><![CDATA[12]]></tag_value>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " </vw>\n" +
+ " <vw key=\"port14\" def=\"false\">\n" +
+ " <name><![CDATA[port7]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <tag_key><![CDATA[business_value]]></tag_key>\n" +
+ " <tag_value><![CDATA[12]]></tag_value>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " </vw>\n" +
+ " <vw key=\"port15\" def=\"false\">\n" +
+ " <name><![CDATA[port7]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <tag_key><![CDATA[business_value]]></tag_key>\n" +
+ " <tag_value><![CDATA[12]]></tag_value>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " </vw>\n" +
+ " <vw key=\"port16\" def=\"false\">\n" +
+ " <name><![CDATA[port7]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <tag_key><![CDATA[business_value]]></tag_key>\n" +
+ " <tag_value><![CDATA[12]]></tag_value>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " </vw>\n" +
+ " <vw key=\"port17\" def=\"false\">\n" +
+ " <name><![CDATA[port7]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <tag_key><![CDATA[business_value]]></tag_key>\n" +
+ " <tag_value><![CDATA[12]]></tag_value>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " </vw>\n" +
+ " <vw key=\"port18\" def=\"false\">\n" +
+ " <name><![CDATA[port7]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <tag_key><![CDATA[business_value]]></tag_key>\n" +
+ " <tag_value><![CDATA[12]]></tag_value>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " </vw>\n" +
+ " <vw key=\"port19\" def=\"false\">\n" +
+ " <name><![CDATA[port7]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <tag_key><![CDATA[business_value]]></tag_key>\n" +
+ " <tag_value><![CDATA[12]]></tag_value>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " </vw>\n" +
+ " <vw key=\"port20\" def=\"false\">\n" +
+ " <name><![CDATA[port7]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <tag_key><![CDATA[business_value]]></tag_key>\n" +
+ " <tag_value><![CDATA[12]]></tag_value>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " </vw>\n" +
+ " <vw key=\"port21\" def=\"false\">\n" +
+ " <name><![CDATA[port7]]></name>\n" +
+ " <desc><![CDATA[]]></desc>\n" +
+ " <tag_key><![CDATA[business_value]]></tag_key>\n" +
+ " <tag_value><![CDATA[12]]></tag_value>\n" +
+ " <qualifier><![CDATA[VW]]></qualifier>\n" +
+ " </vw>\n" +
+ "</views>";
+
+ @Rule
+ public CoreDbTester db = CoreDbTester.createForSchema(MigrateApplicationDefinitionsFromXmlToDbTest.class, "schema.sql");
+
+ private final UuidFactory uuidFactory = UuidFactoryFast.getInstance();
+ private final System2 system2 = new TestSystem2().setNow(NOW);
+ private final MigrationStep underTest = new MigrateApplicationDefinitionsFromXmlToDb(db.database(), uuidFactory, system2);
+
+ @Test
+ public void does_nothing_when_no_views_def_property() throws SQLException {
+ setupProjectsAndApps();
+
+ underTest.execute();
+
+ assertThat(db.countSql("select count(*) from app_projects")).isZero();
+ assertThat(db.countSql("select count(*) from app_branch_project_branch")).isZero();
+ assertThat(db.countSql("select count(*) from internal_properties where kee='views.def'")).isZero();
+ }
+
+ @Test
+ public void does_nothing_when_views_def_property_empty_string() throws SQLException {
+ setupProjectsAndApps();
+ insertViewsDefInternalProperty("");
+
+ underTest.execute();
+
+ assertThat(db.countSql("select count(*) from app_projects")).isZero();
+ assertThat(db.countSql("select count(*) from app_branch_project_branch")).isZero();
+ }
+
+ @Test
+ public void does_nothing_when_views_def_property_empty_views_content() throws SQLException {
+ setupProjectsAndApps();
+ insertViewsDefInternalProperty(EMPTY_XML);
+
+ underTest.execute();
+
+ assertThat(db.countSql("select count(*) from app_projects")).isZero();
+ assertThat(db.countSql("select count(*) from app_branch_project_branch")).isZero();
+ }
+
+ @Test
+ public void throws_ISE_when_views_def_property_does_not_pass_validation() {
+ setupProjectsAndApps();
+ insertViewsDefInternalProperty("abcdefghi");
+
+ assertThatThrownBy(underTest::execute)
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("Failed to migrate views definitions property.");
+
+ assertThat(db.countSql("select count(*) from app_projects")).isZero();
+ assertThat(db.countSql("select count(*) from app_branch_project_branch")).isZero();
+ }
+
+ @Test
+ public void migrates_applications_to_new_tables() throws SQLException {
+ setupProjectsAndApps();
+ insertViewsDefInternalProperty(COMPLEX_XML_BEFORE);
+
+ 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"))
+ .containsExactlyInAnyOrder(
+ tuple(APP_1_UUID, PROJECT_1_UUID),
+ tuple(APP_1_UUID, PROJECT_2_UUID),
+ tuple(APP_2_UUID, PROJECT_1_UUID),
+ tuple(APP_2_UUID, PROJECT_2_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_2_UUID, APP_1_BRANCH_1_UUID, PROJECT_2_BRANCH_1_UUID),
+ tuple(APP_1_UUID, PROJECT_1_UUID, APP_1_BRANCH_2_UUID, PROJECT_1_BRANCH_2_UUID),
+ tuple(APP_1_UUID, PROJECT_2_UUID, APP_1_BRANCH_2_UUID, PROJECT_2_BRANCH_1_UUID),
+ tuple(APP_2_UUID, PROJECT_1_UUID, APP_2_BRANCH_1_UUID, PROJECT_1_BRANCH_1_UUID),
+ tuple(APP_2_UUID, PROJECT_2_UUID, APP_2_BRANCH_1_UUID, PROJECT_2_BRANCH_1_UUID));
+ }
+
+ @Test
+ public void migrates_applications_without_application_branches_to_new_tables() throws SQLException {
+ setupFullProject1();
+ setupProject2();
+ setupApp1WithNoBranches();
+ insertViewsDefInternalProperty(APP_WITH_NO_BRANCHES_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);
+ assertThat(db.select("select application_uuid, project_uuid from app_projects"))
+ .extracting(r -> r.get("APPLICATION_UUID"), r -> r.get("PROJECT_UUID"))
+ .containsExactlyInAnyOrder(
+ tuple(APP_1_UUID, PROJECT_1_UUID),
+ tuple(APP_1_UUID, PROJECT_2_UUID));
+ assertThat(db.countSql("select count(*) from app_branch_project_branch")).isZero();
+ }
+
+ @Test
+ public void migrates_app_with_0_projects_in_views_definition() throws SQLException {
+ setupProjectsAndApps();
+ insertViewsDefInternalProperty(EMPTY_APP_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.countSql("select count(*) from app_projects")).isZero();
+ assertThat(db.countSql("select count(*) from app_branch_project_branch")).isZero();
+ }
+
+ @Test
+ public void skips_apps_that_are_present_in_views_definition_but_not_in_db() throws SQLException {
+ setupFullProject1();
+ setupProject2();
+ setupApp1WithTwoBranches();
+ insertViewsDefInternalProperty(COMPLEX_XML_BEFORE);
+
+ underTest.execute();
+
+ assertThat(db.select("select uuid from projects"))
+ .extracting(r -> r.get("UUID"))
+ .containsExactlyInAnyOrder(PROJECT_1_UUID, PROJECT_2_UUID, APP_1_UUID);
+ assertThat(db.select("select application_uuid, project_uuid from app_projects"))
+ .extracting(r -> r.get("APPLICATION_UUID"), r -> r.get("PROJECT_UUID"))
+ .containsExactlyInAnyOrder(
+ tuple(APP_1_UUID, PROJECT_1_UUID),
+ tuple(APP_1_UUID, PROJECT_2_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_2_UUID, APP_1_BRANCH_1_UUID, PROJECT_2_BRANCH_1_UUID),
+ tuple(APP_1_UUID, PROJECT_1_UUID, APP_1_BRANCH_2_UUID, PROJECT_1_BRANCH_2_UUID),
+ tuple(APP_1_UUID, PROJECT_2_UUID, APP_1_BRANCH_2_UUID, PROJECT_2_BRANCH_1_UUID));
+ }
+
+ @Test
+ public void skips_app_branches_that_are_present_in_views_definition_but_not_in_db() throws SQLException {
+ setupFullProject1();
+ setupProject2();
+ setupApp1WithNoBranches();
+ setupApp2();
+ insertViewsDefInternalProperty(COMPLEX_XML_BEFORE);
+
+ 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"))
+ .containsExactlyInAnyOrder(
+ tuple(APP_1_UUID, PROJECT_1_UUID),
+ tuple(APP_1_UUID, PROJECT_2_UUID),
+ tuple(APP_2_UUID, PROJECT_1_UUID),
+ tuple(APP_2_UUID, PROJECT_2_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_2_UUID, PROJECT_1_UUID, APP_2_BRANCH_1_UUID, PROJECT_1_BRANCH_1_UUID),
+ tuple(APP_2_UUID, PROJECT_2_UUID, APP_2_BRANCH_1_UUID, PROJECT_2_BRANCH_1_UUID));
+ }
+
+ @Test
+ public void skips_projects_that_are_present_in_apps_views_definitions_but_not_in_db() throws SQLException {
+ setupPartialProject1();
+ setupApp1WithTwoBranches();
+ setupApp2();
+ insertViewsDefInternalProperty(COMPLEX_XML_BEFORE);
+
+ underTest.execute();
+
+ assertThat(db.select("select uuid from projects"))
+ .extracting(r -> r.get("UUID"))
+ .containsExactlyInAnyOrder(PROJECT_1_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"))
+ .containsExactlyInAnyOrder(
+ tuple(APP_1_UUID, PROJECT_1_UUID),
+ tuple(APP_2_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_2_UUID, PROJECT_1_UUID, APP_2_BRANCH_1_UUID, PROJECT_1_BRANCH_1_UUID));
+ }
+
+ @Test
+ public void skips_projects_branches_that_are_present_in_apps_views_definitions_but_not_in_db() throws SQLException {
+ setupPartialProject1();
+ setupProject2();
+ setupApp1WithTwoBranches();
+ setupApp2();
+ insertViewsDefInternalProperty(COMPLEX_XML_BEFORE);
+
+ 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"))
+ .containsExactlyInAnyOrder(
+ tuple(APP_1_UUID, PROJECT_1_UUID),
+ tuple(APP_1_UUID, PROJECT_2_UUID),
+ tuple(APP_2_UUID, PROJECT_1_UUID),
+ tuple(APP_2_UUID, PROJECT_2_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_2_UUID, APP_1_BRANCH_1_UUID, PROJECT_2_BRANCH_1_UUID),
+ tuple(APP_1_UUID, PROJECT_2_UUID, APP_1_BRANCH_2_UUID, PROJECT_2_BRANCH_1_UUID),
+ tuple(APP_2_UUID, PROJECT_1_UUID, APP_2_BRANCH_1_UUID, PROJECT_1_BRANCH_1_UUID),
+ tuple(APP_2_UUID, PROJECT_2_UUID, APP_2_BRANCH_1_UUID, PROJECT_2_BRANCH_1_UUID));
+ }
+
+ @Test
+ public void removes_application_definitions_from_xml() throws SQLException {
+ setupProjectsAndApps();
+ insertViewsDefInternalProperty(COMPLEX_XML_BEFORE);
+
+ underTest.execute();
+
+ assertThat(db.countSql("select count(*) from internal_properties where kee='views.def'")).isOne();
+ assertViewsXmlDefinitionSimilar(COMPLEX_XML_AFTER, false);
+ }
+
+ @Test
+ public void removes_application_definitions_from_large_xmls() throws SQLException {
+ setupProjectsAndApps();
+ insertViewsDefInternalProperty(LARGE_XML_BEFORE_AND_AFTER);
+
+ underTest.execute();
+
+ assertThat(db.countSql("select count(*) from internal_properties where kee='views.def'")).isOne();
+ assertViewsXmlDefinitionSimilar(LARGE_XML_BEFORE_AND_AFTER, true);
+ }
+
+ @Test
+ public void does_not_change_the_xml_if_there_are_no_application_definitions() throws SQLException {
+ setupProjectsAndApps();
+ insertViewsDefInternalProperty(EMPTY_XML);
+
+ underTest.execute();
+
+ assertThat(db.countSql("select count(*) from internal_properties where kee='views.def'")).isOne();
+ assertViewsXmlDefinitionSimilar(EMPTY_XML, false);
+ }
+
+ private void setupProjectsAndApps() {
+ setupFullProject1();
+ setupProject2();
+ setupApp1WithTwoBranches();
+ setupApp2();
+ }
+
+ private void setupFullProject1() {
+ setupPartialProject1();
+ insertBranch(PROJECT_1_BRANCH_2_UUID, PROJECT_1_UUID, "proj1-branch-2");
+ }
+
+ private void setupPartialProject1() {
+ insertProject(PROJECT_1_UUID, "proj1-key", QUALIFIER_PROJECT);
+ insertBranch(PROJECT_1_MASTER_BRANCH_UUID, PROJECT_1_UUID, "master");
+ insertBranch(PROJECT_1_BRANCH_1_UUID, PROJECT_1_UUID, "proj1-branch-1");
+ }
+
+ private void setupProject2() {
+ insertProject(PROJECT_2_UUID, "proj2-key", QUALIFIER_PROJECT);
+ insertBranch(PROJECT_2_MASTER_BRANCH_UUID, PROJECT_2_UUID, "master");
+ insertBranch(PROJECT_2_BRANCH_1_UUID, PROJECT_2_UUID, "m1");
+ }
+
+ private void setupApp1WithNoBranches() {
+ insertProject(APP_1_UUID, "app1-key", QUALIFIER_APP);
+ insertBranch(APP_1_MASTER_BRANCH_UUID, APP_1_UUID, "master");
+ }
+
+ private void setupApp1WithOneBranch() {
+ setupApp1WithNoBranches();
+ insertBranch(APP_1_BRANCH_1_UUID, APP_1_UUID, "app1-branch1");
+ }
+
+ private void setupApp1WithTwoBranches() {
+ setupApp1WithOneBranch();
+ insertBranch(APP_1_BRANCH_2_UUID, APP_1_UUID, "app1-branch2");
+ }
+
+ private void setupApp2() {
+ insertProject(APP_2_UUID, "app2-key", QUALIFIER_APP);
+ insertBranch(APP_2_MASTER_BRANCH_UUID, APP_2_UUID, "master");
+ insertBranch(APP_2_BRANCH_1_UUID, APP_2_UUID, "m1");
+ }
+
+ private void insertViewsDefInternalProperty(@Nullable String xml) {
+ String valueColumn = "text_value";
+ if (xml != null && xml.length() > TEXT_VALUE_MAX_LENGTH) {
+ valueColumn = "clob_value";
+ }
+
+ db.executeInsert("internal_properties",
+ "kee", "views.def",
+ "is_empty", "false",
+ valueColumn, xml,
+ "created_at", system2.now());
+ }
+
+ private void insertProject(String uuid, String key, String qualifier) {
+ db.executeInsert("PROJECTS",
+ "UUID", uuid,
+ "KEE", key,
+ "QUALIFIER", qualifier,
+ "ORGANIZATION_UUID", uuid + "-key",
+ "TAGS", "tag1",
+ "PRIVATE", Boolean.toString(false),
+ "UPDATED_AT", System2.INSTANCE.now());
+ }
+
+ private void insertBranch(String uuid, String projectUuid, String key) {
+ db.executeInsert(
+ "PROJECT_BRANCHES",
+ "UUID", uuid,
+ "PROJECT_UUID", projectUuid,
+ "KEE", key,
+ "BRANCH_TYPE", "BRANCH",
+ "MERGE_BRANCH_UUID", null,
+ "CREATED_AT", System2.INSTANCE.now(),
+ "UPDATED_AT", System2.INSTANCE.now(),
+ "NEED_ISSUE_SYNC", Boolean.toString(false));
+ }
+
+ private void assertViewsXmlDefinitionSimilar(final String expectedValue, final boolean expectClob) {
+ Map<String, Object> result = db.selectFirst("select text_value, clob_value from internal_properties where kee='views.def'");
+ String textValue = (String) result.get("TEXT_VALUE");
+ String clobValue = (String) result.get("CLOB_VALUE");
+
+ String existingValue;
+ if (expectClob) {
+ existingValue = clobValue;
+ assertThat(textValue).isNull();
+ } else {
+ existingValue = textValue;
+ assertThat(clobValue).isNull();
+ }
+
+ Diff diff = DiffBuilder
+ .compare(Input.fromString(expectedValue))
+ .withTest(Input.fromString(existingValue))
+ .ignoreWhitespace()
+ .ignoreComments()
+ .checkForSimilar()
+ .build();
+ assertThat(diff.getDifferences())
+ .as(expectedValue)
+ .isEmpty();
+ }
+}
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/AddIndexToApplicationBranchProjsTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/AddIndexToApplicationBranchProjsTest/schema.sql
new file mode 100644
index 00000000000..845d2fd0b18
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/AddIndexToApplicationBranchProjsTest/schema.sql
@@ -0,0 +1,9 @@
+CREATE TABLE "APP_BRANCH_PROJECT_BRANCH"(
+ "UUID" VARCHAR(40) NOT NULL,
+ "APPLICATION_UUID" VARCHAR(40) NOT NULL,
+ "APPLICATION_BRANCH_UUID" VARCHAR(40) NOT NULL,
+ "PROJECT_UUID" VARCHAR(40) NOT NULL,
+ "PROJECT_BRANCH_UUID" VARCHAR(40) NOT NULL,
+ "CREATED_AT" BIGINT NOT NULL
+);
+ALTER TABLE "APP_BRANCH_PROJECT_BRANCH" ADD CONSTRAINT "PK_APP_BRANCH_PROJECT_BRANCH" PRIMARY KEY("UUID");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/AddIndexToApplicationProjectsTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/AddIndexToApplicationProjectsTest/schema.sql
new file mode 100644
index 00000000000..7c2627968a6
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/AddIndexToApplicationProjectsTest/schema.sql
@@ -0,0 +1,7 @@
+CREATE TABLE "APP_PROJECTS"(
+ "UUID" VARCHAR(40) NOT NULL,
+ "APPLICATION_UUID" VARCHAR(40) NOT NULL,
+ "PROJECT_UUID" VARCHAR(40) NOT NULL,
+ "CREATED_AT" BIGINT NOT NULL
+);
+ALTER TABLE "APP_PROJECTS" ADD CONSTRAINT "PK_APP_PROJECTS" PRIMARY KEY("UUID");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/AddPkToApplicationBranchProjsTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/AddPkToApplicationBranchProjsTest/schema.sql
new file mode 100644
index 00000000000..3270865158a
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/AddPkToApplicationBranchProjsTest/schema.sql
@@ -0,0 +1,8 @@
+CREATE TABLE "APP_BRANCH_PROJECT_BRANCH"(
+ "UUID" VARCHAR(40) NOT NULL,
+ "APPLICATION_UUID" VARCHAR(40) NOT NULL,
+ "APPLICATION_BRANCH_UUID" VARCHAR(40) NOT NULL,
+ "PROJECT_UUID" VARCHAR(40) NOT NULL,
+ "PROJECT_BRANCH_UUID" VARCHAR(40) NOT NULL,
+ "CREATED_AT" BIGINT NOT NULL
+);
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/AddPkToApplicationProjectsTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/AddPkToApplicationProjectsTest/schema.sql
new file mode 100644
index 00000000000..3cb13c2066a
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/AddPkToApplicationProjectsTest/schema.sql
@@ -0,0 +1,6 @@
+CREATE TABLE "APP_PROJECTS"(
+ "UUID" VARCHAR(40) NOT NULL,
+ "APPLICATION_UUID" VARCHAR(40) NOT NULL,
+ "PROJECT_UUID" VARCHAR(40) NOT NULL,
+ "CREATED_AT" BIGINT NOT NULL
+);
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/MigrateApplicationDefinitionsFromXmlToDbTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/MigrateApplicationDefinitionsFromXmlToDbTest/schema.sql
new file mode 100644
index 00000000000..dc3010a47b5
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v86/MigrateApplicationDefinitionsFromXmlToDbTest/schema.sql
@@ -0,0 +1,66 @@
+CREATE TABLE "PROJECTS"(
+ "UUID" VARCHAR(40) NOT NULL,
+ "KEE" VARCHAR(400) NOT NULL,
+ "QUALIFIER" VARCHAR(10) NOT NULL,
+ "ORGANIZATION_UUID" VARCHAR(40) NOT NULL,
+ "NAME" VARCHAR(2000),
+ "DESCRIPTION" VARCHAR(2000),
+ "PRIVATE" BOOLEAN NOT NULL,
+ "TAGS" VARCHAR(500),
+ "CREATED_AT" BIGINT,
+ "UPDATED_AT" BIGINT NOT NULL
+);
+ALTER TABLE "PROJECTS" ADD CONSTRAINT "PK_NEW_PROJECTS" PRIMARY KEY("UUID");
+CREATE UNIQUE INDEX "UNIQ_PROJECTS_KEE" ON "PROJECTS"("KEE");
+CREATE INDEX "IDX_QUALIFIER" ON "PROJECTS"("QUALIFIER");
+
+CREATE TABLE "PROJECT_BRANCHES"(
+ "UUID" VARCHAR(50) NOT NULL,
+ "PROJECT_UUID" VARCHAR(50) NOT NULL,
+ "KEE" VARCHAR(255) NOT NULL,
+ "BRANCH_TYPE" VARCHAR(12) NOT NULL,
+ "MERGE_BRANCH_UUID" VARCHAR(50),
+ "PULL_REQUEST_BINARY" BLOB,
+ "MANUAL_BASELINE_ANALYSIS_UUID" VARCHAR(40),
+ "CREATED_AT" BIGINT NOT NULL,
+ "UPDATED_AT" BIGINT NOT NULL,
+ "EXCLUDE_FROM_PURGE" BOOLEAN DEFAULT FALSE NOT NULL,
+ "NEED_ISSUE_SYNC" BOOLEAN NOT NULL
+);
+ALTER TABLE "PROJECT_BRANCHES" ADD CONSTRAINT "PK_PROJECT_BRANCHES" PRIMARY KEY("UUID");
+CREATE UNIQUE INDEX "UNIQ_PROJECT_BRANCHES" ON "PROJECT_BRANCHES"("BRANCH_TYPE", "PROJECT_UUID", "KEE");
+
+CREATE TABLE "INTERNAL_PROPERTIES"(
+ "KEE" VARCHAR(20) NOT NULL,
+ "IS_EMPTY" BOOLEAN NOT NULL,
+ "TEXT_VALUE" VARCHAR(4000),
+ "CLOB_VALUE" CLOB,
+ "CREATED_AT" BIGINT NOT NULL
+);
+ALTER TABLE "INTERNAL_PROPERTIES" ADD CONSTRAINT "PK_INTERNAL_PROPERTIES" PRIMARY KEY("KEE");
+
+CREATE TABLE "APP_BRANCH_PROJECT_BRANCH"(
+ "UUID" VARCHAR(40) NOT NULL,
+ "APPLICATION_UUID" VARCHAR(40) NOT NULL,
+ "APPLICATION_BRANCH_UUID" VARCHAR(40) NOT NULL,
+ "PROJECT_UUID" VARCHAR(40) NOT NULL,
+ "PROJECT_BRANCH_UUID" VARCHAR(40) NOT NULL,
+ "CREATED_AT" BIGINT NOT NULL
+);
+ALTER TABLE "APP_BRANCH_PROJECT_BRANCH" ADD CONSTRAINT "PK_APP_BRANCH_PROJECT_BRANCH" PRIMARY KEY("UUID");
+CREATE UNIQUE INDEX "UNIQ_APP_BRANCH_PROJ" ON "APP_BRANCH_PROJECT_BRANCH"("APPLICATION_BRANCH_UUID", "PROJECT_BRANCH_UUID");
+CREATE INDEX "IDX_ABPB_APP_UUID" ON "APP_BRANCH_PROJECT_BRANCH"("APPLICATION_UUID");
+CREATE INDEX "IDX_ABPB_APP_BRANCH_UUID" ON "APP_BRANCH_PROJECT_BRANCH"("APPLICATION_BRANCH_UUID");
+CREATE INDEX "IDX_ABPB_PROJ_UUID" ON "APP_BRANCH_PROJECT_BRANCH"("PROJECT_UUID");
+CREATE INDEX "IDX_ABPB_PROJ_BRANCH_UUID" ON "APP_BRANCH_PROJECT_BRANCH"("PROJECT_BRANCH_UUID");
+
+CREATE TABLE "APP_PROJECTS"(
+ "UUID" VARCHAR(40) NOT NULL,
+ "APPLICATION_UUID" VARCHAR(40) NOT NULL,
+ "PROJECT_UUID" VARCHAR(40) NOT NULL,
+ "CREATED_AT" BIGINT NOT NULL
+);
+ALTER TABLE "APP_PROJECTS" ADD CONSTRAINT "PK_APP_PROJECTS" PRIMARY KEY("UUID");
+CREATE UNIQUE INDEX "UNIQ_APP_PROJECTS" ON "APP_PROJECTS"("APPLICATION_UUID", "PROJECT_UUID");
+CREATE INDEX "IDX_APP_PROJ_APPLICATION_UUID" ON "APP_PROJECTS"("APPLICATION_UUID");
+CREATE INDEX "IDX_APP_PROJ_PROJECT_UUID" ON "APP_PROJECTS"("PROJECT_UUID");
diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/project/Visibility.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/project/Visibility.java
index 837fed756cf..3c60ff4621d 100644
--- a/server/sonar-webserver-api/src/main/java/org/sonar/server/project/Visibility.java
+++ b/server/sonar-webserver-api/src/main/java/org/sonar/server/project/Visibility.java
@@ -43,7 +43,7 @@ public enum Visibility {
return label;
}
- boolean isPrivate() {
+ public boolean isPrivate() {
return isPrivate;
}
diff --git a/server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/tester/UserSessionRule.java b/server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/tester/UserSessionRule.java
index 3fa2dc6b13f..05c58ae67e0 100644
--- a/server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/tester/UserSessionRule.java
+++ b/server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/tester/UserSessionRule.java
@@ -187,12 +187,17 @@ public class UserSessionRule implements TestRule, UserSession {
return this;
}
+ public UserSessionRule registerProjects(ProjectDto... projectDtos) {
+ ensureAbstractMockUserSession().registerProjects(projectDtos);
+ return this;
+ }
+
public UserSessionRule addProjectPermission(String projectPermission, ComponentDto... components) {
ensureAbstractMockUserSession().addProjectPermission(projectPermission, components);
return this;
}
- public UserSessionRule addProjectPermission(String projectPermission, ProjectDto projectDto) {
+ public UserSessionRule addProjectPermission(String projectPermission, ProjectDto... projectDto) {
ensureAbstractMockUserSession().addProjectPermission(projectPermission, projectDto);
return this;
}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ComponentCleanerService.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ComponentCleanerService.java
index acadb402952..ec1c64e5f04 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ComponentCleanerService.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ComponentCleanerService.java
@@ -61,7 +61,6 @@ public class ComponentCleanerService {
}
public void deleteBranch(DbSession dbSession, BranchDto branch) {
- // TODO: detect if other branches depend on it?
dbClient.purgeDao().deleteBranch(dbSession, branch.getUuid());
projectIndexers.commitAndIndexBranches(dbSession, singletonList(branch), PROJECT_DELETION);
}
@@ -72,6 +71,12 @@ public class ComponentCleanerService {
projectIndexers.commitAndIndexProjects(dbSession, singletonList(project), PROJECT_DELETION);
}
+ public void deleteApplication(DbSession dbSession, ProjectDto application) {
+ dbClient.purgeDao().deleteProject(dbSession, application.getUuid());
+ dbClient.userDao().cleanHomepage(dbSession, application);
+ projectIndexers.commitAndIndexProjects(dbSession, singletonList(application), PROJECT_DELETION);
+ }
+
public void delete(DbSession dbSession, ComponentDto project) {
checkArgument(!hasNotProjectScope(project) && !isNotDeletable(project) && project.getMainBranchProjectUuid() == null, "Only projects can be deleted");
dbClient.purgeDao().deleteProject(dbSession, project.uuid());
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ComponentUpdater.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ComponentUpdater.java
index 3043090204d..dc0fce4b712 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ComponentUpdater.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ComponentUpdater.java
@@ -29,7 +29,7 @@ import org.sonar.api.resources.Qualifiers;
import org.sonar.api.resources.Scopes;
import org.sonar.api.utils.System2;
import org.sonar.core.i18n.I18n;
-import org.sonar.core.util.Uuids;
+import org.sonar.core.util.UuidFactory;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.BranchDto;
@@ -57,16 +57,18 @@ public class ComponentUpdater {
private final PermissionTemplateService permissionTemplateService;
private final FavoriteUpdater favoriteUpdater;
private final ProjectIndexers projectIndexers;
+ private final UuidFactory uuidFactory;
public ComponentUpdater(DbClient dbClient, I18n i18n, System2 system2,
PermissionTemplateService permissionTemplateService, FavoriteUpdater favoriteUpdater,
- ProjectIndexers projectIndexers) {
+ ProjectIndexers projectIndexers, UuidFactory uuidFactory) {
this.dbClient = dbClient;
this.i18n = i18n;
this.system2 = system2;
this.permissionTemplateService = permissionTemplateService;
this.favoriteUpdater = favoriteUpdater;
this.projectIndexers = projectIndexers;
+ this.uuidFactory = uuidFactory;
}
/**
@@ -105,7 +107,7 @@ public class ComponentUpdater {
"Could not create %s, key already exists: %s", getQualifierToDisplay(newComponent.qualifier()), newComponent.key());
long now = system2.now();
- String uuid = Uuids.create();
+ String uuid = uuidFactory.create();
ComponentDto component = new ComponentDto()
.setOrganizationUuid(newComponent.getOrganizationUuid())
.setUuid(uuid)
@@ -116,6 +118,7 @@ public class ComponentUpdater {
.setProjectUuid(uuid)
.setDbKey(newComponent.key())
.setName(newComponent.name())
+ .setDescription(newComponent.description())
.setLongName(newComponent.name())
.setScope(Scopes.PROJECT)
.setQualifier(newComponent.qualifier())
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/NewComponent.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/NewComponent.java
index 99db6475cca..0208be489de 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/NewComponent.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/NewComponent.java
@@ -19,6 +19,8 @@
*/
package org.sonar.server.component;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import static java.util.Objects.requireNonNull;
@@ -33,6 +35,7 @@ public class NewComponent {
private final String key;
private final String qualifier;
private final String name;
+ private final String description;
private final boolean isPrivate;
private NewComponent(NewComponent.Builder builder) {
@@ -41,6 +44,7 @@ public class NewComponent {
this.qualifier = builder.qualifier;
this.name = builder.name;
this.isPrivate = builder.isPrivate;
+ this.description = builder.description;
}
public static Builder newComponentBuilder() {
@@ -67,7 +71,13 @@ public class NewComponent {
return isPrivate;
}
+ @CheckForNull
+ public String description() {
+ return description;
+ }
+
public static class Builder {
+ private String description;
private String organizationUuid;
private String key;
private String qualifier = PROJECT;
@@ -103,6 +113,11 @@ public class NewComponent {
return this;
}
+ public Builder setDescription(@Nullable String description) {
+ this.description = description;
+ return this;
+ }
+
public NewComponent build() {
requireNonNull(organizationUuid, "organization uuid can't be null");
checkComponentKey(key);
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/queue/ReportSubmitterTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/queue/ReportSubmitterTest.java
index c7468e0623a..f6f804a8088 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/queue/ReportSubmitterTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/ce/queue/ReportSubmitterTest.java
@@ -34,6 +34,7 @@ import org.sonar.ce.queue.CeQueue;
import org.sonar.ce.queue.CeQueueImpl;
import org.sonar.ce.queue.CeTaskSubmit;
import org.sonar.core.i18n.I18n;
+import org.sonar.core.util.SequenceUuidFactory;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.ce.CeTaskTypes;
@@ -92,7 +93,7 @@ public class ReportSubmitterTest {
private TestProjectIndexers projectIndexers = new TestProjectIndexers();
private PermissionTemplateService permissionTemplateService = mock(PermissionTemplateService.class);
private ComponentUpdater componentUpdater = new ComponentUpdater(db.getDbClient(), mock(I18n.class), mock(System2.class), permissionTemplateService,
- new FavoriteUpdater(db.getDbClient()), projectIndexers);
+ new FavoriteUpdater(db.getDbClient()), projectIndexers, new SequenceUuidFactory());
private BranchSupport ossEditionBranchSupport = new BranchSupport();
private ReportSubmitter underTest = new ReportSubmitter(queue, userSession, componentUpdater, permissionTemplateService, db.getDbClient(), ossEditionBranchSupport);
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/component/ComponentCleanerServiceTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/component/ComponentCleanerServiceTest.java
index 74645ba954b..da3d4384d56 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/component/ComponentCleanerServiceTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/component/ComponentCleanerServiceTest.java
@@ -49,7 +49,7 @@ import static org.sonar.server.es.ProjectIndexer.Cause.PROJECT_DELETION;
public class ComponentCleanerServiceTest {
- private System2 system2 = System2.INSTANCE;
+ private final System2 system2 = System2.INSTANCE;
@Rule
public DbTester db = DbTester.create(system2);
@@ -57,16 +57,16 @@ public class ComponentCleanerServiceTest {
@Rule
public ExpectedException expectedException = ExpectedException.none();
- private DbClient dbClient = db.getDbClient();
- private DbSession dbSession = db.getSession();
- private TestProjectIndexers projectIndexers = new TestProjectIndexers();
- private ResourceTypes mockResourceTypes = mock(ResourceTypes.class);
- private ComponentCleanerService underTest = new ComponentCleanerService(dbClient, mockResourceTypes, projectIndexers);
+ private final DbClient dbClient = db.getDbClient();
+ private final DbSession dbSession = db.getSession();
+ private final TestProjectIndexers projectIndexers = new TestProjectIndexers();
+ private final ResourceTypes mockResourceTypes = mock(ResourceTypes.class);
+ private final ComponentCleanerService underTest = new ComponentCleanerService(dbClient, mockResourceTypes, projectIndexers);
@Test
public void delete_project_from_db_and_index() {
- DbData data1 = insertData();
- DbData data2 = insertData();
+ DbData data1 = insertProjectData();
+ DbData data2 = insertProjectData();
underTest.delete(dbSession, data1.project);
@@ -76,9 +76,9 @@ public class ComponentCleanerServiceTest {
@Test
public void delete_list_of_projects_from_db_and_index() {
- DbData data1 = insertData();
- DbData data2 = insertData();
- DbData data3 = insertData();
+ DbData data1 = insertProjectData();
+ DbData data2 = insertProjectData();
+ DbData data3 = insertProjectData();
underTest.delete(dbSession, asList(data1.project, data2.project));
dbSession.commit();
@@ -89,10 +89,34 @@ public class ComponentCleanerServiceTest {
}
@Test
+ public void delete_application_from_db_and_index() {
+ DbData data1 = insertProjectData();
+ DbData data2 = insertProjectData();
+ DbData data3 = insertProjectData();
+ ProjectDto app1 = insertApplication(data2.project);
+ ProjectDto app2 = insertApplication(data3.project);
+
+ underTest.deleteApplication(dbSession, app1);
+ dbSession.commit();
+
+ assertProjectOrAppExists(app1, false);
+ assertProjectOrAppExists(app2, true);
+ assertExists(data1);
+ assertExists(data2);
+ assertExists(data3);
+ }
+
+ private ProjectDto insertApplication(ProjectDto project) {
+ ProjectDto app = db.components().insertPublicApplicationDto();
+ db.components().addApplicationProject(app, project);
+ return app;
+ }
+
+ @Test
public void delete_branch() {
- DbData data1 = insertData();
- DbData data2 = insertData();
- DbData data3 = insertData();
+ DbData data1 = insertProjectData();
+ DbData data2 = insertProjectData();
+ DbData data3 = insertProjectData();
underTest.deleteBranch(dbSession, data1.branch);
dbSession.commit();
@@ -139,15 +163,15 @@ public class ComponentCleanerServiceTest {
underTest.delete(dbSession, file);
}
- private DbData insertData() {
+ private DbData insertProjectData() {
OrganizationDto organization = db.organizations().insert();
ComponentDto componentDto = db.components().insertPublicProject(organization);
ProjectDto project = dbClient.projectDao().selectByUuid(dbSession, componentDto.uuid()).get();
BranchDto branch = dbClient.branchDao().selectByUuid(dbSession, project.getUuid()).get();
- ComponentDto component = dbClient.componentDao().selectByKey(dbSession, project.getKey()).get();
+
RuleDefinitionDto rule = db.rules().insert();
- IssueDto issue = db.issues().insert(rule, project, component);
- SnapshotDto analysis = db.components().insertSnapshot(component);
+ IssueDto issue = db.issues().insert(rule, project, componentDto);
+ SnapshotDto analysis = db.components().insertSnapshot(componentDto);
mockResourceTypeAsValidProject();
return new DbData(project, branch, analysis, issue);
}
@@ -169,12 +193,17 @@ public class ComponentCleanerServiceTest {
}
private void assertDataInDb(DbData data, boolean exists) {
- assertThat(dbClient.componentDao().selectByUuid(dbSession, data.project.getUuid()).isPresent()).isEqualTo(exists);
- assertThat(dbClient.branchDao().selectByUuid(dbSession, data.branch.getUuid()).isPresent()).isEqualTo(exists);
+ assertProjectOrAppExists(data.project, exists);
assertThat(dbClient.snapshotDao().selectByUuid(dbSession, data.snapshot.getUuid()).isPresent()).isEqualTo(exists);
assertThat(dbClient.issueDao().selectByKey(dbSession, data.issue.getKey()).isPresent()).isEqualTo(exists);
}
+ private void assertProjectOrAppExists(ProjectDto appOrProject, boolean exists) {
+ assertThat(dbClient.projectDao().selectByUuid(dbSession, appOrProject.getUuid()).isPresent()).isEqualTo(exists);
+ assertThat(dbClient.componentDao().selectByUuid(dbSession, appOrProject.getUuid()).isPresent()).isEqualTo(exists);
+ assertThat(dbClient.branchDao().selectByUuid(dbSession, appOrProject.getUuid()).isPresent()).isEqualTo(exists);
+ }
+
private static class DbData {
final ProjectDto project;
final BranchDto branch;
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/component/ComponentUpdaterTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/component/ComponentUpdaterTest.java
index 915b6adea8c..7fa9a4617bd 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/component/ComponentUpdaterTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/component/ComponentUpdaterTest.java
@@ -26,6 +26,7 @@ import org.junit.rules.ExpectedException;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.resources.Scopes;
import org.sonar.api.utils.System2;
+import org.sonar.core.util.SequenceUuidFactory;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.component.BranchDto;
@@ -70,7 +71,7 @@ public class ComponentUpdaterTest {
private ComponentUpdater underTest = new ComponentUpdater(db.getDbClient(), i18n, system2,
permissionTemplateService,
new FavoriteUpdater(db.getDbClient()),
- projectIndexers);
+ projectIndexers, new SequenceUuidFactory());
@Test
public void persist_and_index_when_creating_project() {
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/project/ws/CreateActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/project/ws/CreateActionTest.java
index ebc7449281e..369ff2ecbcb 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/project/ws/CreateActionTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/project/ws/CreateActionTest.java
@@ -26,6 +26,7 @@ import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.System2;
+import org.sonar.core.util.SequenceUuidFactory;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
@@ -92,7 +93,7 @@ public class CreateActionTest {
new ProjectsWsSupport(db.getDbClient(), defaultOrganizationProvider, billingValidations),
db.getDbClient(), userSession,
new ComponentUpdater(db.getDbClient(), i18n, system2, permissionTemplateService, new FavoriteUpdater(db.getDbClient()),
- projectIndexers)));
+ projectIndexers, new SequenceUuidFactory())));
@Test
public void create_project() {