From 7660b174b186527b5df5354295886c42dec2109d Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Wed, 17 Apr 2013 18:29:35 +0200 Subject: SONAR-3755 support the parameter "rules" in /api/issues/search --- .../main/java/org/sonar/api/issue/IssueChange.java | 146 +++++++++++++++++++++ .../main/java/org/sonar/api/issue/IssueQuery.java | 93 ++++++------- .../main/java/org/sonar/api/issue/JRubyIssues.java | 2 +- .../src/main/java/org/sonar/api/rule/RuleKey.java | 22 +++- .../java/org/sonar/api/issue/IssueChangeTest.java | 41 ++++++ .../java/org/sonar/api/issue/IssueQueryTest.java | 29 ++-- .../test/java/org/sonar/api/rule/RuleKeyTest.java | 40 ++++++ 7 files changed, 304 insertions(+), 69 deletions(-) create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueChange.java create mode 100644 sonar-plugin-api/src/test/java/org/sonar/api/issue/IssueChangeTest.java (limited to 'sonar-plugin-api/src') diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueChange.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueChange.java new file mode 100644 index 00000000000..1587ca25453 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueChange.java @@ -0,0 +1,146 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2012 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.issue; + +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @since 3.6 + */ +public class IssueChange { + private String severity = null; + private String comment = null; + private Boolean manualSeverity = null; + private String message = null; + private boolean lineChanged = false; + private Integer line = null; + private boolean costChanged = false; + private Double cost = null; + private String resolution = null; + private boolean assigneeLoginChanged = false; + private String assigneeLogin = null; + private Map attributes = null; + + private IssueChange() { + } + + public static IssueChange create() { + return new IssueChange(); + } + + public boolean hasChanges() { + return severity != null || comment != null || manualSeverity != null || message != null || + lineChanged || costChanged || resolution != null || assigneeLoginChanged || attributes != null; + } + + public IssueChange setSeverity(String severity) { + this.severity = severity; + return this; + } + + public IssueChange setComment(String comment) { + this.comment = comment; + return this; + } + + public IssueChange setManualSeverity(boolean b) { + this.manualSeverity = b; + return this; + } + + public IssueChange setMessage(String message) { + this.message = message; + return this; + } + + public IssueChange setLine(@Nullable Integer line) { + this.lineChanged = true; + this.line = line; + return this; + } + + public IssueChange setCost(@Nullable Double cost) { + this.costChanged = true; + this.cost = cost; + return this; + } + + public IssueChange setResolution(String resolution) { + this.resolution = resolution; + return this; + } + + public IssueChange setAssigneeLogin(@Nullable String assigneeLogin) { + this.assigneeLoginChanged = true; + this.assigneeLogin = assigneeLogin; + return this; + } + + public IssueChange setAttribute(String key, @Nullable String value) { + if (attributes == null && value != null) { + attributes = new LinkedHashMap(); + } + if (value != null) { + attributes.put(key, value); + } else if (attributes != null) { + attributes.remove(key); + } + return this; + } + + public String severity() { + return severity; + } + + public String comment() { + return comment; + } + + public Boolean manualSeverity() { + return manualSeverity; + } + + public String message() { + return message; + } + + public Integer line() { + return line; + } + + public Double cost() { + return cost; + } + + public String resolution() { + return resolution; + } + + public String assigneeLogin() { + return assigneeLogin; + } + + public Map attributes() { + return attributes == null ? Collections.emptyMap() : new LinkedHashMap(attributes); + } +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQuery.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQuery.java index ab6ec98a9cd..9ab187dab22 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQuery.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQuery.java @@ -22,9 +22,10 @@ package org.sonar.api.issue; import com.google.common.base.Preconditions; import org.apache.commons.lang.builder.ReflectionToStringBuilder; +import org.sonar.api.rule.RuleKey; import java.util.Date; -import java.util.List; +import java.util.Collection; /** * TODO add javadoc @@ -33,16 +34,15 @@ import java.util.List; */ public class IssueQuery { - private final List keys; - private final List severities; - private final List statuses; - private final List resolutions; - private final List components; - private final List componentRoots; - private final String ruleRepository; - private final String rule; - private final List userLogins; - private final List assigneeLogins; + private final Collection keys; + private final Collection severities; + private final Collection statuses; + private final Collection resolutions; + private final Collection components; + private final Collection componentRoots; + private final Collection rules; + private final Collection userLogins; + private final Collection assigneeLogins; private final Date createdAfter; private final Date createdBefore; private final int limit, offset; @@ -54,8 +54,7 @@ public class IssueQuery { this.resolutions = builder.resolutions; this.components = builder.components; this.componentRoots = builder.componentRoots; - this.ruleRepository = builder.ruleRepository; - this.rule = builder.rule; + this.rules = builder.rules; this.userLogins = builder.userLogins; this.assigneeLogins = builder.assigneeLogins; this.createdAfter = builder.createdAfter; @@ -64,43 +63,39 @@ public class IssueQuery { this.offset = builder.offset; } - public List keys() { + public Collection keys() { return keys; } - public List severities() { + public Collection severities() { return severities; } - public List statuses() { + public Collection statuses() { return statuses; } - public List resolutions() { + public Collection resolutions() { return resolutions; } - public List components() { + public Collection components() { return components; } - public List componentRoots() { + public Collection componentRoots() { return componentRoots; } - public String ruleRepository() { - return ruleRepository; + public Collection rules() { + return rules; } - public String rule() { - return rule; - } - - public List userLogins() { + public Collection userLogins() { return userLogins; } - public List assigneeLogins() { + public Collection assigneeLogins() { return assigneeLogins; } @@ -138,16 +133,15 @@ public class IssueQuery { private static final int MAX_LIMIT = 5000; private static final int DEFAULT_OFFSET = 0; - private List keys; - private List severities; - private List statuses; - private List resolutions; - private List components; - private List componentRoots; - private String ruleRepository; - private String rule; - private List userLogins; - private List assigneeLogins; + private Collection keys; + private Collection severities; + private Collection statuses; + private Collection resolutions; + private Collection components; + private Collection componentRoots; + private Collection rules; + private Collection userLogins; + private Collection assigneeLogins; private Date createdAfter; private Date createdBefore; private int limit = DEFAULT_LIMIT; @@ -156,52 +150,47 @@ public class IssueQuery { private Builder() { } - public Builder keys(List l) { + public Builder keys(Collection l) { this.keys = l; return this; } - public Builder severities(List l) { + public Builder severities(Collection l) { this.severities = l; return this; } - public Builder statuses(List l) { + public Builder statuses(Collection l) { this.statuses = l; return this; } - public Builder resolutions(List l) { + public Builder resolutions(Collection l) { this.resolutions = l; return this; } - public Builder components(List l) { + public Builder components(Collection l) { this.components = l; return this; } - public Builder componentRoots(List l) { + public Builder componentRoots(Collection l) { this.componentRoots = l; return this; } - public Builder ruleRepository(String ruleRepository) { - this.ruleRepository = ruleRepository; - return this; - } - - public Builder rule(String rule) { - this.rule = rule; + public Builder rules(Collection rules) { + this.rules = rules; return this; } - public Builder userLogins(List l) { + public Builder userLogins(Collection l) { this.userLogins = l; return this; } - public Builder assigneeLogins(List l) { + public Builder assigneeLogins(Collection l) { this.assigneeLogins = l; return this; } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/JRubyIssues.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/JRubyIssues.java index af3a0a0bffc..6ec537a8399 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/JRubyIssues.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/JRubyIssues.java @@ -24,7 +24,7 @@ import org.sonar.api.ServerComponent; import java.util.Map; /** - * Facade for JRuby on Rails extensions. + * Facade for JRuby on Rails extensions to request issues. *

* Reference from Ruby code : Api.issues *

diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleKey.java b/sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleKey.java index 6227d717646..a286b0f5750 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleKey.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleKey.java @@ -20,9 +20,14 @@ package org.sonar.api.rule; import com.google.common.base.Preconditions; +import com.google.common.base.Strings; import java.io.Serializable; +/** + * Key of a rule. Unique among all the rule repositories. + * @since 3.6 + */ public class RuleKey implements Serializable { private final String repository, rule; @@ -31,20 +36,35 @@ public class RuleKey implements Serializable { this.rule = rule; } + /** + * Create a key. Parameters are NOT null. + */ public static RuleKey of(String repository, String rule) { + Preconditions.checkArgument(!Strings.isNullOrEmpty(repository), "Repository must be set"); + Preconditions.checkArgument(!Strings.isNullOrEmpty(rule), "Rule must be set"); return new RuleKey(repository, rule); } + /** + * Create a key from a string representation (see {@link #toString()}. An {@link IllegalArgumentException} is raised + * if the format is not valid. + */ public static RuleKey parse(String s) { String[] split = s.split(":"); Preconditions.checkArgument(split.length == 2, "Bad format of rule key: " + s); return RuleKey.of(split[0], split[1]); } + /** + * Never null + */ public String repository() { return repository; } + /** + * Never null + */ public String rule() { return rule; } @@ -75,7 +95,7 @@ public class RuleKey implements Serializable { } /** - * Do not change this format because it's used by customers of the API (rails, ...) + * Format is "repository:rule", for example "squid:AvoidCycle" */ @Override public String toString() { diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/issue/IssueChangeTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/issue/IssueChangeTest.java new file mode 100644 index 00000000000..bd51b76127a --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/issue/IssueChangeTest.java @@ -0,0 +1,41 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2012 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.issue; + +import org.junit.Test; + +import static org.fest.assertions.Assertions.assertThat; + +public class IssueChangeTest { + @Test + public void should_not_have_changes_by_default() throws Exception { + IssueChange change = IssueChange.create(); + assertThat(change.hasChanges()).isFalse(); + assertThat(change.severity()).isNull(); + assertThat(change.cost()).isNull(); + assertThat(change.assigneeLogin()).isNull(); + assertThat(change.line()).isNull(); + assertThat(change.comment()).isNull(); + assertThat(change.message()).isNull(); + assertThat(change.resolution()).isNull(); + assertThat(change.manualSeverity()).isNull(); + assertThat(change.attributes()).isEmpty(); + } +} diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/issue/IssueQueryTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/issue/IssueQueryTest.java index 5b3c4491ee4..324d2d78c3c 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/issue/IssueQueryTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/issue/IssueQueryTest.java @@ -21,6 +21,7 @@ package org.sonar.api.issue; import com.google.common.collect.Lists; import org.junit.Test; +import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.Severity; import java.util.Date; @@ -36,27 +37,25 @@ public class IssueQueryTest { .severities(Lists.newArrayList(Severity.BLOCKER)) .statuses(Lists.newArrayList(Issue.STATUS_RESOLVED)) .resolutions(Lists.newArrayList(Issue.RESOLUTION_FALSE_POSITIVE)) - .components(Lists.newArrayList("components")) - .componentRoots(Lists.newArrayList("componentRoots")) - .ruleRepository("ruleRepository") - .rule("rule") - .userLogins(Lists.newArrayList("user")) + .components(Lists.newArrayList("org/struts/Action.java")) + .componentRoots(Lists.newArrayList("org.struts:core")) + .rules(Lists.newArrayList(RuleKey.of("squid", "AvoidCycle"))) + .userLogins(Lists.newArrayList("crunky")) .assigneeLogins(Lists.newArrayList("gargantua")) .createdAfter(new Date()) .createdBefore(new Date()) .limit(125) .offset(33) .build(); - assertThat(query.keys()).containsExactly("ABCDE"); - assertThat(query.severities()).containsExactly(Severity.BLOCKER); - assertThat(query.statuses()).containsExactly(Issue.STATUS_RESOLVED); - assertThat(query.resolutions()).containsExactly(Issue.RESOLUTION_FALSE_POSITIVE); - assertThat(query.components()).containsExactly("components"); - assertThat(query.componentRoots()).containsExactly("componentRoots"); - assertThat(query.userLogins()).containsExactly("user"); - assertThat(query.assigneeLogins()).containsExactly("gargantua"); - assertThat(query.ruleRepository()).isEqualTo("ruleRepository"); - assertThat(query.rule()).isEqualTo("rule"); + assertThat(query.keys()).containsOnly("ABCDE"); + assertThat(query.severities()).containsOnly(Severity.BLOCKER); + assertThat(query.statuses()).containsOnly(Issue.STATUS_RESOLVED); + assertThat(query.resolutions()).containsOnly(Issue.RESOLUTION_FALSE_POSITIVE); + assertThat(query.components()).containsOnly("org/struts/Action.java"); + assertThat(query.componentRoots()).containsOnly("org.struts:core"); + assertThat(query.userLogins()).containsOnly("crunky"); + assertThat(query.assigneeLogins()).containsOnly("gargantua"); + assertThat(query.rules()).containsOnly(RuleKey.of("squid", "AvoidCycle")); assertThat(query.createdAfter()).isNotNull(); assertThat(query.createdBefore()).isNotNull(); assertThat(query.limit()).isEqualTo(125); diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/rule/RuleKeyTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/rule/RuleKeyTest.java index d4411fb860f..aee99313bbc 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/rule/RuleKeyTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/rule/RuleKeyTest.java @@ -32,6 +32,46 @@ public class RuleKeyTest { assertThat(key.rule()).isEqualTo("NullDeref"); } + @Test + public void repository_must_not_be_null() throws Exception { + try { + RuleKey.of(null, "NullDeref"); + fail(); + } catch (IllegalArgumentException e) { + assertThat(e).hasMessage("Repository must be set"); + } + } + + @Test + public void repository_must_not_be_empty() throws Exception { + try { + RuleKey.of("", "NullDeref"); + fail(); + } catch (IllegalArgumentException e) { + assertThat(e).hasMessage("Repository must be set"); + } + } + + @Test + public void rule_must_not_be_null() throws Exception { + try { + RuleKey.of("squid", null); + fail(); + } catch (IllegalArgumentException e) { + assertThat(e).hasMessage("Rule must be set"); + } + } + + @Test + public void rule_must_not_be_empty() throws Exception { + try { + RuleKey.of("squid", ""); + fail(); + } catch (IllegalArgumentException e) { + assertThat(e).hasMessage("Rule must be set"); + } + } + @Test public void should_encode_and_decode_string() throws Exception { RuleKey key = RuleKey.of("squid", "NullDeref"); -- cgit v1.2.3