diff options
author | Evgeny Mandrikov <mandrikov@gmail.com> | 2011-05-27 02:07:33 +0400 |
---|---|---|
committer | Evgeny Mandrikov <mandrikov@gmail.com> | 2011-05-27 02:54:10 +0400 |
commit | 2dbed652688d87b303f7821ea619ed36ba654a19 (patch) | |
tree | cbf627ca9957741a70eb4dc55ca6210974986b32 /sonar-plugin-api | |
parent | 880d215a9fbafec25f7d6172b22e8dd0915a0c9a (diff) | |
download | sonarqube-2dbed652688d87b303f7821ea619ed36ba654a19.tar.gz sonarqube-2dbed652688d87b303f7821ea619ed36ba654a19.zip |
SONAR-1922 Add a kind of version control for quality profiles
Apply patch, which was contributed by Julien Henry:
* Following algorithm was implemented: Every profile starts with
version=1 and used=false. As soon as there is an analysis of a
project, the involved profile is set to used=true. Every modification
to a quality profile (activation, deactivation or modification of
rule) is logged in DB in dedicated tables. When a modification is done
on a profile that is used=true, then version number is increased and
profile is set to used=false.
* Introduced new metric to store profile version, which was used during
analysis.
* If profile for project is different than the one used during previous
analysis, then event would be created.
* Introduced new tab 'changelog' for profiles.
Following fixes were applied on original patch:
* Index name limited to 30 characters in Oracle DB, so names were
reduced.
* Field ActiveRuleChange.profileVersion never read locally, because
ruby read it directly from DB, so getter added.
* Direction doesn't make sense for 'profile_version' metric, so was
removed.
* Fixed ProfileEventsSensor: it seems that TimeMachine not guarantee
that the order of measures would be the same as in query, so we should
perform two sequential queries.
* Fixed handling of null values during migration.
Diffstat (limited to 'sonar-plugin-api')
7 files changed, 359 insertions, 0 deletions
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/Event.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/Event.java index b1a7f1e19b9..fa2263548d6 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/Event.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/Event.java @@ -35,6 +35,7 @@ import javax.persistence.*; public class Event extends BaseIdentifiable { public static final String CATEGORY_VERSION = "Version"; public static final String CATEGORY_ALERT = "Alert"; + public static final String CATEGORY_PROFILE = "Profile"; @Column(name = "name", updatable = true, nullable = true, length = 50) private String name; @@ -121,6 +122,10 @@ public class Event extends BaseIdentifiable { return CATEGORY_VERSION.equalsIgnoreCase(category); } + public boolean isProfileCategory() { + return CATEGORY_PROFILE.equalsIgnoreCase(category); + } + public Date getDate() { return date; } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/TimeMachineQuery.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/TimeMachineQuery.java index 13f8818d0bc..20b0ee861c0 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/TimeMachineQuery.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/TimeMachineQuery.java @@ -19,6 +19,8 @@ */ package org.sonar.api.batch; +import org.apache.commons.lang.builder.EqualsBuilder; + import com.google.common.collect.Lists; import org.apache.commons.lang.builder.ToStringBuilder; import org.sonar.api.measures.Metric; @@ -231,4 +233,10 @@ public class TimeMachineQuery { .append("to", to) .toString(); } + + @Override + public boolean equals(Object obj) { + return EqualsBuilder.reflectionEquals(this, obj); + } + } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java index 4e265bac406..2cf3d598b40 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java @@ -1141,6 +1141,12 @@ public final class CoreMetrics { .setDomain(DOMAIN_GENERAL) .create(); + public static final String PROFILE_VERSION_KEY = "profile_version"; + public static final Metric PROFILE_VERSION = new Metric.Builder(PROFILE_VERSION_KEY, "Profile version", Metric.ValueType.INT) + .setDescription("Selected quality profile version") + .setQualitative(false) + .setDomain(DOMAIN_GENERAL) + .create(); public static List<Metric> metrics = Lists.newLinkedList(); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/profiles/RulesProfile.java b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/RulesProfile.java index 5b34326daf6..e7f61d92df6 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/profiles/RulesProfile.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/RulesProfile.java @@ -64,6 +64,9 @@ public class RulesProfile implements Cloneable { @Column(name = "name", updatable = true, nullable = false) private String name; + @Column(name = "version", updatable = true, nullable = false) + private int version = 1; + @Column(name = "default_profile", updatable = true, nullable = false) private Boolean defaultProfile = Boolean.FALSE; @@ -73,6 +76,9 @@ public class RulesProfile implements Cloneable { @Column(name = "enabled", updatable = true, nullable = false) private Boolean enabled = Boolean.TRUE; + @Column(name = "used_profile", updatable = true, nullable = false) + private Boolean used = Boolean.FALSE; + @Column(name = "language", updatable = true, nullable = false) private String language; @@ -135,6 +141,24 @@ public class RulesProfile implements Cloneable { this.name = s; return this; } + + public int getVersion() { + return version; + } + + public RulesProfile setVersion(int version) { + this.version = version; + return this; + } + + public Boolean getUsed() { + return used; + } + + public RulesProfile setUsed(Boolean used) { + this.used = used; + return this; + } /** * @return the list of active rules diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/ActiveRuleChange.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/ActiveRuleChange.java new file mode 100644 index 00000000000..a3b95213220 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/ActiveRuleChange.java @@ -0,0 +1,213 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.api.rules; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.FetchType; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; +import javax.persistence.Table; +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; +import org.sonar.api.database.BaseIdentifiable; +import org.sonar.api.profiles.RulesProfile; + +/** + * A class to map a RuleChange to the hibernate model + * + * @since 2.9 + */ +@Entity +@Table(name = "active_rule_changes") +public class ActiveRuleChange extends BaseIdentifiable { + + @Column(name = "user_login", updatable = false, nullable = false) + private String modifierLogin; + + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "profile_id", updatable = false, nullable = false) + private RulesProfile rulesProfile; + + @Column(name = "profile_version", updatable = false, nullable = false) + private int profileVersion; + + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "rule_id", updatable = false, nullable = false) + private Rule rule; + + @Column(name = "change_date", updatable = false, nullable = false) + private Date date; + + /** + * true means rule was enabled + * false means rule was disabled + * null means rule stay enabled (another param was changed) + */ + @Column(name = "enabled") + private Boolean enabled; + + @Column(name = "old_severity", updatable = false, nullable = true) + @Enumerated(EnumType.ORDINAL) + private RulePriority oldSeverity; + + @Column(name = "new_severity", updatable = false, nullable = true) + @Enumerated(EnumType.ORDINAL) + private RulePriority newSeverity; + + @OneToMany(mappedBy = "activeRuleChange", fetch = FetchType.LAZY, cascade = { CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REMOVE }) + private List<ActiveRuleParamChange> activeRuleParamChanges = new ArrayList<ActiveRuleParamChange>(); + + public ActiveRuleChange(String modifierLogin, RulesProfile profile, Rule rule) { + this.modifierLogin = modifierLogin; + this.rulesProfile = profile; + this.profileVersion = profile.getVersion(); + this.rule = rule; + this.date = Calendar.getInstance().getTime(); + } + + public Rule getRule() { + return rule; + } + + public RulePriority getOldSeverity() { + return oldSeverity; + } + + public void setOldSeverity(RulePriority oldSeverity) { + this.oldSeverity = oldSeverity; + } + + public RulePriority getNewSeverity() { + return newSeverity; + } + + public void setNewSeverity(RulePriority newSeverity) { + this.newSeverity = newSeverity; + } + + public RulesProfile getRulesProfile() { + return rulesProfile; + } + + public int getProfileVersion() { + return profileVersion; + } + + public String getRepositoryKey() { + return rule.getRepositoryKey(); + } + + /** + * @return the config key the changed rule belongs to + */ + public String getConfigKey() { + return rule.getConfigKey(); + } + + /** + * @return the key of the changed rule + */ + public String getRuleKey() { + return rule.getKey(); + } + + public Boolean isEnabled() { + return enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + public List<ActiveRuleParamChange> getActiveRuleParamChanges() { + return activeRuleParamChanges; + } + + public String getModifierLogin() { + return modifierLogin; + } + + public ActiveRuleChange setParameterChange(String key, String oldValue, String newValue) { + RuleParam ruleParameter = rule.getParam(key); + if (ruleParameter != null) { + activeRuleParamChanges.add(new ActiveRuleParamChange(this, ruleParameter, oldValue, newValue)); + } + return this; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (obj == this) { + return true; + } + if (obj.getClass() != getClass()) { + return false; + } + ActiveRuleChange rhs = (ActiveRuleChange) obj; + return new EqualsBuilder() + .appendSuper(super.equals(obj)) + .append(modifierLogin, rhs.modifierLogin) + .append(rulesProfile, rhs.rulesProfile) + .append(rule, rhs.rule) + .append(date, rhs.date) + .append(enabled, rhs.enabled) + .append(newSeverity, rhs.newSeverity) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(41, 33) + .append(modifierLogin) + .append(rulesProfile) + .append(rule) + .append(date) + .append(enabled) + .append(newSeverity) + .toHashCode(); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("id", getId()) + .append("profile", rulesProfile) + .append("rule", rule) + .append("modifier", modifierLogin) + .append("changed at", date) + .append("enabled", enabled) + .append("new severity", newSeverity) + .toString(); + } + +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/ActiveRuleParamChange.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/ActiveRuleParamChange.java new file mode 100644 index 00000000000..be7d2cef003 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/ActiveRuleParamChange.java @@ -0,0 +1,97 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2011 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.api.rules; + +import org.sonar.api.database.BaseIdentifiable; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; + +import javax.persistence.*; + +/** + * @since 2.9 + */ +@Entity +@Table(name = "active_rule_param_changes") +public class ActiveRuleParamChange extends BaseIdentifiable { + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "active_rule_change_id") + private ActiveRuleChange activeRuleChange; + + @ManyToOne(fetch = FetchType.LAZY, optional = true) + @JoinColumn(name = "rules_parameter_id") + private RuleParam ruleParam; + + @Column(name = "old_value", updatable = false, nullable = true, length = 4000) + private String oldValue; + + @Column(name = "new_value", updatable = false, nullable = true, length = 4000) + private String newValue; + + ActiveRuleParamChange(ActiveRuleChange activeRuleChange, RuleParam ruleParam, String oldValue, String newValue) { + this.activeRuleChange = activeRuleChange; + this.ruleParam = ruleParam; + this.oldValue = oldValue; + this.newValue = newValue; + } + + public ActiveRuleChange getActiveRuleChange() { + return activeRuleChange; + } + + public RuleParam getRuleParam() { + return ruleParam; + } + + public String getOldValue() { + return oldValue; + } + + public String getNewValue() { + return newValue; + } + + public String getKey() { + return ruleParam.getKey(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ActiveRuleParamChange)) { + return false; + } + if (this == obj) { + return true; + } + ActiveRuleParamChange other = (ActiveRuleParamChange) obj; + return new EqualsBuilder() + .append(getId(), other.getId()).isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 57) + .append(getId()) + .toHashCode(); + } + +} diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/profiles/RulesProfileTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/profiles/RulesProfileTest.java index 944805f866c..dd364486646 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/profiles/RulesProfileTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/profiles/RulesProfileTest.java @@ -54,4 +54,10 @@ public class RulesProfileTest { profile.activateRule(rule, RulePriority.MINOR); assertThat(profile.getActiveRule("repo", "key1").getSeverity(), is(RulePriority.MINOR)); } + + @Test + public void defaultVersionIs1() { + RulesProfile profile = RulesProfile.create(); + assertThat(profile.getVersion(), is(1)); + } } |