Skip to content

Commit

Permalink
SONAR-7824 Populate DB column ACTIVITIES.PROFILE_KEY
Browse files Browse the repository at this point in the history
  • Loading branch information
teryk committed Jun 29, 2016
1 parent 082949f commit da6dbbc
Show file tree
Hide file tree
Showing 12 changed files with 221 additions and 9 deletions.
Expand Up @@ -27,6 +27,7 @@
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Date;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.update.UpdateRequest;
import org.sonar.api.utils.KeyValueFormat;
Expand All @@ -50,7 +51,8 @@ class ActivityResultSetIterator extends ResultSetIterator<UpdateRequest> {
"data_field",
"user_login",
"log_type",
"created_at"
"created_at",
"profile_key"
};

private static final String SQL_ALL = "select " + StringUtils.join(FIELDS, ",") + " from activities ";
Expand Down Expand Up @@ -86,7 +88,9 @@ protected UpdateRequest read(ResultSet rs) throws SQLException {
writer.prop(ActivityIndexDefinition.FIELD_KEY, key);
writer.prop(ActivityIndexDefinition.FIELD_ACTION, rs.getString(2));
writer.prop(ActivityIndexDefinition.FIELD_MESSAGE, rs.getString(3));
writer.name(ActivityIndexDefinition.FIELD_DETAILS).valueObject(KeyValueFormat.parse(rs.getString(4)));
Map<String, String> details = KeyValueFormat.parse(rs.getString(4));
details.put("profileKey", rs.getString(8));
writer.name(ActivityIndexDefinition.FIELD_DETAILS).valueObject(details);
writer.prop(ActivityIndexDefinition.FIELD_LOGIN, rs.getString(5));
writer.prop(ActivityIndexDefinition.FIELD_TYPE, rs.getString(6));
Date createdAt = rs.getTimestamp(7);
Expand Down
Expand Up @@ -95,7 +95,6 @@ public Activity toActivity() {
activity.setProfileKey(getKey().qProfile());
activity.setData("key", getKey().toString());
activity.setData("ruleKey", getKey().ruleKey().toString());
activity.setData("profileKey", getKey().qProfile());

parameters.entrySet().stream()
.filter(param -> !param.getKey().isEmpty())
Expand Down
Expand Up @@ -71,15 +71,21 @@ public void insert_and_index() {
activity.setAction("THE_ACTION");
activity.setMessage("THE_MSG");
activity.setData("foo", "bar");
activity.setData("profileKey", "PROFILE_KEY");
activity.setProfileKey("PROFILE_KEY");
service.save(activity);

Map<String, Object> dbMap = db.selectFirst("select log_type as \"type\", log_action as \"action\", log_message as \"msg\", data_field as \"data\" from activities");
Map<String, Object> dbMap = db.selectFirst("select " +
" log_type as \"type\", " +
" log_action as \"action\", " +
" log_message as \"msg\", " +
" data_field as \"data\", " +
" profile_key as \"profileKey\" " +
"from activities");
assertThat(dbMap).containsEntry("type", "ANALYSIS_REPORT");
assertThat(dbMap).containsEntry("action", "THE_ACTION");
assertThat(dbMap).containsEntry("msg", "THE_MSG");
assertThat(dbMap.get("data")).isEqualTo("foo=bar;profileKey=PROFILE_KEY");
assertThat(dbMap).containsEntry("profileKey", "PROFILE_KEY");
assertThat(dbMap.get("data")).isEqualTo("foo=bar");

List<ActivityDoc> docs = es.getDocuments("activities", "activity", ActivityDoc.class);
assertThat(docs).hasSize(1);
Expand Down
Expand Up @@ -49,7 +49,8 @@ public void traverse() {
assertThat(doc.get(ActivityIndexDefinition.FIELD_KEY)).isEqualTo("UUID1");
assertThat(doc.get(ActivityIndexDefinition.FIELD_ACTION)).isEqualTo("THE_ACTION");
assertThat(doc.get(ActivityIndexDefinition.FIELD_MESSAGE)).isEqualTo("THE_MSG");
assertThat((Map) doc.get(ActivityIndexDefinition.FIELD_DETAILS)).containsOnly(MapEntry.entry("foo", "bar"));
assertThat((Map<String, String>) doc.get(ActivityIndexDefinition.FIELD_DETAILS))
.containsOnly(MapEntry.entry("foo", "bar"), MapEntry.entry("profileKey", "PROFILE_KEY"));
assertThat(doc.get(ActivityIndexDefinition.FIELD_LOGIN)).isEqualTo("THE_AUTHOR");

assertThat(it.hasNext()).isTrue();
Expand Down
@@ -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 PopulateProfileKeyOfActivities < ActiveRecord::Migration
def self.up
execute_java_migration('org.sonar.db.version.v60.PopulateProfileKeyOfActivities')
end
end
Expand Up @@ -30,7 +30,7 @@

public class DatabaseVersion {

public static final int LAST_VERSION = 1_259;
public static final int LAST_VERSION = 1_260;

/**
* The minimum supported version which can be upgraded. Lower
Expand Down
Expand Up @@ -128,6 +128,7 @@
import org.sonar.db.version.v60.PopulateComponentUuidOfDuplicationsIndex;
import org.sonar.db.version.v60.PopulateComponentUuidOfMeasures;
import org.sonar.db.version.v60.PopulateLastUsedColumnOfRulesProfiles;
import org.sonar.db.version.v60.PopulateProfileKeyOfActivities;
import org.sonar.db.version.v60.PopulateUuidColumnOnSnapshots;
import org.sonar.db.version.v60.PopulateUuidColumnsOfProjects;
import org.sonar.db.version.v60.PopulateUuidColumnsOfResourceIndex;
Expand Down Expand Up @@ -243,6 +244,7 @@ protected void configureModule() {
AddLastUsedColumnToRulesProfiles.class,
PopulateLastUsedColumnOfRulesProfiles.class,
AddProfileKeyToActivities.class,
PopulateProfileKeyOfActivities.class,

// SNAPSHOTS.UUID
AddUuidColumnToSnapshots.class,
Expand Down
@@ -0,0 +1,63 @@
/*
* 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 java.sql.SQLException;
import java.util.Map;
import org.sonar.api.utils.KeyValueFormat;
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;

import static com.google.common.base.Preconditions.checkState;
import static org.apache.commons.lang.StringUtils.isNotBlank;

public class PopulateProfileKeyOfActivities extends BaseDataChange {

public PopulateProfileKeyOfActivities(Database db) {
super(db);
}

@Override
public void execute(Context context) throws SQLException {
MassUpdate massUpdate = context.prepareMassUpdate();
massUpdate.select("select id, data_field from activities where profile_key is null");
massUpdate.update("update activities set profile_key=?, data_field=? where id=?");
massUpdate.rowPluralName("activities");
massUpdate.execute(PopulateProfileKeyOfActivities::handle);
}

private static boolean handle(Select.Row row, SqlStatement update) throws SQLException {
int id = row.getInt(1);
String data = row.getString(2);
Map<String, String> fields = KeyValueFormat.parse(data);
String profileKey = fields.remove("profileKey");
checkState(isNotBlank(profileKey), "No profile key found in db row of activities.data_field", id);

update.setString(1, profileKey);
update.setString(2, KeyValueFormat.format(fields));
update.setInt(3, id);

return true;
}
}
Expand Up @@ -465,6 +465,7 @@ INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1255');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1256');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1257');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1258');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1259');

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;
Expand Up @@ -29,6 +29,6 @@ public class MigrationStepModuleTest {
public void verify_count_of_added_MigrationStep_types() {
ComponentContainer container = new ComponentContainer();
new MigrationStepModule().configure(container);
assertThat(container.size()).isEqualTo(115);
assertThat(container.size()).isEqualTo(116);
}
}
@@ -0,0 +1,97 @@
/*
* 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 java.sql.SQLException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.utils.System2;
import org.sonar.db.DbTester;

import static org.assertj.core.api.Assertions.assertThat;

public class PopulateProfileKeyOfActivitiesTest {
private static final String ACTIVITIES_TABLE = "activities";

@Rule
public ExpectedException expectedException = ExpectedException.none();
@Rule
public DbTester db = DbTester.createForSchema(System2.INSTANCE, PopulateProfileKeyOfActivitiesTest.class, "activities.sql");

PopulateProfileKeyOfActivities underTest = new PopulateProfileKeyOfActivities(db.database());

@Test
public void migration_has_no_effect_on_empty_tables() throws SQLException {
underTest.execute();

assertThat(db.countRowsOfTable(ACTIVITIES_TABLE)).isEqualTo(0);
}

@Test
public void migration_update_activities_profile_key() throws SQLException {
insertActivity("first-profile-key");
insertActivity("first-profile-key");
insertActivity("first-profile-key");
insertActivity("second-profile-key");
insertActivity("third-profile-key");

underTest.execute();

assertCountActivitiesWithProfile("first-profile-key", 3);
assertCountActivitiesWithProfile("second-profile-key", 1);
assertCountActivitiesWithProfile("third-profile-key", 1);
}

@Test
public void migration_is_reentrant() throws SQLException {
insertActivity("profile-key");
underTest.execute();
assertCountActivitiesWithProfile("profile-key", 1);

underTest.execute();
assertCountActivitiesWithProfile("profile-key", 1);
}

@Test
public void fail_if_activity_data_is_not_well_formatted() throws SQLException {
expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("Error during processing of row: ");

db.executeInsert(ACTIVITIES_TABLE, "data_field", "key=fakeKey");

underTest.execute();
}

private void assertCountActivitiesWithProfile(String profileKey, int expectedNumberOfActivities) {
assertThat(countActivitiesWithProfile(profileKey)).isEqualTo(expectedNumberOfActivities);
}

private int countActivitiesWithProfile(String qualityProfileKey) {
// profile key is removed from data_field
return db.countSql(String.format("select count(1) from activities where profile_key='%s' and data_field='key=fakeKey'", qualityProfileKey));
}

private void insertActivity(String profileKey) {
db.executeInsert(ACTIVITIES_TABLE,
"data_field", "key=fakeKey;profileKey=" + profileKey);
}
}
@@ -0,0 +1,11 @@
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),
"CREATED_AT" TIMESTAMP,
"USER_LOGIN" VARCHAR(255),
"LOG_TYPE" VARCHAR(250),
"LOG_ACTION" VARCHAR(250),
"LOG_MESSAGE" VARCHAR(250),
"DATA_FIELD" CLOB(2147483647)
);

0 comments on commit da6dbbc

Please sign in to comment.