From e99a948405e47182d419353341560711ee8f40bf Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Wed, 18 Dec 2013 16:39:58 +0100 Subject: [PATCH] SONAR-4535 create activate / deactivate / update severity actions in QProfiles --- .../core/persistence/DatabaseVersion.java | 2 +- .../core/qualityprofile/db/ActiveRuleDao.java | 64 ++++++++++ .../core/qualityprofile/db/ActiveRuleDto.java | 12 +- .../qualityprofile/db/ActiveRuleMapper.java | 16 +++ .../java/org/sonar/core/rule/RuleDao.java | 8 +- .../org/sonar/core/rule/RuleParamDto.java | 18 ++- .../org/sonar/core/persistence/rows-h2.sql | 2 + .../org/sonar/core/persistence/schema-h2.ddl | 1 + .../qualityprofile/db/ActiveRuleMapper.xml | 61 ++++++++-- .../qualityprofile/db/ActiveRuleDaoTest.java | 75 ++++++++++++ .../db/ActiveRuleDaoTest/delete-result.xml | 9 ++ .../delete_parameters-result.xml | 14 +++ .../db/ActiveRuleDaoTest/empty.xml | 3 + .../insertParameter-result.xml | 2 +- .../db/ActiveRuleDaoTest/shared.xml | 14 +++ .../db/ActiveRuleDaoTest/update-result.xml | 9 ++ .../java/org/sonar/api/rule/Severity.java | 4 + .../java/org/sonar/api/rules/ActiveRule.java | 2 +- .../org/sonar/api/rules/ActiveRuleParam.java | 26 +++-- .../org/sonar/api/rules/RulePriority.java | 5 +- .../server/configuration/ProfilesBackup.java | 18 +-- .../server/configuration/ProfilesManager.java | 1 + .../qualityprofile/QProfileOperations.java | 110 ++++++++++++++++-- .../server/qualityprofile/QProfiles.java | 68 ++++++++--- .../sonar/server/user/RubyUserSession.java | 4 +- .../org/sonar/server/user/UserSession.java | 12 ++ .../app/controllers/application_controller.rb | 4 +- ...parameter_key_to_active_rule_parameters.rb | 30 +++++ ...rameters_name_to_active_rule_parameters.rb | 47 ++++++++ .../QProfileOperationsTest.java | 106 ++++++++++++++++- .../server/qualityprofile/QProfilesTest.java | 51 +++++++- .../sonar/server/user/MockUserSession.java | 5 + .../server/user/RubyUserSessionTest.java | 6 +- .../shouldActivateInChildren-result.xml | 4 +- .../shouldActivateInChildren.xml | 2 +- .../RuleChangeTest/ruleReverted.xml | 6 +- 36 files changed, 726 insertions(+), 95 deletions(-) create mode 100644 sonar-core/src/test/resources/org/sonar/core/qualityprofile/db/ActiveRuleDaoTest/delete-result.xml create mode 100644 sonar-core/src/test/resources/org/sonar/core/qualityprofile/db/ActiveRuleDaoTest/delete_parameters-result.xml create mode 100644 sonar-core/src/test/resources/org/sonar/core/qualityprofile/db/ActiveRuleDaoTest/empty.xml create mode 100644 sonar-core/src/test/resources/org/sonar/core/qualityprofile/db/ActiveRuleDaoTest/shared.xml create mode 100644 sonar-core/src/test/resources/org/sonar/core/qualityprofile/db/ActiveRuleDaoTest/update-result.xml create mode 100644 sonar-server/src/main/webapp/WEB-INF/db/migrate/483_add_rules_parameter_key_to_active_rule_parameters.rb create mode 100644 sonar-server/src/main/webapp/WEB-INF/db/migrate/484_copy_rule_parameters_name_to_active_rule_parameters.rb diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java b/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java index f9785136f17..d2ca80341e3 100644 --- a/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java +++ b/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java @@ -33,7 +33,7 @@ import java.util.List; */ public class DatabaseVersion implements BatchComponent, ServerComponent { - public static final int LAST_VERSION = 482; + public static final int LAST_VERSION = 484; public static enum Status { UP_TO_DATE, REQUIRES_UPGRADE, REQUIRES_DOWNGRADE, FRESH_INSTALL diff --git a/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/ActiveRuleDao.java b/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/ActiveRuleDao.java index 2346103e655..00dc167dced 100644 --- a/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/ActiveRuleDao.java +++ b/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/ActiveRuleDao.java @@ -32,6 +32,28 @@ public class ActiveRuleDao implements ServerComponent { this.mybatis = mybatis; } + public ActiveRuleDto selectById(Integer id) { + SqlSession session = mybatis.openSession(); + try { + return session.getMapper(ActiveRuleMapper.class).selectById(id); + } finally { + MyBatis.closeQuietly(session); + } + } + + public ActiveRuleDto selectByProfileAndRule(Integer profileId, Integer ruleId) { + SqlSession session = mybatis.openSession(); + try { + return selectByProfileAndRule(profileId, ruleId, session); + } finally { + MyBatis.closeQuietly(session); + } + } + + public ActiveRuleDto selectByProfileAndRule(Integer profileId, Integer ruleId, SqlSession session) { + return session.getMapper(ActiveRuleMapper.class).selectByProfileAndRule(profileId, ruleId); + } + public void insert(ActiveRuleDto dto, SqlSession session) { session.getMapper(ActiveRuleMapper.class).insert(dto); } @@ -46,6 +68,20 @@ public class ActiveRuleDao implements ServerComponent { } } + public void update(ActiveRuleDto dto, SqlSession session) { + session.getMapper(ActiveRuleMapper.class).update(dto); + } + + public void update(ActiveRuleDto dto) { + SqlSession session = mybatis.openSession(); + try { + update(dto, session); + session.commit(); + } finally { + MyBatis.closeQuietly(session); + } + } + public void insert(ActiveRuleParamDto dto, SqlSession session) { session.getMapper(ActiveRuleMapper.class).insertParameter(dto); } @@ -60,4 +96,32 @@ public class ActiveRuleDao implements ServerComponent { } } + public void delete(Integer activeRuleId, SqlSession session) { + session.getMapper(ActiveRuleMapper.class).delete(activeRuleId); + } + + public void delete(Integer activeRuleId) { + SqlSession session = mybatis.openSession(); + try { + delete(activeRuleId, session); + session.commit(); + } finally { + MyBatis.closeQuietly(session); + } + } + + public void deleteParameters(Integer activeRuleIdo, SqlSession session) { + session.getMapper(ActiveRuleMapper.class).deleteParameters(activeRuleIdo); + } + + public void deleteParameters(Integer activeRuleId) { + SqlSession session = mybatis.openSession(); + try { + deleteParameters(activeRuleId, session); + session.commit(); + } finally { + MyBatis.closeQuietly(session); + } + } + } diff --git a/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/ActiveRuleDto.java b/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/ActiveRuleDto.java index f04dd148cb8..07137e58fd3 100644 --- a/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/ActiveRuleDto.java +++ b/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/ActiveRuleDto.java @@ -83,31 +83,35 @@ public class ActiveRuleDto { return noteCreatedAt; } - public void setNoteCreatedAt(Date noteCreatedAt) { + public ActiveRuleDto setNoteCreatedAt(Date noteCreatedAt) { this.noteCreatedAt = noteCreatedAt; + return this; } public Date getNoteUpdatedAt() { return noteUpdatedAt; } - public void setNoteUpdatedAt(Date noteUpdatedAt) { + public ActiveRuleDto setNoteUpdatedAt(Date noteUpdatedAt) { this.noteUpdatedAt = noteUpdatedAt; + return this; } public String getNoteUserLogin() { return noteUserLogin; } - public void setNoteUserLogin(String noteUserLogin) { + public ActiveRuleDto setNoteUserLogin(String noteUserLogin) { this.noteUserLogin = noteUserLogin; + return this; } public String getNoteData() { return noteData; } - public void setNoteData(String noteData) { + public ActiveRuleDto setNoteData(String noteData) { this.noteData = noteData; + return this; } } diff --git a/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/ActiveRuleMapper.java b/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/ActiveRuleMapper.java index e4068570c50..0805237691d 100644 --- a/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/ActiveRuleMapper.java +++ b/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/ActiveRuleMapper.java @@ -20,10 +20,26 @@ package org.sonar.core.qualityprofile.db; +import org.apache.ibatis.annotations.Param; + +import javax.annotation.CheckForNull; + public interface ActiveRuleMapper { + @CheckForNull + ActiveRuleDto selectById(Integer id); + + @CheckForNull + ActiveRuleDto selectByProfileAndRule(@Param("profileId") Integer profileId, @Param("ruleId") Integer ruleId); + void insert(ActiveRuleDto dto); + void update(ActiveRuleDto dto); + void insertParameter(ActiveRuleParamDto dto); + void delete(Integer activeRuleId); + + void deleteParameters(Integer activeRuleId); + } diff --git a/sonar-core/src/main/java/org/sonar/core/rule/RuleDao.java b/sonar-core/src/main/java/org/sonar/core/rule/RuleDao.java index 82001371398..60fcf4b6b74 100644 --- a/sonar-core/src/main/java/org/sonar/core/rule/RuleDao.java +++ b/sonar-core/src/main/java/org/sonar/core/rule/RuleDao.java @@ -85,7 +85,7 @@ public class RuleDao implements BatchComponent, ServerComponent { public void insert(Collection rules) { SqlSession session = mybatis.openBatchSession(); try { - for (RuleDto rule: rules) { + for (RuleDto rule : rules) { getMapper(session).insert(rule); } session.commit(); @@ -106,12 +106,16 @@ public class RuleDao implements BatchComponent, ServerComponent { public List selectParameters(Long id) { SqlSession session = mybatis.openSession(); try { - return getMapper(session).selectParamsForRule(id); + return selectParameters(id, session); } finally { MyBatis.closeQuietly(session); } } + public List selectParameters(Long id, SqlSession session) { + return getMapper(session).selectParamsForRule(id); + } + private RuleMapper getMapper(SqlSession session) { return session.getMapper(RuleMapper.class); } diff --git a/sonar-core/src/main/java/org/sonar/core/rule/RuleParamDto.java b/sonar-core/src/main/java/org/sonar/core/rule/RuleParamDto.java index 793b56d22bf..7a4f999f743 100644 --- a/sonar-core/src/main/java/org/sonar/core/rule/RuleParamDto.java +++ b/sonar-core/src/main/java/org/sonar/core/rule/RuleParamDto.java @@ -33,47 +33,53 @@ public class RuleParamDto { return id; } - public void setId(int id) { + public RuleParamDto setId(int id) { this.id = id; + return this; } public Long getRuleId() { return ruleId; } - public void setRuleId(Long ruleId) { + public RuleParamDto setRuleId(Long ruleId) { this.ruleId = ruleId; + return this; } public String getName() { return name; } - public void setName(String name) { + public RuleParamDto setName(String name) { this.name = name; + return this; } public String getType() { return type; } - public void setType(String type) { + public RuleParamDto setType(String type) { this.type = type; + return this; } public String getDefaultValue() { return defaultValue; } - public void setDefaultValue(String defaultValue) { + public RuleParamDto setDefaultValue(String defaultValue) { this.defaultValue = defaultValue; + return this; } public String getDescription() { return description; } - public void setDescription(String description) { + public RuleParamDto setDescription(String description) { this.description = description; + return this; } } diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql b/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql index 3c128f020a1..e0ce7afbee1 100644 --- a/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql +++ b/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql @@ -193,6 +193,8 @@ INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('466'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('480'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('481'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('482'); +INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('483'); +INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('484'); INSERT INTO USERS(ID, LOGIN, NAME, EMAIL, CRYPTED_PASSWORD, SALT, CREATED_AT, UPDATED_AT, REMEMBER_TOKEN, REMEMBER_TOKEN_EXPIRES_AT) VALUES (1, 'admin', 'Administrator', '', 'a373a0e667abb2604c1fd571eb4ad47fe8cc0878', '48bc4b0d93179b5103fd3885ea9119498e9d161b', '2011-09-26 22:27:48.0', '2011-09-26 22:27:48.0', null, null); ALTER TABLE USERS ALTER COLUMN ID RESTART WITH 2; diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl b/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl index 670337719f1..1ef71efa658 100644 --- a/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl +++ b/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl @@ -319,6 +319,7 @@ CREATE TABLE "ACTIVE_RULE_PARAMETERS" ( "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), "ACTIVE_RULE_ID" INTEGER NOT NULL, "RULES_PARAMETER_ID" INTEGER NOT NULL, + "RULES_PARAMETER_KEY" VARCHAR(128), "VALUE" VARCHAR(4000) ); diff --git a/sonar-core/src/main/resources/org/sonar/core/qualityprofile/db/ActiveRuleMapper.xml b/sonar-core/src/main/resources/org/sonar/core/qualityprofile/db/ActiveRuleMapper.xml index e47f8f4abae..009bf47efc1 100644 --- a/sonar-core/src/main/resources/org/sonar/core/qualityprofile/db/ActiveRuleMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/qualityprofile/db/ActiveRuleMapper.xml @@ -4,27 +4,64 @@ - p.id, - p.profile_id as profileId, - p.rule_id as ruleId, - p.failure_level as severity, - p.version as version, - p.used_profile as used, - p.note_created_at, - p.note_updated_at, - p.note_user_login, - p.note_data + a.id, + a.profile_id as profileId, + a.rule_id as ruleId, + a.failure_level as severity, + a.inheritance as inheritance, + a.note_data as noteData, + a.note_user_login as noteUserLogin, + a.note_updated_at as noteUpdatedAt, + a.note_created_at as noteCreatedAt + + + + INSERT INTO active_rules (profile_id, rule_id, failure_level, inheritance, note_created_at, note_updated_at, note_user_login, note_data) VALUES (#{profileId}, #{ruleId}, #{severity}, #{inheritance}, #{noteCreatedAt}, #{noteUpdatedAt}, #{noteUserLogin}, #{noteData}) + + UPDATE active_rules SET + profile_id=#{profileId}, + rule_id=#{ruleId}, + failure_level=#{severity}, + inheritance=#{inheritance}, + note_created_at=#{noteCreatedAt}, + note_updated_at=#{noteUpdatedAt}, + note_user_login=#{noteUserLogin}, + note_data=#{noteData} + WHERE id=#{id} + + - INSERT INTO active_rule_parameters (active_rule_id, rules_parameter_id, value) - VALUES (#{activeRuleId}, #{rulesParameterId}, #{value}) + INSERT INTO active_rule_parameters (active_rule_id, rules_parameter_id, rules_parameter_key, value) + VALUES (#{activeRuleId}, #{rulesParameterId}, #{key}, #{value}) + + DELETE FROM active_rules WHERE id=#{id} + + + + DELETE FROM active_rule_parameters WHERE active_rule_id=#{id} + + diff --git a/sonar-core/src/test/java/org/sonar/core/qualityprofile/db/ActiveRuleDaoTest.java b/sonar-core/src/test/java/org/sonar/core/qualityprofile/db/ActiveRuleDaoTest.java index ddfe06f8c65..53114042072 100644 --- a/sonar-core/src/test/java/org/sonar/core/qualityprofile/db/ActiveRuleDaoTest.java +++ b/sonar-core/src/test/java/org/sonar/core/qualityprofile/db/ActiveRuleDaoTest.java @@ -22,8 +22,11 @@ package org.sonar.core.qualityprofile.db; import org.junit.Before; import org.junit.Test; +import org.sonar.api.utils.DateUtils; import org.sonar.core.persistence.AbstractDaoTestCase; +import static org.fest.assertions.Assertions.assertThat; + public class ActiveRuleDaoTest extends AbstractDaoTestCase { ActiveRuleDao dao; @@ -33,8 +36,42 @@ public class ActiveRuleDaoTest extends AbstractDaoTestCase { dao = new ActiveRuleDao(getMyBatis()); } + @Test + public void select_by_id() { + setupData("shared"); + + ActiveRuleDto result = dao.selectById(1); + assertThat(result.getId()).isEqualTo(1); + assertThat(result.getProfileId()).isEqualTo(1); + assertThat(result.getRulId()).isEqualTo(10); + assertThat(result.getSeverity()).isEqualTo(2); + assertThat(result.getInheritance()).isEqualTo("INHERITED"); + assertThat(result.getNoteData()).isEqualTo("some note"); + assertThat(result.getNoteUserLogin()).isEqualTo("henry"); + assertThat(result.getNoteCreatedAt()).isEqualTo(DateUtils.parseDate("2013-12-18")); + assertThat(result.getNoteUpdatedAt()).isEqualTo(DateUtils.parseDate("2013-12-18")); + } + + @Test + public void select_by_profile_and_rule() { + setupData("shared"); + + ActiveRuleDto result = dao.selectByProfileAndRule(1, 10); + assertThat(result.getId()).isEqualTo(1); + assertThat(result.getProfileId()).isEqualTo(1); + assertThat(result.getRulId()).isEqualTo(10); + assertThat(result.getSeverity()).isEqualTo(2); + assertThat(result.getInheritance()).isEqualTo("INHERITED"); + assertThat(result.getNoteData()).isEqualTo("some note"); + assertThat(result.getNoteUserLogin()).isEqualTo("henry"); + assertThat(result.getNoteCreatedAt()).isEqualTo(DateUtils.parseDate("2013-12-18")); + assertThat(result.getNoteUpdatedAt()).isEqualTo(DateUtils.parseDate("2013-12-18")); + } + @Test public void insert() { + setupData("empty"); + ActiveRuleDto dto = new ActiveRuleDto() .setProfileId(1) .setRuleId(10) @@ -46,15 +83,53 @@ public class ActiveRuleDaoTest extends AbstractDaoTestCase { checkTables("insert", "active_rules"); } + @Test + public void update() { + setupData("shared"); + + ActiveRuleDto dto = new ActiveRuleDto() + .setId(1) + .setProfileId(1) + .setRuleId(10) + .setSeverity(4) + .setInheritance(null) + .setNoteData("text"); + + dao.update(dto); + + checkTables("update", "active_rules"); + } + @Test public void insert_parameter() { + setupData("empty"); + ActiveRuleParamDto dto = new ActiveRuleParamDto() .setActiveRuleId(1) .setRulesParameterId(1) + .setKey("max") .setValue("20"); dao.insert(dto); checkTables("insertParameter", "active_rule_parameters"); } + + @Test + public void delete() { + setupData("shared"); + + dao.delete(1); + + checkTables("delete", "active_rules"); + } + + @Test + public void delete_parameters() { + setupData("shared"); + + dao.deleteParameters(1); + + checkTables("delete_parameters", "active_rule_parameters"); + } } diff --git a/sonar-core/src/test/resources/org/sonar/core/qualityprofile/db/ActiveRuleDaoTest/delete-result.xml b/sonar-core/src/test/resources/org/sonar/core/qualityprofile/db/ActiveRuleDaoTest/delete-result.xml new file mode 100644 index 00000000000..2a65aa4de98 --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/qualityprofile/db/ActiveRuleDaoTest/delete-result.xml @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/sonar-core/src/test/resources/org/sonar/core/qualityprofile/db/ActiveRuleDaoTest/delete_parameters-result.xml b/sonar-core/src/test/resources/org/sonar/core/qualityprofile/db/ActiveRuleDaoTest/delete_parameters-result.xml new file mode 100644 index 00000000000..b986e9f8b0a --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/qualityprofile/db/ActiveRuleDaoTest/delete_parameters-result.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/sonar-core/src/test/resources/org/sonar/core/qualityprofile/db/ActiveRuleDaoTest/empty.xml b/sonar-core/src/test/resources/org/sonar/core/qualityprofile/db/ActiveRuleDaoTest/empty.xml new file mode 100644 index 00000000000..871dedcb5e9 --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/qualityprofile/db/ActiveRuleDaoTest/empty.xml @@ -0,0 +1,3 @@ + + + diff --git a/sonar-core/src/test/resources/org/sonar/core/qualityprofile/db/ActiveRuleDaoTest/insertParameter-result.xml b/sonar-core/src/test/resources/org/sonar/core/qualityprofile/db/ActiveRuleDaoTest/insertParameter-result.xml index 69ec55f8007..ed2e17d48d3 100644 --- a/sonar-core/src/test/resources/org/sonar/core/qualityprofile/db/ActiveRuleDaoTest/insertParameter-result.xml +++ b/sonar-core/src/test/resources/org/sonar/core/qualityprofile/db/ActiveRuleDaoTest/insertParameter-result.xml @@ -1,5 +1,5 @@ - + diff --git a/sonar-core/src/test/resources/org/sonar/core/qualityprofile/db/ActiveRuleDaoTest/shared.xml b/sonar-core/src/test/resources/org/sonar/core/qualityprofile/db/ActiveRuleDaoTest/shared.xml new file mode 100644 index 00000000000..3d92151f292 --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/qualityprofile/db/ActiveRuleDaoTest/shared.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/sonar-core/src/test/resources/org/sonar/core/qualityprofile/db/ActiveRuleDaoTest/update-result.xml b/sonar-core/src/test/resources/org/sonar/core/qualityprofile/db/ActiveRuleDaoTest/update-result.xml new file mode 100644 index 00000000000..87b48ac77a3 --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/qualityprofile/db/ActiveRuleDaoTest/update-result.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rule/Severity.java b/sonar-plugin-api/src/main/java/org/sonar/api/rule/Severity.java index 7fe9fe68348..24e320f4031 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/rule/Severity.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/rule/Severity.java @@ -43,6 +43,10 @@ public final class Severity { return ALL.get(ordinal); } + public static Integer ordinal(String severiy) { + return ALL.indexOf(severiy); + } + private Severity() { // utility } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/ActiveRule.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/ActiveRule.java index 8b0d89c0c8e..55772eac07d 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/rules/ActiveRule.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/ActiveRule.java @@ -208,7 +208,7 @@ public class ActiveRule implements Cloneable { public ActiveRule setParameter(String key, String value) { RuleParam ruleParameter = rule.getParam(key); if (ruleParameter != null) { - activeRuleParams.add(new ActiveRuleParam(this, ruleParameter, value)); + activeRuleParams.add(new ActiveRuleParam(this, ruleParameter, key, value)); } return this; } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/ActiveRuleParam.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/ActiveRuleParam.java index 1db0a4de32c..f634ce38110 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/rules/ActiveRuleParam.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/ActiveRuleParam.java @@ -19,14 +19,7 @@ */ package org.sonar.api.rules; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.Table; +import javax.persistence.*; @Entity @Table(name = "active_rule_parameters") @@ -45,6 +38,9 @@ public class ActiveRuleParam implements Cloneable { @JoinColumn(name = "rules_parameter_id") private RuleParam ruleParam; + @Column(name = "rules_parameter_key", updatable = false, nullable = false, length = 128) + private String paramKey; + @Column(name = "value", updatable = false, nullable = true, length = 4000) private String value; @@ -71,10 +67,11 @@ public class ActiveRuleParam implements Cloneable { * @deprecated visibility should be decreased to protected or package */ @Deprecated - public ActiveRuleParam(ActiveRule activeRule, RuleParam ruleParam, String value) { + public ActiveRuleParam(ActiveRule activeRule, RuleParam ruleParam, String paramKey, String value) { this.activeRule = activeRule; this.ruleParam = ruleParam; this.value = value; + this.paramKey = paramKey; } public ActiveRule getActiveRule() { @@ -109,6 +106,15 @@ public class ActiveRuleParam implements Cloneable { this.value = value; } + public String getParamKey() { + return paramKey; + } + + public void setParamKey(String paramKey) { + this.paramKey = paramKey; + } + + public String getKey() { return ruleParam.getKey(); } @@ -132,7 +138,7 @@ public class ActiveRuleParam implements Cloneable { @Override public Object clone() { - return new ActiveRuleParam(getActiveRule(), getRuleParam(), getValue()); + return new ActiveRuleParam(getActiveRule(), getRuleParam(), getParamKey(), getValue()); } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/RulePriority.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/RulePriority.java index 35f0a000582..3f4bb152db8 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/rules/RulePriority.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/RulePriority.java @@ -35,7 +35,6 @@ public enum RulePriority { private static final String UNKNOWN_PRIORITY = "Unknown priority "; - /** * A class to map priority level prior to Sonar 1.10 to the new ones * @@ -78,4 +77,8 @@ public enum RulePriority { } throw new IllegalArgumentException(UNKNOWN_PRIORITY + checkPriority); } + + public static RulePriority valueOfInt(int ordinal) { + return RulePriority.values()[ordinal]; + } } diff --git a/sonar-server/src/main/java/org/sonar/server/configuration/ProfilesBackup.java b/sonar-server/src/main/java/org/sonar/server/configuration/ProfilesBackup.java index c4291cd73dd..db5c6770c4b 100644 --- a/sonar-server/src/main/java/org/sonar/server/configuration/ProfilesBackup.java +++ b/sonar-server/src/main/java/org/sonar/server/configuration/ProfilesBackup.java @@ -19,8 +19,6 @@ */ package org.sonar.server.configuration; -import org.sonar.core.preview.PreviewCache; - import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.converters.Converter; import com.thoughtworks.xstream.converters.MarshallingContext; @@ -33,19 +31,11 @@ import org.sonar.api.database.DatabaseSession; import org.sonar.api.measures.Metric; import org.sonar.api.profiles.Alert; import org.sonar.api.profiles.RulesProfile; -import org.sonar.api.rules.ActiveRule; -import org.sonar.api.rules.ActiveRuleParam; -import org.sonar.api.rules.Rule; -import org.sonar.api.rules.RuleParam; -import org.sonar.api.rules.RulePriority; +import org.sonar.api.rules.*; +import org.sonar.core.preview.PreviewCache; import org.sonar.jpa.dao.RulesDao; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; +import java.util.*; public class ProfilesBackup implements Backupable { @@ -244,7 +234,7 @@ public class ProfilesBackup implements Backupable { while (reader.hasMoreChildren()) { reader.moveDown(); Map valuesParam = readNode(reader); - ActiveRuleParam activeRuleParam = new ActiveRuleParam(null, new RuleParam(null, valuesParam.get(KEY), null, null), + ActiveRuleParam activeRuleParam = new ActiveRuleParam(null, new RuleParam(null, valuesParam.get(KEY), null, null), valuesParam.get(KEY), valuesParam.get(VALUE)); params.add(activeRuleParam); reader.moveUp(); diff --git a/sonar-server/src/main/java/org/sonar/server/configuration/ProfilesManager.java b/sonar-server/src/main/java/org/sonar/server/configuration/ProfilesManager.java index b594dab10e0..d05d08233ea 100644 --- a/sonar-server/src/main/java/org/sonar/server/configuration/ProfilesManager.java +++ b/sonar-server/src/main/java/org/sonar/server/configuration/ProfilesManager.java @@ -126,6 +126,7 @@ public class ProfilesManager extends BaseDao { dryRunCache.reportGlobalModification(); } + /** * Rule was activated */ diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileOperations.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileOperations.java index 3332e99b08b..3929bb79164 100644 --- a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileOperations.java +++ b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileOperations.java @@ -29,8 +29,10 @@ import org.sonar.api.ServerComponent; import org.sonar.api.profiles.ProfileExporter; import org.sonar.api.profiles.ProfileImporter; import org.sonar.api.profiles.RulesProfile; +import org.sonar.api.rule.Severity; import org.sonar.api.rules.ActiveRule; import org.sonar.api.rules.ActiveRuleParam; +import org.sonar.api.rules.Rule; import org.sonar.api.rules.RulePriority; import org.sonar.api.utils.ValidationMessages; import org.sonar.core.permission.GlobalPermissions; @@ -39,10 +41,15 @@ import org.sonar.core.preview.PreviewCache; import org.sonar.core.properties.PropertiesDao; import org.sonar.core.properties.PropertyDto; import org.sonar.core.qualityprofile.db.*; +import org.sonar.core.rule.RuleDao; +import org.sonar.core.rule.RuleParamDto; +import org.sonar.server.configuration.ProfilesManager; import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.rule.RuleRegistry; import org.sonar.server.user.UserSession; +import javax.annotation.CheckForNull; + import java.io.StringReader; import java.util.List; import java.util.Map; @@ -56,30 +63,38 @@ public class QProfileOperations implements ServerComponent { private final MyBatis myBatis; private final QualityProfileDao dao; private final ActiveRuleDao activeRuleDao; + private final RuleDao ruleDao; private final PropertiesDao propertiesDao; private final List exporters; private final List importers; private final PreviewCache dryRunCache; private final RuleRegistry ruleRegistry; + // Should not be used as it still uses Hibernate + private final ProfilesManager profilesManager; + /** * Used by pico when no plugin provide profile exporter / importer */ - public QProfileOperations(MyBatis myBatis, QualityProfileDao dao, ActiveRuleDao activeRuleDao, PropertiesDao propertiesDao, - PreviewCache dryRunCache, RuleRegistry ruleRegistry) { - this(myBatis, dao, activeRuleDao, propertiesDao, Lists.newArrayList(), Lists.newArrayList(), dryRunCache, ruleRegistry); + public QProfileOperations(MyBatis myBatis, QualityProfileDao dao, ActiveRuleDao activeRuleDao, RuleDao ruleDao, PropertiesDao propertiesDao, + PreviewCache dryRunCache, RuleRegistry ruleRegistry, ProfilesManager profilesManager) { + this(myBatis, dao, activeRuleDao, ruleDao, propertiesDao, Lists.newArrayList(), Lists.newArrayList(), dryRunCache, ruleRegistry, + profilesManager); } - public QProfileOperations(MyBatis myBatis, QualityProfileDao dao, ActiveRuleDao activeRuleDao, PropertiesDao propertiesDao, - List exporters, List importers, PreviewCache dryRunCache, RuleRegistry ruleRegistry) { + public QProfileOperations(MyBatis myBatis, QualityProfileDao dao, ActiveRuleDao activeRuleDao, RuleDao ruleDao, PropertiesDao propertiesDao, + List exporters, List importers, PreviewCache dryRunCache, RuleRegistry ruleRegistry, + ProfilesManager profilesManager) { this.myBatis = myBatis; this.dao = dao; this.activeRuleDao = activeRuleDao; + this.ruleDao = ruleDao; this.propertiesDao = propertiesDao; this.exporters = exporters; this.importers = importers; this.dryRunCache = dryRunCache; this.ruleRegistry = ruleRegistry; + this.profilesManager = profilesManager; } public NewProfileResult newProfile(String name, String language, Map xmlProfilesByPlugin, UserSession userSession) { @@ -88,18 +103,18 @@ public class QProfileOperations implements ServerComponent { NewProfileResult result = new NewProfileResult(); List importProfiles = readProfilesFromXml(result, xmlProfilesByPlugin); - SqlSession sqlSession = myBatis.openSession(); + SqlSession session = myBatis.openSession(); try { QualityProfileDto dto = new QualityProfileDto().setName(name).setLanguage(language).setVersion(1).setUsed(false); - dao.insert(dto, sqlSession); + dao.insert(dto, session); for (RulesProfile rulesProfile : importProfiles) { - importProfile(dto, rulesProfile, sqlSession); + importProfile(dto, rulesProfile, session); } result.setProfile(QProfile.from(dto)); - sqlSession.commit(); + session.commit(); dryRunCache.reportGlobalModification(); } finally { - MyBatis.closeQuietly(sqlSession); + MyBatis.closeQuietly(session); } return result; } @@ -115,6 +130,69 @@ public class QProfileOperations implements ServerComponent { propertiesDao.setProperty(new PropertyDto().setKey(PROPERTY_PREFIX + qualityProfile.getLanguage()).setValue(qualityProfile.getName())); } + public void activateRule(QualityProfileDto qualityProfile, Rule rule, String severity, UserSession userSession) { + checkPermission(userSession); + + SqlSession session = myBatis.openSession(); + try { + ActiveRuleDto activeRule = findActiveRule(qualityProfile, rule); + if (activeRule == null) { + newActiveRule(qualityProfile, rule, severity, userSession, session); + } else { + updateSeverity(activeRule, severity, userSession, session); + } + } finally { + MyBatis.closeQuietly(session); + } + } + + private void newActiveRule(QualityProfileDto qualityProfile, Rule rule, String severity, UserSession userSession, SqlSession session) { + ActiveRuleDto activeRuleDto = new ActiveRuleDto() + .setProfileId(qualityProfile.getId()) + .setRuleId(rule.getId()) + .setSeverity(Severity.ordinal(severity)); + activeRuleDao.insert(activeRuleDto, session); + + List ruleParams = ruleDao.selectParameters(rule.getId().longValue(), session); + for (RuleParamDto ruleParam : ruleParams) { + ActiveRuleParamDto activeRuleParam = new ActiveRuleParamDto() + .setActiveRuleId(activeRuleDto.getId()) + .setRulesParameterId(ruleParam.getId()) + .setKey(ruleParam.getName()) + .setValue(ruleParam.getDefaultValue()); + activeRuleDao.insert(activeRuleParam, session); + } + session.commit(); + + profilesManager.activated(qualityProfile.getId(), activeRuleDto.getId(), userSession.name()); + } + + private void updateSeverity(ActiveRuleDto activeRule, String newSeverity, UserSession userSession, SqlSession session) { + Integer oldSeverity = activeRule.getSeverity(); + activeRule.setSeverity(Severity.ordinal(newSeverity)); + activeRuleDao.update(activeRule, session); + session.commit(); + + profilesManager.ruleSeverityChanged(activeRule.getProfileId(), activeRule.getId(), RulePriority.valueOfInt(oldSeverity), RulePriority.valueOf(newSeverity), + userSession.name()); + } + + public void deactivateRule(QualityProfileDto qualityProfile, Rule rule, UserSession userSession) { + checkPermission(userSession); + + SqlSession session = myBatis.openSession(); + try { + ActiveRuleDto activeRule = validate(qualityProfile, rule); + + activeRuleDao.delete(activeRule.getId(), session); + activeRuleDao.deleteParameters(activeRule.getId(), session); + session.commit(); + profilesManager.deactivated(activeRule.getProfileId(), activeRule.getId(), userSession.name()); + } finally { + MyBatis.closeQuietly(session); + } + } + private List readProfilesFromXml(NewProfileResult result, Map xmlProfilesByPlugin) { List profiles = newArrayList(); ValidationMessages messages = ValidationMessages.create(); @@ -189,4 +267,16 @@ public class QProfileOperations implements ServerComponent { userSession.checkGlobalPermission(GlobalPermissions.QUALITY_PROFILE_ADMIN); } + private ActiveRuleDto validate(QualityProfileDto qualityProfile, Rule rule) { + ActiveRuleDto activeRuleDto = findActiveRule(qualityProfile, rule); + if (activeRuleDto == null) { + throw new BadRequestException("No rule has been activated on this profile."); + } + return activeRuleDto; + } + + @CheckForNull + private ActiveRuleDto findActiveRule(QualityProfileDto qualityProfile, Rule rule) { + return activeRuleDao.selectByProfileAndRule(qualityProfile.getId(), rule.getId()); + } } diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfiles.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfiles.java index c06b1a16d59..83c79be48ce 100644 --- a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfiles.java +++ b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfiles.java @@ -23,6 +23,8 @@ package org.sonar.server.qualityprofile; import com.google.common.base.Strings; import org.sonar.api.ServerComponent; import org.sonar.api.component.Component; +import org.sonar.api.rules.Rule; +import org.sonar.api.rules.RuleFinder; import org.sonar.core.component.ComponentDto; import org.sonar.core.qualityprofile.db.QualityProfileDao; import org.sonar.core.qualityprofile.db.QualityProfileDto; @@ -43,6 +45,7 @@ public class QProfiles implements ServerComponent { private final QualityProfileDao qualityProfileDao; private final ResourceDao resourceDao; + private final RuleFinder ruleFinder; private final QProfileProjectService projectService; @@ -50,10 +53,11 @@ public class QProfiles implements ServerComponent { private final QProfileOperations operations; private final ProfileRules rules; - public QProfiles(QualityProfileDao qualityProfileDao, ResourceDao resourceDao, QProfileProjectService projectService, QProfileSearch search, + public QProfiles(QualityProfileDao qualityProfileDao, ResourceDao resourceDao, RuleFinder ruleFinder, QProfileProjectService projectService, QProfileSearch search, QProfileOperations operations, ProfileRules rules) { this.qualityProfileDao = qualityProfileDao; this.resourceDao = resourceDao; + this.ruleFinder = ruleFinder; this.projectService = projectService; this.search = search; this.operations = operations; @@ -145,7 +149,7 @@ public class QProfiles implements ServerComponent { } public void addProject(int profileId, long projectId) { - ComponentDto project = (ComponentDto) findNotNull(projectId); + ComponentDto project = (ComponentDto) findProjectNotNull(projectId); QualityProfileDto qualityProfile = findNotNull(profileId); projectService.addProject(qualityProfile, project, UserSession.get()); @@ -153,14 +157,14 @@ public class QProfiles implements ServerComponent { public void removeProject(int profileId, long projectId) { QualityProfileDto qualityProfile = findNotNull(profileId); - ComponentDto project = (ComponentDto) findNotNull(projectId); + ComponentDto project = (ComponentDto) findProjectNotNull(projectId); projectService.removeProject(qualityProfile, project, UserSession.get()); } public void removeProjectByLanguage(String language, long projectId) { Validation.checkMandatoryParameter(language, "language"); - ComponentDto project = (ComponentDto) findNotNull(projectId); + ComponentDto project = (ComponentDto) findProjectNotNull(projectId); projectService.removeProject(language, project, UserSession.get()); } @@ -189,16 +193,33 @@ public class QProfiles implements ServerComponent { return rules.countInactiveRules(query); } + public void activateRule(int profileId, int ruleId, String severity) { + QualityProfileDto qualityProfile = findNotNull(profileId); + Rule rule = findRuleNotNull(ruleId); + operations.activateRule(qualityProfile, rule, severity, UserSession.get()); + } + + public void deactivateRule(int profileId, int ruleId) { + QualityProfileDto qualityProfile = findNotNull(profileId); + Rule rule = findRuleNotNull(ruleId); + operations.deactivateRule(qualityProfile, rule, UserSession.get()); + } + + // + // Quality profile validation + // + private void validateNewProfile(String name, String language) { - validateName(name); + validateProfileName(name); Validation.checkMandatoryParameter(language, "language"); checkNotAlreadyExists(name, language); } private QualityProfileDto validateRenameProfile(Integer profileId, String newName) { - validateName(newName); + validateProfileName(newName); QualityProfileDto profileDto = findNotNull(profileId); if (!profileDto.getName().equals(newName)) { + // TODO move this check to the service checkNotAlreadyExists(newName, profileDto.getLanguage()); } return profileDto; @@ -220,14 +241,6 @@ public class QProfiles implements ServerComponent { return checkNotNull(qualityProfile); } - private Component findNotNull(long projectId) { - Component component = resourceDao.findById(projectId); - if (component == null) { - throw new NotFoundException("This project does not exists."); - } - return component; - } - private QualityProfileDto checkNotNull(QualityProfileDto qualityProfile) { if (qualityProfile == null) { throw new NotFoundException("This quality profile does not exists."); @@ -245,10 +258,35 @@ public class QProfiles implements ServerComponent { return qualityProfileDao.selectById(id); } - private void validateName(String name) { + private void validateProfileName(String name) { if (Strings.isNullOrEmpty(name)) { throw BadRequestException.ofL10n("quality_profiles.please_type_profile_name"); } } + // + // Project validation + // + + private Component findProjectNotNull(long projectId) { + Component component = resourceDao.findById(projectId); + if (component == null) { + throw new NotFoundException("This project does not exists."); + } + return component; + } + + // + // Rule validation + // + + private Rule findRuleNotNull(int ruleId) { + Rule rule = ruleFinder.findById(ruleId); + if (rule == null) { + throw new NotFoundException("This rule does not exists."); + } + return rule; + } + + } diff --git a/sonar-server/src/main/java/org/sonar/server/user/RubyUserSession.java b/sonar-server/src/main/java/org/sonar/server/user/RubyUserSession.java index e603cde6813..acd87010081 100644 --- a/sonar-server/src/main/java/org/sonar/server/user/RubyUserSession.java +++ b/sonar-server/src/main/java/org/sonar/server/user/RubyUserSession.java @@ -27,8 +27,8 @@ public class RubyUserSession { /** * Invoked by Ruby code - see application_controller.rb */ - public static void setSession(@Nullable Integer userId, @Nullable String login, @Nullable String localeRubyKey) { - UserSession session = new UserSession().setLogin(login).setUserId(userId).setLocale(JRubyI18n.toLocale(localeRubyKey)); + public static void setSession(@Nullable Integer userId, @Nullable String login, @Nullable String name, @Nullable String localeRubyKey) { + UserSession session = new UserSession().setLogin(login).setName(name).setUserId(userId).setLocale(JRubyI18n.toLocale(localeRubyKey)); UserSession.set(session); } diff --git a/sonar-server/src/main/java/org/sonar/server/user/UserSession.java b/sonar-server/src/main/java/org/sonar/server/user/UserSession.java index 9a991ff175c..d2c92f31158 100644 --- a/sonar-server/src/main/java/org/sonar/server/user/UserSession.java +++ b/sonar-server/src/main/java/org/sonar/server/user/UserSession.java @@ -32,6 +32,7 @@ import org.sonar.server.platform.Platform; import javax.annotation.CheckForNull; import javax.annotation.Nullable; + import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -52,6 +53,7 @@ public class UserSession { private Integer userId; private String login; + private String name; private Locale locale = Locale.ENGLISH; List globalPermissions = null; @@ -71,6 +73,16 @@ public class UserSession { return this; } + @CheckForNull + public String name() { + return name; + } + + UserSession setName(@Nullable String s) { + this.name = Strings.emptyToNull(s); + return this; + } + @CheckForNull public Integer userId() { return userId; diff --git a/sonar-server/src/main/webapp/WEB-INF/app/controllers/application_controller.rb b/sonar-server/src/main/webapp/WEB-INF/app/controllers/application_controller.rb index d073a943ed0..64bcc347291 100644 --- a/sonar-server/src/main/webapp/WEB-INF/app/controllers/application_controller.rb +++ b/sonar-server/src/main/webapp/WEB-INF/app/controllers/application_controller.rb @@ -100,9 +100,9 @@ class ApplicationController < ActionController::Base end if current_user && current_user.id - Java::OrgSonarServerUser::RubyUserSession.setSession(current_user.id.to_i, current_user.login, I18n.locale.to_s) + Java::OrgSonarServerUser::RubyUserSession.setSession(current_user.id.to_i, current_user.login, current_user.name, I18n.locale.to_s) else - Java::OrgSonarServerUser::RubyUserSession.setSession(nil, nil, I18n.locale.to_s) + Java::OrgSonarServerUser::RubyUserSession.setSession(nil, nil, nil, I18n.locale.to_s) end end diff --git a/sonar-server/src/main/webapp/WEB-INF/db/migrate/483_add_rules_parameter_key_to_active_rule_parameters.rb b/sonar-server/src/main/webapp/WEB-INF/db/migrate/483_add_rules_parameter_key_to_active_rule_parameters.rb new file mode 100644 index 00000000000..4a2468aca65 --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/db/migrate/483_add_rules_parameter_key_to_active_rule_parameters.rb @@ -0,0 +1,30 @@ +# +# SonarQube, open source software quality management tool. +# Copyright (C) 2008-2013 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 4.2 +# SONAR-4950 +# +class AddRulesParameterKeyToActiveRuleParameters < ActiveRecord::Migration + + def self.up + add_column 'active_rule_parameters', :rules_parameter_key, :string, :null => true, :limit => 128 + end +end diff --git a/sonar-server/src/main/webapp/WEB-INF/db/migrate/484_copy_rule_parameters_name_to_active_rule_parameters.rb b/sonar-server/src/main/webapp/WEB-INF/db/migrate/484_copy_rule_parameters_name_to_active_rule_parameters.rb new file mode 100644 index 00000000000..e9abbcb28df --- /dev/null +++ b/sonar-server/src/main/webapp/WEB-INF/db/migrate/484_copy_rule_parameters_name_to_active_rule_parameters.rb @@ -0,0 +1,47 @@ +# +# SonarQube, open source software quality management tool. +# Copyright (C) 2008-2013 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 4.2 +# SONAR-4950 +# +class CopyRuleParametersNameToActiveRuleParameters < ActiveRecord::Migration + + class ActiveRuleParameter < ActiveRecord::Base + end + + class RulesParameter < ActiveRecord::Base + end + + def self.up + ActiveRuleParameter.reset_column_information + + rule_params_by_id = {} + RulesParameter.all.each do |rule_param| + rule_params_by_id[rule_param.id] = rule_param + end + + ActiveRuleParameter.all.each do |active_rule_parameter| + rule_param = rule_params_by_id[active_rule_parameter.rules_parameter_id] + active_rule_parameter.rules_parameter_key = rule_param.name if rule_param + active_rule_parameter.save + end + end +end diff --git a/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileOperationsTest.java b/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileOperationsTest.java index 635d5e4d3b6..68cd7b8b581 100644 --- a/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileOperationsTest.java +++ b/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileOperationsTest.java @@ -34,6 +34,7 @@ import org.mockito.stubbing.Answer; import org.sonar.api.profiles.ProfileExporter; import org.sonar.api.profiles.ProfileImporter; import org.sonar.api.profiles.RulesProfile; +import org.sonar.api.rule.Severity; import org.sonar.api.rules.ActiveRule; import org.sonar.api.rules.Rule; import org.sonar.api.rules.RulePriority; @@ -44,6 +45,9 @@ import org.sonar.core.preview.PreviewCache; import org.sonar.core.properties.PropertiesDao; import org.sonar.core.properties.PropertyDto; import org.sonar.core.qualityprofile.db.*; +import org.sonar.core.rule.RuleDao; +import org.sonar.core.rule.RuleParamDto; +import org.sonar.server.configuration.ProfilesManager; import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.rule.RuleRegistry; @@ -74,6 +78,9 @@ public class QProfileOperationsTest { @Mock ActiveRuleDao activeRuleDao; + @Mock + RuleDao ruleDao; + @Mock PropertiesDao propertiesDao; @@ -83,16 +90,32 @@ public class QProfileOperationsTest { @Mock RuleRegistry ruleRegistry; + @Mock + ProfilesManager profilesManager; + List exporters = newArrayList(); List importers = newArrayList(); QProfileOperations operations; + Integer currentId = 1; + @Before public void setUp() throws Exception { when(myBatis.openSession()).thenReturn(session); - operations = new QProfileOperations(myBatis, qualityProfileDao, activeRuleDao, propertiesDao, exporters, importers, dryRunCache, ruleRegistry); + + // Associate an id when inserting an object to simulate the db id generator + doAnswer(new Answer() { + public Object answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + ActiveRuleDto dto = (ActiveRuleDto) args[0]; + dto.setId(currentId++); + return null; + } + }).when(activeRuleDao).insert(any(ActiveRuleDto.class), any(SqlSession.class)); + + operations = new QProfileOperations(myBatis, qualityProfileDao, activeRuleDao, ruleDao, propertiesDao, exporters, importers, dryRunCache, ruleRegistry, profilesManager); } @Test @@ -127,10 +150,10 @@ public class QProfileOperationsTest { public void create_profile_from_xml_plugin() throws Exception { RulesProfile profile = RulesProfile.create("Default", "java"); Rule rule = Rule.create("pmd", "rule1"); - rule.createParameter("paramKey"); + rule.createParameter("max"); rule.setId(10); ActiveRule activeRule = profile.activateRule(rule, RulePriority.BLOCKER); - activeRule.setParameter("paramKey", "paramValue"); + activeRule.setParameter("max", "10"); Map xmlProfilesByPlugin = newHashMap(); xmlProfilesByPlugin.put("pmd", ""); @@ -154,8 +177,8 @@ public class QProfileOperationsTest { ArgumentCaptor activeRuleParamArgument = ArgumentCaptor.forClass(ActiveRuleParamDto.class); verify(activeRuleDao).insert(activeRuleParamArgument.capture(), eq(session)); - assertThat(activeRuleParamArgument.getValue().getKey()).isEqualTo("paramKey"); - assertThat(activeRuleParamArgument.getValue().getValue()).isEqualTo("paramValue"); + assertThat(activeRuleParamArgument.getValue().getKey()).isEqualTo("max"); + assertThat(activeRuleParamArgument.getValue().getValue()).isEqualTo("10"); verify(ruleRegistry).bulkIndexActiveRules(anyListOf(ActiveRuleDto.class), any(Multimap.class)); } @@ -210,4 +233,77 @@ public class QProfileOperationsTest { assertThat(argumentCaptor.getValue().getValue()).isEqualTo("My profile"); } + @Test + public void activate_rule() throws Exception { + QualityProfileDto qualityProfile = new QualityProfileDto().setId(1).setName("My profile").setLanguage("java"); + Rule rule = Rule.create().setRepositoryKey("squid").setKey("AvoidCycle"); + rule.setId(10); + when(ruleDao.selectParameters(eq(10L), eq(session))).thenReturn(newArrayList(new RuleParamDto().setId(20).setName("max").setDefaultValue("10"))); + + operations.activateRule(qualityProfile, rule, Severity.CRITICAL, MockUserSession.create().setName("nicolas").setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN)); + + ArgumentCaptor activeRuleArgument = ArgumentCaptor.forClass(ActiveRuleDto.class); + verify(activeRuleDao).insert(activeRuleArgument.capture(), eq(session)); + assertThat(activeRuleArgument.getValue().getRulId()).isEqualTo(10); + assertThat(activeRuleArgument.getValue().getSeverity()).isEqualTo(3); + + ArgumentCaptor activeRuleParamArgument = ArgumentCaptor.forClass(ActiveRuleParamDto.class); + verify(activeRuleDao).insert(activeRuleParamArgument.capture(), eq(session)); + assertThat(activeRuleParamArgument.getValue().getKey()).isEqualTo("max"); + assertThat(activeRuleParamArgument.getValue().getValue()).isEqualTo("10"); + + verify(session).commit(); + verify(profilesManager).activated(eq(1), anyInt(), eq("nicolas")); + } + + @Test + public void update_severity() throws Exception { + QualityProfileDto qualityProfile = new QualityProfileDto().setId(1).setName("My profile").setLanguage("java"); + Rule rule = Rule.create().setRepositoryKey("squid").setKey("AvoidCycle"); + rule.setId(10); + ActiveRuleDto activeRule = new ActiveRuleDto().setId(5).setProfileId(1).setRuleId(10).setSeverity(1); + when(activeRuleDao.selectByProfileAndRule(1, 10)).thenReturn(activeRule); + + operations.activateRule(qualityProfile, rule, Severity.MAJOR, MockUserSession.create().setName("nicolas").setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN)); + + verify(activeRuleDao).update(eq(activeRule), eq(session)); + + verify(session).commit(); + verify(profilesManager).ruleSeverityChanged(eq(1), eq(5), eq(RulePriority.MINOR), eq(RulePriority.MAJOR), eq("nicolas")); + } + + @Test + public void deactivate_rule() throws Exception { + QualityProfileDto qualityProfile = new QualityProfileDto().setId(1).setName("My profile").setLanguage("java"); + Rule rule = Rule.create().setRepositoryKey("squid").setKey("AvoidCycle"); + rule.setId(10); + ActiveRuleDto activeRule = new ActiveRuleDto().setId(5).setProfileId(1).setRuleId(10).setSeverity(1); + when(activeRuleDao.selectByProfileAndRule(1, 10)).thenReturn(activeRule); + + operations.deactivateRule(qualityProfile, rule, MockUserSession.create().setName("nicolas").setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN)); + + verify(activeRuleDao).delete(eq(5), eq(session)); + verify(activeRuleDao).deleteParameters(eq(5), eq(session)); + verify(session).commit(); + verify(profilesManager).deactivated(eq(1), anyInt(), eq("nicolas")); + } + + @Test + public void fail_to_deactivate_rule_if_no_active_rule_on_profile() throws Exception { + QualityProfileDto qualityProfile = new QualityProfileDto().setId(1).setName("My profile").setLanguage("java"); + Rule rule = Rule.create().setRepositoryKey("squid").setKey("AvoidCycle"); + rule.setId(10); + when(activeRuleDao.selectByProfileAndRule(1, 10)).thenReturn(null); + + try { + operations.deactivateRule(qualityProfile, rule, MockUserSession.create().setName("nicolas").setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN)); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(BadRequestException.class); + } + verify(activeRuleDao, never()).update(any(ActiveRuleDto.class), eq(session)); + verify(session, never()).commit(); + verifyZeroInteractions(profilesManager); + } + } diff --git a/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfilesTest.java b/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfilesTest.java index 7d3852b4ced..dcd7695bbe8 100644 --- a/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfilesTest.java +++ b/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfilesTest.java @@ -26,6 +26,9 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; +import org.sonar.api.rule.Severity; +import org.sonar.api.rules.Rule; +import org.sonar.api.rules.RuleFinder; import org.sonar.core.component.ComponentDto; import org.sonar.core.qualityprofile.db.QualityProfileDao; import org.sonar.core.qualityprofile.db.QualityProfileDto; @@ -55,6 +58,9 @@ public class QProfilesTest { @Mock ResourceDao resourceDao; + @Mock + RuleFinder ruleFinder; + @Mock QProfileProjectService projectService; @@ -71,7 +77,7 @@ public class QProfilesTest { @Before public void setUp() throws Exception { - qProfiles = new QProfiles(qualityProfileDao, resourceDao, projectService, search, service, rules); + qProfiles = new QProfiles(qualityProfileDao, resourceDao, ruleFinder, projectService, search, service, rules); } @Test @@ -273,4 +279,47 @@ public class QProfilesTest { assertThat(qProfiles.searchInactiveRules(query, paging)).isEqualTo(result); } + @Test + public void activate_rule() throws Exception { + QualityProfileDto qualityProfile = new QualityProfileDto().setId(1).setName("My profile").setLanguage("java"); + when(qualityProfileDao.selectById(1)).thenReturn(qualityProfile); + Rule rule = Rule.create().setRepositoryKey("squid").setKey("AvoidCycle"); + rule.setId(10); + when(ruleFinder.findById(10)).thenReturn(rule); + + qProfiles.activateRule(1, 10, Severity.BLOCKER); + + verify(service).activateRule(eq(qualityProfile), eq(rule), eq(Severity.BLOCKER), any(UserSession.class)); + } + + @Test + public void fail_to_activate_rule_if_rule_not_found() throws Exception { + QualityProfileDto qualityProfile = new QualityProfileDto().setId(1).setName("My profile").setLanguage("java"); + when(qualityProfileDao.selectById(1)).thenReturn(qualityProfile); + Rule rule = Rule.create().setRepositoryKey("squid").setKey("AvoidCycle"); + rule.setId(10); + when(ruleFinder.findById(10)).thenReturn(null); + + try { + qProfiles.activateRule(1, 10, Severity.BLOCKER); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(NotFoundException.class); + } + verifyZeroInteractions(service); + } + + @Test + public void deactivate_rule() throws Exception { + QualityProfileDto qualityProfile = new QualityProfileDto().setId(1).setName("My profile").setLanguage("java"); + when(qualityProfileDao.selectById(1)).thenReturn(qualityProfile); + Rule rule = Rule.create().setRepositoryKey("squid").setKey("AvoidCycle"); + rule.setId(10); + when(ruleFinder.findById(10)).thenReturn(rule); + + qProfiles.deactivateRule(1, 10); + + verify(service).deactivateRule(eq(qualityProfile), eq(rule), any(UserSession.class)); + } + } diff --git a/sonar-server/src/test/java/org/sonar/server/user/MockUserSession.java b/sonar-server/src/test/java/org/sonar/server/user/MockUserSession.java index d5770638049..c7f76929102 100644 --- a/sonar-server/src/test/java/org/sonar/server/user/MockUserSession.java +++ b/sonar-server/src/test/java/org/sonar/server/user/MockUserSession.java @@ -56,6 +56,11 @@ public class MockUserSession extends UserSession { return this; } + public MockUserSession setName(@Nullable String name) { + super.setName(name); + return this; + } + public MockUserSession setUserId(@Nullable Integer userId) { super.setUserId(userId); return this; diff --git a/sonar-server/src/test/java/org/sonar/server/user/RubyUserSessionTest.java b/sonar-server/src/test/java/org/sonar/server/user/RubyUserSessionTest.java index afe3d1265a5..c816f38ac9e 100644 --- a/sonar-server/src/test/java/org/sonar/server/user/RubyUserSessionTest.java +++ b/sonar-server/src/test/java/org/sonar/server/user/RubyUserSessionTest.java @@ -28,12 +28,13 @@ import static org.fest.assertions.Assertions.assertThat; public class RubyUserSessionTest { @Test public void should_set_session() throws Exception { - RubyUserSession.setSession(123, "karadoc", "fr"); + RubyUserSession.setSession(123, "karadoc", "Karadoc", "fr"); UserSession session = UserSession.get(); assertThat(session).isNotNull(); assertThat(session.login()).isEqualTo("karadoc"); + assertThat(session.name()).isEqualTo("Karadoc"); assertThat(session.userId()).isEqualTo(123); assertThat(session.isLoggedIn()).isTrue(); assertThat(session.locale()).isEqualTo(Locale.FRENCH); @@ -41,12 +42,13 @@ public class RubyUserSessionTest { @Test public void should_set_anonymous_session() throws Exception { - RubyUserSession.setSession(null, null, "fr"); + RubyUserSession.setSession(null, null, null, "fr"); UserSession session = UserSession.get(); assertThat(session).isNotNull(); assertThat(session.login()).isNull(); + assertThat(session.name()).isNull(); assertThat(session.userId()).isNull(); assertThat(session.isLoggedIn()).isFalse(); assertThat(session.locale()).isEqualTo(Locale.FRENCH); diff --git a/sonar-server/src/test/resources/org/sonar/server/configuration/InheritedProfilesTest/shouldActivateInChildren-result.xml b/sonar-server/src/test/resources/org/sonar/server/configuration/InheritedProfilesTest/shouldActivateInChildren-result.xml index edfeb956ecf..6990ee887cf 100644 --- a/sonar-server/src/test/resources/org/sonar/server/configuration/InheritedProfilesTest/shouldActivateInChildren-result.xml +++ b/sonar-server/src/test/resources/org/sonar/server/configuration/InheritedProfilesTest/shouldActivateInChildren-result.xml @@ -11,10 +11,10 @@ - + - + diff --git a/sonar-server/src/test/resources/org/sonar/server/configuration/InheritedProfilesTest/shouldActivateInChildren.xml b/sonar-server/src/test/resources/org/sonar/server/configuration/InheritedProfilesTest/shouldActivateInChildren.xml index 67bea788819..cf8d9f25a59 100644 --- a/sonar-server/src/test/resources/org/sonar/server/configuration/InheritedProfilesTest/shouldActivateInChildren.xml +++ b/sonar-server/src/test/resources/org/sonar/server/configuration/InheritedProfilesTest/shouldActivateInChildren.xml @@ -10,6 +10,6 @@ - + diff --git a/sonar-server/src/test/resources/org/sonar/server/configuration/RuleChangeTest/ruleReverted.xml b/sonar-server/src/test/resources/org/sonar/server/configuration/RuleChangeTest/ruleReverted.xml index d6e7fc81799..5ef9bf1a269 100644 --- a/sonar-server/src/test/resources/org/sonar/server/configuration/RuleChangeTest/ruleReverted.xml +++ b/sonar-server/src/test/resources/org/sonar/server/configuration/RuleChangeTest/ruleReverted.xml @@ -10,10 +10,10 @@ - + - - + + -- 2.39.5