public void addSteps(MigrationStepRegistry registry) {
registry
.add(3100, "Create ALM_SETTINGS table", CreateAlmSettingsTable.class)
- .add(3101, "Create PROJECT_ALM_SETTINGS table", CreateProjectAlmSettingsTable.class);
+ .add(3101, "Create PROJECT_ALM_SETTINGS table", CreateProjectAlmSettingsTable.class)
+ .add(3102, "Migrate GitHub ALM settings from PROPERTIES to ALM_SETTINGS tables", MigrateGithubAlmSettings.class)
+ ;
}
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration.version.v81;
+
+import com.google.common.collect.ImmutableSet;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+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.MassUpdate;
+import org.sonar.server.platform.db.migration.step.Select;
+import org.sonar.server.platform.db.migration.step.SqlStatement;
+import org.sonar.server.platform.db.migration.step.Upsert;
+
+public class MigrateGithubAlmSettings extends DataChange {
+
+ private static final String PROVIDER = "sonar.pullrequest.provider";
+
+ // Global settings
+ private static final String GITHUB_ENDPOINT = "sonar.pullrequest.github.endpoint";
+ private static final String GITHUB_APP_ID = "sonar.alm.github.app.id";
+ private static final String GITHUB_APP_PRIVATE_KEY = "sonar.alm.github.app.privateKeyContent.secured";
+ private static final String GITHUB_APP_NAME = "sonar.alm.github.app.name";
+ // Project setting
+ private static final String GITHUB_REPOSITORY = "sonar.pullrequest.github.repository";
+
+ private static final Set<String> MANDATORY_GLOBAL_KEYS = ImmutableSet.of(GITHUB_ENDPOINT, GITHUB_APP_ID, GITHUB_APP_PRIVATE_KEY);
+
+ private static final String PROVIDER_VALUE = "GitHub";
+
+ private static final String ALM_SETTING_GITHUB_ID = "github";
+
+ private static final String INSERT_SQL = "insert into project_alm_settings (uuid, project_uuid, alm_setting_uuid, alm_repo, created_at, updated_at) values (?, ?, ?, ?, ?, ?)";
+ private static final String DELETE_SQL = "delete from properties where prop_key = ? and resource_id = ?";
+
+ private final UuidFactory uuidFactory;
+ private final System2 system2;
+
+ public MigrateGithubAlmSettings(Database db, UuidFactory uuidFactory, System2 system2) {
+ super(db);
+ this.uuidFactory = uuidFactory;
+ this.system2 = system2;
+ }
+
+ @Override
+ protected void execute(Context context) throws SQLException {
+ Map<String, Property> globalPropertiesByKey = loadGlobalProperties(context);
+
+ if (globalPropertiesByKey.keySet().containsAll(MANDATORY_GLOBAL_KEYS)) {
+ String gitHubAlmSettingUuid = loadOrInsertAlmSetting(context, globalPropertiesByKey);
+ Property globalProviderProperty = globalPropertiesByKey.getOrDefault(PROVIDER, null);
+ String globalProvider = globalProviderProperty != null ? globalProviderProperty.getValue() : null;
+
+ insertProjectAlmSettings(context, globalProvider, gitHubAlmSettingUuid);
+ }
+
+ context.prepareUpsert("delete from properties where prop_key in (?, ?, ?, ?, ?)")
+ .setString(1, GITHUB_ENDPOINT)
+ .setString(2, GITHUB_APP_ID)
+ .setString(3, GITHUB_APP_PRIVATE_KEY)
+ .setString(4, GITHUB_APP_NAME)
+ .setString(5, GITHUB_REPOSITORY)
+ .execute()
+ .commit();
+ }
+
+ private static Map<String, Property> loadGlobalProperties(Context context) throws SQLException {
+ return context
+ .prepareSelect("select prop_key, text_value from properties where prop_key in (?, ?, ?, ?, ?) " +
+ "and resource_id is null " +
+ "and text_value is not null ")
+ .setString(1, PROVIDER)
+ .setString(2, GITHUB_ENDPOINT)
+ .setString(3, GITHUB_APP_ID)
+ .setString(4, GITHUB_APP_PRIVATE_KEY)
+ .setString(5, GITHUB_APP_NAME)
+ .list(Property::new)
+ .stream()
+ .collect(Collectors.toMap(Property::getKey, Function.identity()));
+ }
+
+ private String loadOrInsertAlmSetting(Context context, Map<String, Property> globalPropertiesByKey) throws SQLException {
+ String gitHubAlmSettingUuid = loadAlmSetting(context);
+ if (gitHubAlmSettingUuid != null) {
+ return gitHubAlmSettingUuid;
+ }
+ return insertAlmSetting(context, globalPropertiesByKey);
+ }
+
+ @CheckForNull
+ private static String loadAlmSetting(Context context) throws SQLException {
+ List<String> list = context.prepareSelect("select uuid from alm_settings where alm_id=?")
+ .setString(1, ALM_SETTING_GITHUB_ID)
+ .list(row -> row.getString(1));
+ if (list.isEmpty()) {
+ return null;
+ }
+ return list.get(0);
+ }
+
+ private String insertAlmSetting(Context context, Map<String, Property> globalPropertiesByKey) throws SQLException {
+ String gitHubAlmSettingUuid = uuidFactory.create();
+ Property appName = globalPropertiesByKey.get(GITHUB_APP_NAME);
+ context.prepareUpsert("insert into alm_settings (uuid, alm_id, kee, url, app_id, private_key, updated_at, created_at) values (?, ?, ?, ?, ?, ?, ?, ?)")
+ .setString(1, gitHubAlmSettingUuid)
+ .setString(2, ALM_SETTING_GITHUB_ID)
+ .setString(3, appName == null ? PROVIDER_VALUE : appName.getValue())
+ .setString(4, globalPropertiesByKey.get(GITHUB_ENDPOINT).getValue())
+ .setString(5, globalPropertiesByKey.get(GITHUB_APP_ID).getValue())
+ .setString(6, globalPropertiesByKey.get(GITHUB_APP_PRIVATE_KEY).getValue())
+ .setLong(7, system2.now())
+ .setLong(8, system2.now())
+ .execute()
+ .commit();
+ return gitHubAlmSettingUuid;
+ }
+
+ private void insertProjectAlmSettings(Context context, @Nullable String globalProvider, String almSettingUuid) throws SQLException {
+ final Buffer buffer = new Buffer(globalProvider);
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("select prop_key, text_value, prj.uuid, prj.id from properties prop " +
+ "inner join projects prj on prj.id=prop.resource_id " +
+ "where prop.prop_key in (?, ?) " +
+ "order by prj.id asc")
+ .setString(1, PROVIDER)
+ .setString(2, GITHUB_REPOSITORY);
+ massUpdate.update(INSERT_SQL);
+ massUpdate.update(DELETE_SQL);
+ massUpdate.execute((row, update, updateIndex) -> {
+ boolean shouldExecuteUpdate = false;
+ String projectUuid = row.getString(3);
+ Long projectId = row.getLong(4);
+ // Set last projectUuid the first time
+ if (buffer.getLastProjectUuid() == null) {
+ buffer.setLastProject(projectUuid, projectId);
+ }
+ // When current projectUuid is different from last processed projectUuid, feed the prepared statement
+ if (!projectUuid.equals(buffer.getLastProjectUuid())) {
+ if (updateIndex == 0) {
+ // Insert new row in PROJECT_ALM_SETTINGS
+ shouldExecuteUpdate = updateStatementIfNeeded(buffer.getLastProjectUuid(), buffer, update, almSettingUuid);
+ } else {
+ // Delete old property
+ shouldExecuteUpdate = deleteProperty(buffer.getLastProjectId(), buffer, update);
+ buffer.clear();
+ // Update last projectUuid in buffer only when it has changed
+ buffer.setLastProject(projectUuid, projectId);
+ }
+ }
+ // Update remaining buffer values only once, and only after delete
+ if (updateIndex == 1) {
+ String propertyKey = row.getString(1);
+ String propertyValue = row.getString(2);
+ if (propertyKey.equals(PROVIDER)) {
+ buffer.setProvider(propertyValue);
+ } else if (propertyKey.equals(GITHUB_REPOSITORY)) {
+ buffer.setRepository(propertyValue);
+ }
+ buffer.setCurrentProject(projectUuid, projectId);
+ }
+ return shouldExecuteUpdate;
+ });
+ String projectUuid = buffer.getCurrentProjectUuid();
+ if (projectUuid == null) {
+ return;
+ }
+ // Process last entry
+ Upsert upsert = context.prepareUpsert(INSERT_SQL);
+ if (updateStatementIfNeeded(projectUuid, buffer, upsert, almSettingUuid)) {
+ upsert.execute().commit();
+ }
+ if (buffer.shouldDelete()) {
+ context.prepareUpsert(DELETE_SQL)
+ .setString(1, GITHUB_REPOSITORY)
+ .setLong(2, buffer.getCurrentProjectId())
+ .execute()
+ .commit();
+ }
+ }
+
+ private static boolean deleteProperty(long effectiveProjectId, Buffer buffer, SqlStatement update) throws SQLException {
+ if (buffer.shouldDelete()) {
+ update.setString(1, GITHUB_REPOSITORY);
+ update.setLong(2, effectiveProjectId);
+ return true;
+ }
+ return false;
+ }
+
+ private boolean updateStatementIfNeeded(String effectiveProjectUuid, Buffer buffer, SqlStatement update, String almSettingUuid)
+ throws SQLException {
+ if (!buffer.shouldUpdate()) {
+ return false;
+ }
+ update.setString(1, uuidFactory.create());
+ update.setString(2, effectiveProjectUuid);
+ update.setString(3, almSettingUuid);
+ update.setString(4, buffer.getRepository());
+ update.setLong(5, system2.now());
+ update.setLong(6, system2.now());
+ return true;
+ }
+
+ private static class Property {
+ private final String key;
+ private final String value;
+
+ Property(Select.Row row) throws SQLException {
+ this.key = row.getString(1);
+ this.value = row.getString(2);
+ }
+
+ String getKey() {
+ return key;
+ }
+
+ String getValue() {
+ return value;
+ }
+ }
+
+ private static class Buffer {
+ private final String globalProvider;
+ private String lastProjectUuid;
+ private String currentProjectUuid;
+ private Long lastProjectId;
+ private Long currentProjectId;
+ private String provider;
+ private String repository;
+
+ public Buffer(@Nullable String globalProvider) {
+ this.globalProvider = globalProvider;
+ }
+
+ Buffer setLastProject(@Nullable String projectUuid, @Nullable Long projectId) {
+ this.lastProjectUuid = projectUuid;
+ this.lastProjectId = projectId;
+ return this;
+ }
+
+ @CheckForNull
+ String getLastProjectUuid() {
+ return lastProjectUuid;
+ }
+
+ @CheckForNull
+ Long getLastProjectId() {
+ return lastProjectId;
+ }
+
+ Buffer setCurrentProject(@Nullable String projectUuid, @Nullable Long projectId) {
+ this.currentProjectUuid = projectUuid;
+ this.currentProjectId = projectId;
+ return this;
+ }
+
+ @CheckForNull
+ String getCurrentProjectUuid() {
+ return currentProjectUuid;
+ }
+
+ @CheckForNull
+ Long getCurrentProjectId() {
+ return currentProjectId;
+ }
+
+ Buffer setProvider(@Nullable String provider) {
+ this.provider = provider;
+ return this;
+ }
+
+ @CheckForNull
+ String getRepository() {
+ return repository;
+ }
+
+ Buffer setRepository(@Nullable String repository) {
+ this.repository = repository;
+ return this;
+ }
+
+ boolean shouldUpdate() {
+ if (repository == null) {
+ return false;
+ }
+ if (Objects.equals(provider, PROVIDER_VALUE)) {
+ return true;
+ }
+ return provider == null && Objects.equals(globalProvider, PROVIDER_VALUE);
+ }
+
+ boolean shouldDelete() {
+ if (provider != null) {
+ return provider.equals(PROVIDER_VALUE);
+ }
+ return Objects.equals(globalProvider, PROVIDER_VALUE);
+ }
+
+ void clear() {
+ this.lastProjectUuid = null;
+ this.currentProjectUuid = null;
+ this.lastProjectId = null;
+ this.currentProjectId = null;
+ this.provider = null;
+ this.repository = null;
+ }
+ }
+
+}
@Test
public void verify_migration_count() {
- verifyMigrationCount(underTest, 2);
+ verifyMigrationCount(underTest, 3);
}
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.platform.db.migration.version.v81;
+
+import java.sql.SQLException;
+import javax.annotation.Nullable;
+import org.assertj.core.groups.Tuple;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+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.DataChange;
+
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toSet;
+import static org.apache.commons.lang.math.RandomUtils.nextInt;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
+
+public class MigrateGithubAlmSettingsTest {
+
+ private final static long PAST = 10_000_000_000L;
+ private static final long NOW = 50_000_000_000L;
+ private System2 system2 = new TestSystem2().setNow(NOW);
+
+ @Rule
+ public CoreDbTester db = CoreDbTester.createForSchema(MigrateGithubAlmSettingsTest.class, "schema.sql");
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private UuidFactory uuidFactory = UuidFactoryFast.getInstance();
+
+ private DataChange underTest = new MigrateGithubAlmSettings(db.database(), uuidFactory, system2);
+
+ @Test
+ public void migrate_settings_when_global_provider_is_set_to_github() throws SQLException {
+ insertProperty("sonar.pullrequest.provider", "GitHub", null);
+ insertProperty("sonar.pullrequest.github.endpoint", "https://enterprise.github.com", null);
+ insertProperty("sonar.alm.github.app.id", "12345", null);
+ insertProperty("sonar.alm.github.app.privateKeyContent.secured", "<PRIVATE_KEY>", null);
+ long projectId1 = insertProject("PROJECT_1");
+ insertProperty("sonar.pullrequest.github.repository", "Repository1", projectId1);
+ long projectId2 = insertProject("PROJECT_2");
+ insertProperty("sonar.pullrequest.github.repository", "Repository2", projectId2);
+
+ underTest.execute();
+
+ assertAlmSettings(tuple("github", "GitHub", "https://enterprise.github.com", "12345", "<PRIVATE_KEY>", NOW, NOW));
+ String gitHubAlmSettingUuid = selectAlmSettingUuid("GitHub");
+ assertProjectAlmSettings(
+ tuple("PROJECT_1", gitHubAlmSettingUuid, "Repository1", NOW, NOW),
+ tuple("PROJECT_2", gitHubAlmSettingUuid, "Repository2", NOW, NOW));
+ assertProperties(tuple("sonar.pullrequest.provider", "GitHub", null));
+ }
+
+ @Test
+ public void migrate_settings_when_project_provider_is_set_to_github_and_global_provider_is_set_to_something_else() throws SQLException {
+ insertProperty("sonar.pullrequest.provider", "Azure DevOps", null);
+ insertProperty("sonar.pullrequest.github.endpoint", "https://enterprise.github.com", null);
+ insertProperty("sonar.alm.github.app.id", "12345", null);
+ insertProperty("sonar.alm.github.app.privateKeyContent.secured", "<PRIVATE_KEY>", null);
+ long projectId1 = insertProject("PROJECT_1");
+ insertProperty("sonar.pullrequest.provider", "GitHub", projectId1);
+ insertProperty("sonar.pullrequest.github.repository", "Repository1", projectId1);
+ long projectId2 = insertProject("PROJECT_2");
+ insertProperty("sonar.pullrequest.provider", "GitHub", projectId2);
+ insertProperty("sonar.pullrequest.github.repository", "Repository2", projectId2);
+
+ underTest.execute();
+
+ assertAlmSettings(tuple("github", "GitHub", "https://enterprise.github.com", "12345", "<PRIVATE_KEY>", NOW, NOW));
+ String gitHubAlmSettingUuid = selectAlmSettingUuid("GitHub");
+ assertProjectAlmSettings(
+ tuple("PROJECT_1", gitHubAlmSettingUuid, "Repository1", NOW, NOW),
+ tuple("PROJECT_2", gitHubAlmSettingUuid, "Repository2", NOW, NOW));
+ assertProperties(
+ tuple("sonar.pullrequest.provider", "Azure DevOps", null),
+ tuple("sonar.pullrequest.provider", "GitHub", projectId1),
+ tuple("sonar.pullrequest.provider", "GitHub", projectId2));
+ }
+
+ @Test
+ public void delete_github_settings_when_project_provider_is_not_set_to_github() throws SQLException {
+ insertProperty("sonar.pullrequest.provider", "GitHub", null);
+ insertProperty("sonar.pullrequest.github.endpoint", "https://enterprise.github.com", null);
+ insertProperty("sonar.alm.github.app.id", "12345", null);
+ insertProperty("sonar.alm.github.app.privateKeyContent.secured", "<PRIVATE_KEY>", null);
+ long projectId1 = insertProject("PROJECT_1");
+ // Project provider is set to something else
+ insertProperty("sonar.pullrequest.provider", "Azure", projectId1);
+ insertProperty("sonar.pullrequest.github.repository", "Repository1", projectId1);
+
+ underTest.execute();
+
+ assertNoProjectAlmSettings();
+ assertProperties(
+ tuple("sonar.pullrequest.provider", "GitHub", null),
+ tuple("sonar.pullrequest.provider", "Azure", projectId1));
+ }
+
+ @Test
+ public void use_existing_alm_setting() throws SQLException {
+ db.executeInsert("alm_settings",
+ "uuid", "ABCD",
+ "alm_id", "github",
+ "kee", "GitHub",
+ "url", "https://enterprise.github.com",
+ "app_id", "12345",
+ "private_key", "<PRIVATE_KEY>",
+ "created_at", PAST,
+ "updated_at", PAST);
+ insertProperty("sonar.pullrequest.provider", "GitHub", null);
+ insertProperty("sonar.pullrequest.github.endpoint", "https://enterprise.github.com", null);
+ insertProperty("sonar.alm.github.app.id", "12345", null);
+ insertProperty("sonar.alm.github.app.privateKeyContent.secured", "<PRIVATE_KEY>", null);
+ long projectId1 = insertProject("PROJECT_1");
+ insertProperty("sonar.pullrequest.github.repository", "Repository1", projectId1);
+
+ underTest.execute();
+
+ assertProjectAlmSettings(tuple("PROJECT_1", "ABCD", "Repository1", NOW, NOW));
+ assertProperties(tuple("sonar.pullrequest.provider", "GitHub", null));
+ }
+
+ @Test
+ public void ignore_none_github_project_settings() throws SQLException {
+ insertProperty("sonar.pullrequest.provider", "GitHub", null);
+ insertProperty("sonar.pullrequest.github.endpoint", "https://enterprise.github.com", null);
+ insertProperty("sonar.alm.github.app.id", "12345", null);
+ insertProperty("sonar.alm.github.app.privateKeyContent.secured", "<PRIVATE_KEY>", null);
+ long projectId1 = insertProject("PROJECT_1");
+ insertProperty("sonar.pullrequest.provider", "Bitbucket", projectId1);
+ long projectId2 = insertProject("PROJECT_2");
+ insertProperty("sonar.pullrequest.provider", "Bitbucket", projectId2);
+
+ underTest.execute();
+
+ assertAlmSettings(tuple("github", "GitHub", "https://enterprise.github.com", "12345", "<PRIVATE_KEY>", NOW, NOW));
+ assertNoProjectAlmSettings();
+ assertProperties(
+ tuple("sonar.pullrequest.provider", "GitHub", null),
+ tuple("sonar.pullrequest.provider", "Bitbucket", projectId1),
+ tuple("sonar.pullrequest.provider", "Bitbucket", projectId2));
+ }
+
+ @Test
+ public void do_not_create_alm_settings_when_missing_some_global_properties() throws SQLException {
+ insertProperty("sonar.pullrequest.provider", "GitHub", null);
+ // No endpoint
+ insertProperty("sonar.alm.github.app.id", "12345", null);
+ insertProperty("sonar.alm.github.app.privateKeyContent.secured", "<PRIVATE_KEY>", null);
+ long projectId1 = insertProject("PROJECT_1");
+ insertProperty("sonar.pullrequest.github.repository", "Repository1", projectId1);
+
+ underTest.execute();
+
+ assertNoAlmSettings();
+ assertNoProjectAlmSettings();
+ assertProperties(tuple("sonar.pullrequest.provider", "GitHub", null));
+ }
+
+ @Test
+ public void do_not_create_project_alm_settings_when_missing_some_project_properties() throws SQLException {
+ insertProperty("sonar.pullrequest.provider", "GitHub", null);
+ insertProperty("sonar.pullrequest.github.endpoint", "https://enterprise.github.com", null);
+ insertProperty("sonar.alm.github.app.id", "12345", null);
+ insertProperty("sonar.alm.github.app.privateKeyContent.secured", "<PRIVATE_KEY>", null);
+ long projectId1 = insertProject("PROJECT_1");
+ // No repository
+ insertProperty("sonar.pullrequest.provider", "GitHub", projectId1);
+
+ underTest.execute();
+
+ assertAlmSettings(tuple("github", "GitHub", "https://enterprise.github.com", "12345", "<PRIVATE_KEY>", NOW, NOW));
+ assertNoProjectAlmSettings();
+ assertProperties(
+ tuple("sonar.pullrequest.provider", "GitHub", null),
+ tuple("sonar.pullrequest.provider", "GitHub", projectId1));
+ }
+
+ @Test
+ public void do_nothing_when_no_alm_properties() throws SQLException {
+ insertProperty("sonar.other.property", "Something", null);
+
+ underTest.execute();
+
+ assertNoAlmSettings();
+ assertNoProjectAlmSettings();
+ assertProperties(tuple("sonar.other.property", "Something", null));
+ }
+
+ @Test
+ public void migration_is_reentrant() throws SQLException {
+ insertProperty("sonar.pullrequest.provider", "GitHub", null);
+ insertProperty("sonar.pullrequest.github.endpoint", "https://enterprise.github.com", null);
+ insertProperty("sonar.alm.github.app.id", "12345", null);
+ insertProperty("sonar.alm.github.app.privateKeyContent.secured", "<PRIVATE_KEY>", null);
+ long projectId1 = insertProject("PROJECT_1");
+ insertProperty("sonar.pullrequest.provider", "GitHub", projectId1);
+ insertProperty("sonar.pullrequest.github.repository", "Repository1", projectId1);
+ underTest.execute();
+
+ // Global settings have been removed, let's re-create them
+ insertProperty("sonar.pullrequest.github.endpoint", "https://enterprise.github.com", null);
+ insertProperty("sonar.alm.github.app.id", "12345", null);
+ insertProperty("sonar.alm.github.app.privateKeyContent.secured", "<PRIVATE_KEY>", null);
+ long projectId2 = insertProject("PROJECT_2");
+ insertProperty("sonar.pullrequest.github.repository", "Repository2", projectId2);
+ underTest.execute();
+
+ assertAlmSettings(tuple("github", "GitHub", "https://enterprise.github.com", "12345", "<PRIVATE_KEY>", NOW, NOW));
+ assertProperties(
+ tuple("sonar.pullrequest.provider", "GitHub", null),
+ tuple("sonar.pullrequest.provider", "GitHub", projectId1));
+ }
+
+ private void assertAlmSettings(Tuple... expectedTuples) {
+ assertThat(db.select("SELECT alm_id, kee, url, app_id, private_key, created_at, updated_at FROM alm_settings")
+ .stream()
+ .map(map -> new Tuple(map.get("ALM_ID"), map.get("KEE"), map.get("URL"), map.get("APP_ID"), map.get("PRIVATE_KEY"), map.get("CREATED_AT"),
+ map.get("UPDATED_AT")))
+ .collect(toList()))
+ .containsExactlyInAnyOrder(expectedTuples);
+ }
+
+ private void assertNoAlmSettings() {
+ assertAlmSettings();
+ }
+
+ private void assertProjectAlmSettings(Tuple... expectedTuples) {
+ assertThat(db.select("SELECT project_uuid, alm_setting_uuid, alm_repo, created_at, updated_at FROM project_alm_settings")
+ .stream()
+ .map(map -> new Tuple(map.get("PROJECT_UUID"), map.get("ALM_SETTING_UUID"), map.get("ALM_REPO"), map.get("CREATED_AT"), map.get("UPDATED_AT")))
+ .collect(toList()))
+ .containsExactlyInAnyOrder(expectedTuples);
+ }
+
+ private void assertNoProjectAlmSettings() {
+ assertProjectAlmSettings();
+ }
+
+ private void assertProperties(Tuple... expectedTuples) {
+ assertThat(db.select("SELECT prop_key, text_value, resource_id FROM properties")
+ .stream()
+ .map(map -> new Tuple(map.get("PROP_KEY"), map.get("TEXT_VALUE"), map.get("RESOURCE_ID")))
+ .collect(toSet()))
+ .containsExactlyInAnyOrder(expectedTuples);
+ }
+
+ private void assertNoProperties() {
+ assertProperties();
+ }
+
+ private String selectAlmSettingUuid(String almSettingKey) {
+ return (String) db.selectFirst("select uuid from alm_settings where kee='" + almSettingKey + "'").get("UUID");
+ }
+
+ private void insertProperty(String key, String value, @Nullable Long projectId) {
+ db.executeInsert(
+ "PROPERTIES",
+ "PROP_KEY", key,
+ "RESOURCE_ID", projectId,
+ "USER_ID", null,
+ "IS_EMPTY", false,
+ "TEXT_VALUE", value,
+ "CLOB_VALUE", null,
+ "CREATED_AT", System2.INSTANCE.now());
+ }
+
+ private long insertProject(String uuid) {
+ int id = nextInt();
+ db.executeInsert("PROJECTS",
+ "ID", id,
+ "ORGANIZATION_UUID", "default",
+ "KEE", uuid + "-key",
+ "UUID", uuid,
+ "PROJECT_UUID", uuid,
+ "MAIN_BRANCH_PROJECT_UUID", uuid,
+ "UUID_PATH", ".",
+ "ROOT_UUID", uuid,
+ "PRIVATE", Boolean.toString(false),
+ "SCOPE", "PRJ",
+ "QUALIFIER", "PRJ");
+ return id;
+ }
+
+}
--- /dev/null
+CREATE TABLE ALM_SETTINGS(
+ UUID VARCHAR(40) NOT NULL,
+ ALM_ID VARCHAR(40) NOT NULL,
+ KEE VARCHAR(200) NOT NULL,
+ URL VARCHAR(2000),
+ APP_ID VARCHAR(80),
+ PRIVATE_KEY VARCHAR(2000),
+ PAT VARCHAR(2000),
+ UPDATED_AT BIGINT NOT NULL,
+ CREATED_AT BIGINT NOT NULL
+);
+ALTER TABLE ALM_SETTINGS ADD CONSTRAINT PK_ALM_SETTINGS PRIMARY KEY(UUID);
+CREATE UNIQUE INDEX UNIQ_ALM_SETTINGS ON ALM_SETTINGS(KEE);
+
+CREATE TABLE PROJECT_ALM_SETTINGS(
+ UUID VARCHAR(40) NOT NULL,
+ ALM_SETTING_UUID VARCHAR(40) NOT NULL,
+ PROJECT_UUID VARCHAR(50) NOT NULL,
+ ALM_REPO VARCHAR(256),
+ ALM_SLUG VARCHAR(256),
+ UPDATED_AT BIGINT NOT NULL,
+ CREATED_AT BIGINT NOT NULL
+);
+ALTER TABLE PROJECT_ALM_SETTINGS ADD CONSTRAINT PK_PROJECT_ALM_SETTINGS PRIMARY KEY(UUID);
+CREATE UNIQUE INDEX UNIQ_PROJECT_ALM_SETTINGS ON PROJECT_ALM_SETTINGS(PROJECT_UUID);
+CREATE INDEX PROJECT_ALM_SETTINGS_ALM ON PROJECT_ALM_SETTINGS(ALM_SETTING_UUID);
+
+CREATE TABLE "PROPERTIES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "PROP_KEY" VARCHAR(512) NOT NULL,
+ "RESOURCE_ID" INTEGER,
+ "USER_ID" INTEGER,
+ "IS_EMPTY" BOOLEAN NOT NULL,
+ "TEXT_VALUE" VARCHAR(4000),
+ "CLOB_VALUE" CLOB,
+ "CREATED_AT" BIGINT
+);
+CREATE INDEX "PROPERTIES_KEY" ON "PROPERTIES" ("PROP_KEY");
+
+CREATE TABLE PROJECTS(
+ ID INTEGER NOT NULL AUTO_INCREMENT (1,1),
+ UUID VARCHAR(50) NOT NULL,
+ ORGANIZATION_UUID VARCHAR(40) NOT NULL,
+ KEE VARCHAR(400),
+ DEPRECATED_KEE VARCHAR(400),
+ NAME VARCHAR(2000),
+ LONG_NAME VARCHAR(2000),
+ DESCRIPTION VARCHAR(2000),
+ ENABLED BOOLEAN DEFAULT TRUE NOT NULL,
+ SCOPE VARCHAR(3),
+ QUALIFIER VARCHAR(10),
+ PRIVATE BOOLEAN NOT NULL,
+ ROOT_UUID VARCHAR(50) NOT NULL,
+ LANGUAGE VARCHAR(20),
+ COPY_COMPONENT_UUID VARCHAR(50),
+ DEVELOPER_UUID VARCHAR(50),
+ PATH VARCHAR(2000),
+ UUID_PATH VARCHAR(1500) NOT NULL,
+ PROJECT_UUID VARCHAR(50) NOT NULL,
+ MODULE_UUID VARCHAR(50),
+ MODULE_UUID_PATH VARCHAR(1500),
+ AUTHORIZATION_UPDATED_AT BIGINT,
+ TAGS VARCHAR(500),
+ MAIN_BRANCH_PROJECT_UUID VARCHAR(50),
+ B_CHANGED BOOLEAN,
+ B_NAME VARCHAR(500),
+ B_LONG_NAME VARCHAR(500),
+ B_DESCRIPTION VARCHAR(2000),
+ B_ENABLED BOOLEAN,
+ B_QUALIFIER VARCHAR(10),
+ B_LANGUAGE VARCHAR(20),
+ B_COPY_COMPONENT_UUID VARCHAR(50),
+ B_PATH VARCHAR(2000),
+ B_UUID_PATH VARCHAR(1500),
+ B_MODULE_UUID VARCHAR(50),
+ B_MODULE_UUID_PATH VARCHAR(1500),
+ CREATED_AT TIMESTAMP
+);
+ALTER TABLE PROJECTS ADD CONSTRAINT PK_PROJECTS PRIMARY KEY(ID);
+CREATE INDEX PROJECTS_ORGANIZATION ON PROJECTS(ORGANIZATION_UUID);
+CREATE UNIQUE INDEX PROJECTS_KEE ON PROJECTS(KEE);
+CREATE INDEX PROJECTS_MODULE_UUID ON PROJECTS(MODULE_UUID);
+CREATE INDEX PROJECTS_PROJECT_UUID ON PROJECTS(PROJECT_UUID);
+CREATE INDEX PROJECTS_QUALIFIER ON PROJECTS(QUALIFIER);
+CREATE INDEX PROJECTS_ROOT_UUID ON PROJECTS(ROOT_UUID);
+CREATE INDEX PROJECTS_UUID ON PROJECTS(UUID);