-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
SONAR-7794 Populate DB column RULES_PROFILES.USER_UPDATED_AT
- Loading branch information
Showing
8 changed files
with
270 additions
and
2 deletions.
There are no files selected for viewing
28 changes: 28 additions & 0 deletions
28
...web/src/main/webapp/WEB-INF/db/migrate/1263_populate_user_updated_at_of_rules_profiles.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# | ||
# 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.0 | ||
# SONAR-7794 | ||
# | ||
class PopulateUserUpdatedAtOfRulesProfiles < ActiveRecord::Migration | ||
def self.up | ||
execute_java_migration('org.sonar.db.version.v60.PopulateUserUpdatedAtOfRulesProfiles') | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
92 changes: 92 additions & 0 deletions
92
sonar-db/src/main/java/org/sonar/db/version/v60/PopulateUserUpdatedAtOfRulesProfiles.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
/* | ||
* 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.v60; | ||
|
||
import com.google.common.base.Throwables; | ||
import java.sql.SQLException; | ||
import java.util.Date; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import javax.annotation.CheckForNull; | ||
import org.sonar.db.Database; | ||
import org.sonar.db.version.BaseDataChange; | ||
import org.sonar.db.version.MassUpdate; | ||
import org.sonar.db.version.Select; | ||
import org.sonar.db.version.SqlStatement; | ||
|
||
public class PopulateUserUpdatedAtOfRulesProfiles extends BaseDataChange { | ||
|
||
private static final String SQL_SELECT_PROFILES_NOT_UPDATED = "select kee from rules_profiles where user_updated_at is null"; | ||
|
||
public PopulateUserUpdatedAtOfRulesProfiles(Database db) { | ||
super(db); | ||
} | ||
|
||
@Override | ||
public void execute(Context context) throws SQLException { | ||
Map<String, Long> userUpdatedAtByProfileKeys = buildUserUpdatedAtMap(context); | ||
populateUserUpdatedAtColumn(context, userUpdatedAtByProfileKeys); | ||
} | ||
|
||
private static Map<String, Long> buildUserUpdatedAtMap(Context context) throws SQLException { | ||
Map<String, Long> lastAnalysisDatesByQPKeys = new HashMap<>(); | ||
List<String> profileKeys = context.prepareSelect(SQL_SELECT_PROFILES_NOT_UPDATED).list(row -> row.getString(1)); | ||
profileKeys.forEach(profileKey -> lastAnalysisDatesByQPKeys.put(profileKey, getUserUpdateAt(context, profileKey))); | ||
|
||
return lastAnalysisDatesByQPKeys; | ||
} | ||
|
||
@CheckForNull | ||
private static Long getUserUpdateAt(Context context, String profileKey) { | ||
try { | ||
return context.prepareSelect("select created_at as \"createdAt\" " + | ||
"from activities " + | ||
"where user_login is not null " + | ||
" and profile_key=? " + | ||
"order by created_at DESC ") | ||
.setString(1, profileKey) | ||
.get(row -> { | ||
Date userUpdatedAt = row.getNullableDate(1); | ||
return userUpdatedAt == null ? null : userUpdatedAt.getTime(); | ||
}); | ||
} catch (SQLException e) { | ||
throw Throwables.propagate(e); | ||
} | ||
} | ||
|
||
private static void populateUserUpdatedAtColumn(Context context, Map<String, Long> userUpdatedAdByProfileKey) throws SQLException { | ||
MassUpdate massUpdate = context.prepareMassUpdate(); | ||
massUpdate.select(SQL_SELECT_PROFILES_NOT_UPDATED); | ||
massUpdate.update("update rules_profiles set user_updated_at=? where kee=?"); | ||
massUpdate.rowPluralName("quality profiles"); | ||
massUpdate.execute((row, update) -> handle(userUpdatedAdByProfileKey, row, update)); | ||
} | ||
|
||
private static boolean handle(Map<String, Long> userUpdatedAtByProfileKey, Select.Row row, SqlStatement update) throws SQLException { | ||
String profileKey = row.getString(1); | ||
|
||
update.setLong(1, userUpdatedAtByProfileKey.get(profileKey)); | ||
update.setString(2, profileKey); | ||
|
||
return true; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
119 changes: 119 additions & 0 deletions
119
...r-db/src/test/java/org/sonar/db/version/v60/PopulateUserUpdatedAtOfRulesProfilesTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
/* | ||
* 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.v60; | ||
|
||
import com.google.common.base.Throwables; | ||
import java.sql.PreparedStatement; | ||
import java.sql.SQLException; | ||
import java.sql.Timestamp; | ||
import javax.annotation.CheckForNull; | ||
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 java.lang.String.valueOf; | ||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
public class PopulateUserUpdatedAtOfRulesProfilesTest { | ||
private static final String TABLE_QUALITY_PROFILES = "rules_profiles"; | ||
private static final String TABLE_ACTIVITIES = "activities"; | ||
|
||
@Rule | ||
public DbTester db = DbTester.createForSchema(System2.INSTANCE, PopulateUserUpdatedAtOfRulesProfilesTest.class, "schema.sql"); | ||
|
||
PopulateUserUpdatedAtOfRulesProfiles underTest = new PopulateUserUpdatedAtOfRulesProfiles(db.database()); | ||
|
||
@Test | ||
public void migration_has_no_effect_on_empty_tables() throws SQLException { | ||
underTest.execute(); | ||
|
||
assertThat(db.countRowsOfTable(TABLE_QUALITY_PROFILES)).isEqualTo(0); | ||
assertThat(db.countRowsOfTable(TABLE_ACTIVITIES)).isEqualTo(0); | ||
} | ||
|
||
@Test | ||
public void migration_update_quality_profiles_user_updated_at() throws SQLException { | ||
|
||
insertQualityProfile(1, "first-quality-profile"); | ||
insertActivity("first-quality-profile", "my-login", 1_000_000_00L); | ||
insertActivity("first-quality-profile", null, 2_000_000_000L); | ||
insertActivity("first-quality-profile", "my-login", 1_100_000_000L); | ||
insertQualityProfile(2, "second-quality-profile"); | ||
insertActivity("second-quality-profile", null, 1_000_000_00L); | ||
insertQualityProfile(3, "third-quality-profile"); | ||
insertQualityProfile(4, "fourth-quality-profile"); | ||
insertActivity("fourth-quality-profile", "my-login", 1_000_000_00L); | ||
|
||
underTest.execute(); | ||
|
||
assertUserUpdatedAt("first-quality-profile", 1_100_000_000L); | ||
assertNoUserUpdatedAtDate("second-quality-profile"); | ||
assertNoUserUpdatedAtDate("third-quality-profile"); | ||
assertUserUpdatedAt("fourth-quality-profile", 1_000_000_00L); | ||
} | ||
|
||
@Test | ||
public void migration_is_reentrant() throws SQLException { | ||
insertQualityProfile(1, "first-quality-profile"); | ||
insertActivity("first-quality-profile", "my-login", 1_000_000_000L); | ||
|
||
underTest.execute(); | ||
assertUserUpdatedAt("first-quality-profile", 1_000_000_000L); | ||
|
||
underTest.execute(); | ||
assertUserUpdatedAt("first-quality-profile", 1_000_000_000L); | ||
} | ||
|
||
private void assertUserUpdatedAt(String qualityProfileKey, long expectedLastUsed) { | ||
assertThat(selectUserUpdatedAt(qualityProfileKey)).isEqualTo(expectedLastUsed); | ||
} | ||
|
||
private void assertNoUserUpdatedAtDate(String qualityProfileKey) { | ||
assertThat(selectUserUpdatedAt(qualityProfileKey)).isNull(); | ||
} | ||
|
||
@CheckForNull | ||
private Long selectUserUpdatedAt(String qualityProfileKey) { | ||
return (Long) db.selectFirst(String.format("select user_updated_at as \"userUpdatedAt\" from rules_profiles where kee ='%s'", qualityProfileKey)).get("userUpdatedAt"); | ||
} | ||
|
||
private void insertActivity(String profileKey, @Nullable String login, @Nullable Long createdAt) { | ||
final String sqlInsertActivity = "insert into activities (profile_key, user_login, created_at) values (?, ?, ?) "; | ||
try (PreparedStatement ps = db.getSession().getConnection().prepareStatement(sqlInsertActivity)) { | ||
ps.setString(1, profileKey); | ||
ps.setString(2, login); | ||
ps.setTimestamp(3, createdAt == null ? null : new Timestamp(createdAt)); | ||
ps.executeUpdate(); | ||
db.getSession().commit(); | ||
} catch (SQLException e) { | ||
throw Throwables.propagate(e); | ||
} | ||
} | ||
|
||
private void insertQualityProfile(long id, String key) { | ||
db.executeInsert(TABLE_QUALITY_PROFILES, | ||
"id", valueOf(id), | ||
"name", key, | ||
"kee", key); | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
...st/resources/org/sonar/db/version/v60/PopulateUserUpdatedAtOfRulesProfilesTest/schema.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
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 TABLE "RULES_PROFILES" ( | ||
"ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), | ||
"NAME" VARCHAR(100) NOT NULL, | ||
"LANGUAGE" VARCHAR(20), | ||
"KEE" VARCHAR(255) NOT NULL, | ||
"PARENT_KEE" VARCHAR(255), | ||
"RULES_UPDATED_AT" VARCHAR(100), | ||
"IS_DEFAULT" BOOLEAN NOT NULL DEFAULT FALSE, | ||
"CREATED_AT" TIMESTAMP, | ||
"UPDATED_AT" TIMESTAMP, | ||
"LAST_USED" BIGINT, | ||
"USER_UPDATED_AT" BIGINT | ||
); |