From b5851d0ff8318549c2356b20df36401b879fc5a2 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Fri, 9 Sep 2016 11:16:37 +0200 Subject: [PATCH] SONAR-7851 copy activities to qprofile_changes --- ...317_copy_activities_to_qprofile_changes.rb | 29 ++++ .../org/sonar/db/version/DatabaseVersion.java | 2 +- .../sonar/db/version/MigrationStepModule.java | 7 + .../v61/CopyActivitiesToQprofileChanges.java | 68 +++++++++ .../org/sonar/db/version/rows-h2.sql | 1 + .../CopyActivitiesToQprofileChangesTest.java | 133 ++++++++++++++++++ .../schema.sql | 22 +++ 7 files changed, 261 insertions(+), 1 deletion(-) create mode 100644 server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1317_copy_activities_to_qprofile_changes.rb create mode 100644 sonar-db/src/main/java/org/sonar/db/version/v61/CopyActivitiesToQprofileChanges.java create mode 100644 sonar-db/src/test/java/org/sonar/db/version/v61/CopyActivitiesToQprofileChangesTest.java create mode 100644 sonar-db/src/test/resources/org/sonar/db/version/v61/CopyActivitiesToQprofileChangesTest/schema.sql diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1317_copy_activities_to_qprofile_changes.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1317_copy_activities_to_qprofile_changes.rb new file mode 100644 index 00000000000..422b66db88c --- /dev/null +++ b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1317_copy_activities_to_qprofile_changes.rb @@ -0,0 +1,29 @@ +# +# SonarQube, open source software quality management tool. +# Copyright (C) 2008-2014 SonarSource +# mailto:contact AT sonarsource DOT com +# +# SonarQube 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. +# +# SonarQube 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. +# + +# +# SonarQube 6.1 +# +class CopyActivitiesToQprofileChanges < ActiveRecord::Migration + + def self.up + execute_java_migration('org.sonar.db.version.v61.CopyActivitiesToQprofileChanges') + end +end diff --git a/sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java b/sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java index 34cd448686e..12f111a480a 100644 --- a/sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java +++ b/sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java @@ -30,7 +30,7 @@ import org.sonar.db.MyBatis; public class DatabaseVersion { - public static final int LAST_VERSION = 1_316; + public static final int LAST_VERSION = 1_317; /** * The minimum supported version which can be upgraded. Lower diff --git a/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java b/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java index e1efe08001b..9bd60db038d 100644 --- a/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java +++ b/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java @@ -146,6 +146,7 @@ import org.sonar.db.version.v60.PopulateUuidPathColumnOnProjects; import org.sonar.db.version.v60.RemoveUsersPasswordWhenNotLocal; import org.sonar.db.version.v61.AddBUuidPathToProjects; import org.sonar.db.version.v61.AddErrorColumnsToCeActivity; +import org.sonar.db.version.v61.CopyActivitiesToQprofileChanges; import org.sonar.db.version.v61.DeleteProjectDashboards; import org.sonar.db.version.v61.DeleteReportsFromCeQueue; import org.sonar.db.version.v61.DropIsGlobalFromDashboards; @@ -321,7 +322,13 @@ public class MigrationStepModule extends Module { ShrinkModuleUuidPathOfProjects.class, AddBUuidPathToProjects.class, AddErrorColumnsToCeActivity.class, +<<<<<<< HEAD PopulateTableProperties2.class, RemoveViewsDefinitionFromProperties.class); +======= + RemoveViewsDefinitionFromProperties.class, + CopyActivitiesToQprofileChanges.class + ); +>>>>>>> SONAR-7851 copy activities to qprofile_changes } } diff --git a/sonar-db/src/main/java/org/sonar/db/version/v61/CopyActivitiesToQprofileChanges.java b/sonar-db/src/main/java/org/sonar/db/version/v61/CopyActivitiesToQprofileChanges.java new file mode 100644 index 00000000000..e3707462147 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v61/CopyActivitiesToQprofileChanges.java @@ -0,0 +1,68 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.version.v61; + +import java.sql.SQLException; +import java.util.Date; +import org.sonar.db.Database; +import org.sonar.db.version.BaseDataChange; +import org.sonar.db.version.MassUpdate; + +public class CopyActivitiesToQprofileChanges extends BaseDataChange { + + public CopyActivitiesToQprofileChanges(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate.rowPluralName("activities"); + massUpdate + .select("select a.log_key, a.profile_key, a.created_at, a.user_login, a.log_action, a.data_field " + + "from activities a " + + "left join qprofile_changes qc on qc.kee = a.log_key " + + "where a.log_type=? " + + "and a.log_action is not null " + + "and a.profile_key is not null " + + "and a.created_at is not null " + + "and qc.kee is null") + .setString(1, "QPROFILE"); + + massUpdate.update("insert into qprofile_changes (kee, qprofile_key, created_at, user_login, change_type, data) values (?,?,?,?,?,?)"); + massUpdate.execute((row, update) -> { + String key = row.getString(1); + String profileKey = row.getString(2); + Date createdAt = row.getDate(3); + String login = row.getNullableString(4); + String type = row.getString(5); + String data = row.getNullableString(6); + + update.setString(1, key); + update.setString(2, profileKey); + update.setLong(3, createdAt.getTime()); + update.setString(4, login); + update.setString(5, type); + update.setString(6, data); + return true; + }); + } + +} diff --git a/sonar-db/src/main/resources/org/sonar/db/version/rows-h2.sql b/sonar-db/src/main/resources/org/sonar/db/version/rows-h2.sql index f5c3897d4da..e44e43e6b5e 100644 --- a/sonar-db/src/main/resources/org/sonar/db/version/rows-h2.sql +++ b/sonar-db/src/main/resources/org/sonar/db/version/rows-h2.sql @@ -501,6 +501,7 @@ INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1313'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1314'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1315'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1316'); +INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1317'); INSERT INTO USERS(ID, LOGIN, NAME, EMAIL, EXTERNAL_IDENTITY, EXTERNAL_IDENTITY_PROVIDER, USER_LOCAL, CRYPTED_PASSWORD, SALT, CREATED_AT, UPDATED_AT) VALUES (1, 'admin', 'Administrator', '', 'admin', 'sonarqube', true, 'a373a0e667abb2604c1fd571eb4ad47fe8cc0878', '48bc4b0d93179b5103fd3885ea9119498e9d161b', '1418215735482', '1418215735482'); ALTER TABLE USERS ALTER COLUMN ID RESTART WITH 2; diff --git a/sonar-db/src/test/java/org/sonar/db/version/v61/CopyActivitiesToQprofileChangesTest.java b/sonar-db/src/test/java/org/sonar/db/version/v61/CopyActivitiesToQprofileChangesTest.java new file mode 100644 index 00000000000..d4eacf9aacb --- /dev/null +++ b/sonar-db/src/test/java/org/sonar/db/version/v61/CopyActivitiesToQprofileChangesTest.java @@ -0,0 +1,133 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.version.v61; + +import java.sql.SQLException; +import java.util.Date; +import java.util.Map; +import javax.annotation.Nullable; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.utils.System2; +import org.sonar.db.DbTester; + +import static org.assertj.core.api.Assertions.assertThat; + + +public class CopyActivitiesToQprofileChangesTest { + + private static final long A_DATE = 1_500_000_000_000L; + private static final String TABLE_ACTIVITIES = "activities"; + private static final String TABLE_QPROFILE_CHANGES = "qprofile_changes"; + + @Rule + public DbTester db = DbTester.createForSchema(System2.INSTANCE, CopyActivitiesToQprofileChangesTest.class, "schema.sql"); + + private CopyActivitiesToQprofileChanges underTest = new CopyActivitiesToQprofileChanges(db.database()); + + @Test + public void migration_has_no_effect_on_empty_table() throws SQLException { + underTest.execute(); + + assertThat(db.countRowsOfTable(TABLE_QPROFILE_CHANGES)).isEqualTo(0); + } + + @Test + public void copy_qprofile_changes() throws SQLException { + String key = "U1"; + String profileKey = "P1"; + String login = "marcel"; + String type = "ACTIVATED"; + String data = "D1"; + insertActivity(key, profileKey, login, "QPROFILE", type, data, A_DATE); + + underTest.execute(); + + assertThat(db.countRowsOfTable(TABLE_QPROFILE_CHANGES)).isEqualTo(1); + Map change = selectChangeByKey(key); + assertThat(change.get("qprofileKey")).isEqualTo(profileKey); + assertThat(change.get("createdAt")).isEqualTo(A_DATE); + assertThat(change.get("login")).isEqualTo(login); + assertThat(change.get("changeType")).isEqualTo(type); + assertThat(change.get("data")).isEqualTo(data); + } + + /** + * Do not copy twice the same row + */ + @Test + public void copy_is_reentrant() throws SQLException { + insertActivity("U1", "P1", "marcel", "QPROFILE", "ACTIVATED", "D1", A_DATE); + + // first run + underTest.execute(); + assertThat(db.countRowsOfTable(TABLE_QPROFILE_CHANGES)).isEqualTo(1); + + // second run + underTest.execute(); + assertThat(db.countRowsOfTable(TABLE_QPROFILE_CHANGES)).isEqualTo(1); + } + + @Test + public void copy_nullable_fields() throws SQLException { + String key = "U1"; + String type = "ACTIVATED"; + // no login nor data + insertActivity(key, "P1", null, "QPROFILE", type, null, A_DATE); + + underTest.execute(); + + Map change = selectChangeByKey(key); + assertThat(change.get("qprofileKey")).isEqualTo("P1"); + assertThat(change.get("createdAt")).isEqualTo(A_DATE); + assertThat(change.get("changeType")).isEqualTo(type); + assertThat(change.get("login")).isNull(); + assertThat(change.get("data")).isNull(); + } + + @Test + public void ignore_activities_that_do_not_relate_to_qprofiles() throws SQLException { + insertActivity("U1", "P1", "marcel", "OTHER_ACTIVITY_TYPE", "T1", "D1", A_DATE); + + underTest.execute(); + + assertThat(db.countRowsOfTable(TABLE_QPROFILE_CHANGES)).isEqualTo(0); + } + + @Test + public void ignore_invalid_activities() throws SQLException { + // no change type + insertActivity("U1", "P1", "marcel", "QPROFILE", null, "D1", A_DATE); + // no date + insertActivity("U2", "P1", "marcel", "QPROFILE", "ACTIVATED", "D1", null); + + underTest.execute(); + + assertThat(db.countRowsOfTable(TABLE_QPROFILE_CHANGES)).isEqualTo(0); + } + + private void insertActivity(String key, @Nullable String profileKey, @Nullable String login, @Nullable String activityType, @Nullable String type, @Nullable String data, @Nullable Long createdAt) { + db.executeInsert(TABLE_ACTIVITIES, "log_key", key, "profile_key", profileKey, "user_login", login, "log_type", activityType, "log_action", type, "data_field", data, "created_at", createdAt != null ? new Date(createdAt) : null); + } + + private Map selectChangeByKey(String key) { + return db.selectFirst("select qprofile_key as \"qprofileKey\", created_at as \"createdAt\", user_login as \"login\", change_type as \"changeType\", data as \"data\" from qprofile_changes where kee='" + key + "'"); + } +} diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v61/CopyActivitiesToQprofileChangesTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v61/CopyActivitiesToQprofileChangesTest/schema.sql new file mode 100644 index 00000000000..1ad0dd1d259 --- /dev/null +++ b/sonar-db/src/test/resources/org/sonar/db/version/v61/CopyActivitiesToQprofileChangesTest/schema.sql @@ -0,0 +1,22 @@ +CREATE TABLE "ACTIVITIES" ( + "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "LOG_KEY" VARCHAR(250), + "PROFILE_KEY" VARCHAR(255) NOT NULL, + "CREATED_AT" TIMESTAMP, + "USER_LOGIN" VARCHAR(255), + "LOG_TYPE" VARCHAR(250), + "LOG_ACTION" VARCHAR(250), + "LOG_MESSAGE" VARCHAR(250), + "DATA_FIELD" CLOB(2147483647) +); +CREATE UNIQUE INDEX "ACTIVITIES_LOG_KEY" ON "ACTIVITIES" ("LOG_KEY"); + +CREATE TABLE "QPROFILE_CHANGES" ( + "KEE" VARCHAR(40) NOT NULL PRIMARY KEY, + "QPROFILE_KEY" VARCHAR(255) NOT NULL, + "CHANGE_TYPE" VARCHAR(20) NOT NULL, + "CREATED_AT" BIGINT NOT NULL, + "USER_LOGIN" VARCHAR(255), + "DATA" CLOB +); +CREATE INDEX "QPROFILE_CHANGES_QPROFILE_KEY" ON "QPROFILE_CHANGES" ("QPROFILE_KEY"); -- 2.39.5