diff options
10 files changed, 252 insertions, 5 deletions
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingDecorator.java index e4ae221f2eb..0d66c96f94d 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingDecorator.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingDecorator.java @@ -36,6 +36,7 @@ import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.IssueDto; import javax.annotation.Nullable; + import java.util.ArrayList; import java.util.Collection; import java.util.Date; @@ -75,7 +76,6 @@ public class IssueTrackingDecorator implements Decorator { // not in newIssues addManualIssuesAndCloseResolvedOnes(openIssue); - closeResolvedStandardIssues(openIssue, issueKeys); keepFalsePositiveIssues(openIssue); reopenUnresolvedIssues(openIssue); diff --git a/sonar-core/src/main/java/org/sonar/core/issue/IssueDao.java b/sonar-core/src/main/java/org/sonar/core/issue/IssueDao.java index 4a190ba3449..faa60590ef8 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/IssueDao.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/IssueDao.java @@ -35,6 +35,7 @@ import java.util.List; import java.util.Map; import static com.google.common.collect.Lists.newArrayList; +import static com.google.common.collect.Maps.newHashMap; /** * @since 3.6 @@ -43,8 +44,11 @@ public class IssueDao implements BatchComponent, ServerComponent { private final MyBatis mybatis; + private Map<String, String> avalailableSorts; + public IssueDao(MyBatis mybatis) { this.mybatis = mybatis; + this.avalailableSorts = getAvalailableSorts(); } public void insert(IssueDto issueDto) { @@ -145,4 +149,13 @@ public class IssueDao implements BatchComponent, ServerComponent { return session.selectList("org.sonar.core.issue.IssueMapper.selectByIds", params); } + private Map<String, String> getAvalailableSorts() { + Map<String, String> availableSorts = newHashMap(); + availableSorts.put("created", "i.created_at"); + availableSorts.put("updated", "i.updated_at"); + availableSorts.put("closed", "i.closed_at"); + availableSorts.put("assignee", "i.assignee"); + return availableSorts; + } + } diff --git a/sonar-core/src/main/resources/org/sonar/core/issue/IssueMapper.xml b/sonar-core/src/main/resources/org/sonar/core/issue/IssueMapper.xml index bad09e26d69..15e299742a3 100644 --- a/sonar-core/src/main/resources/org/sonar/core/issue/IssueMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/issue/IssueMapper.xml @@ -196,13 +196,38 @@ <if test="rules != null and rules.size() > 0"> and (<foreach item="rule" index="index" collection="rules" open="(" separator=" or " close=")">r.plugin_name=#{rule.repository} and r.plugin_rule_key=#{rule.rule}</foreach>) </if> - <if test="createdAfter"> + <if test="createdAfter != null"> and i.created_at > #{createdAfter} </if> - <if test="createdBefore"> + <if test="createdBefore != null"> and i.created_at < #{createdBefore} </if> </where> + <if test="sort != null"> + order by + <choose> + <when test="'created'.equals(sort)"> + i.created_at + </when> + <when test="'updated'.equals(sort)"> + i.updated_at + </when> + <when test="'closed'.equals(sort)"> + i.closed_at + </when> + <when test="'assignee'.equals(sort)"> + i.assignee_login + </when> + </choose> + <choose> + <when test="true.equals(asc)"> + asc + </when> + <otherwise> + desc + </otherwise> + </choose> + </if> </sql> </mapper> diff --git a/sonar-core/src/test/java/org/sonar/core/issue/IssueDaoTest.java b/sonar-core/src/test/java/org/sonar/core/issue/IssueDaoTest.java index 3880b69cf9e..37dfa625f45 100644 --- a/sonar-core/src/test/java/org/sonar/core/issue/IssueDaoTest.java +++ b/sonar-core/src/test/java/org/sonar/core/issue/IssueDaoTest.java @@ -178,7 +178,7 @@ public class IssueDaoTest extends AbstractDaoTestCase { assertThat(dao.select(query)).hasSize(1); query = IssueQuery.builder().createdBefore(DateUtils.parseDate("2013-04-17")).build(); - assertThat(dao.select(query)).hasSize(1); + assertThat(dao.select(query)).hasSize(2); } @Test @@ -201,6 +201,18 @@ public class IssueDaoTest extends AbstractDaoTestCase { } @Test + public void should_select_sort_by_assignee() { + setupData("shared", "should_select_returned_sorted_result"); + + IssueQuery query = IssueQuery.builder().sort("assignee").asc(true).build(); + List<IssueDto> results = newArrayList(dao.select(query)); + assertThat(results).hasSize(3); + assertThat(results.get(0).getAssignee()).isEqualTo("arthur"); + assertThat(results.get(1).getAssignee()).isEqualTo("henry"); + assertThat(results.get(2).getAssignee()).isEqualTo("perceval"); + } + + @Test public void should_select_issue_ids_and_components_ids() { setupData("shared", "should_select_issue_ids_and_components_ids"); @@ -230,4 +242,5 @@ public class IssueDaoTest extends AbstractDaoTestCase { assertThat(results).hasSize(3); } + } diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_by_date_creation.xml b/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_by_date_creation.xml index 11fe9b857b8..e4d6cac1cd6 100644 --- a/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_by_date_creation.xml +++ b/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_by_date_creation.xml @@ -23,4 +23,27 @@ closed_at="2013-04-16" /> + <issues + id="101" + kee="ABCDE" + resource_id="400" + rule_id="500" + severity="BLOCKER" + manual_severity="[false]" + manual_issue="[false]" + description="[null]" + line="200" + cost="4.2" + status="OPEN" + resolution="FIXED" + checksum="XXX" + user_login="arthur" + assignee_login="perceval" + author_login="[null]" + attributes="JIRA=FOO-1234" + created_at="2013-04-13" + updated_at="2013-04-13" + closed_at="2013-04-13" + /> + </dataset> diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_returned_sorted_result.xml b/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_returned_sorted_result.xml new file mode 100644 index 00000000000..e77de4125c9 --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/issue/IssueDaoTest/should_select_returned_sorted_result.xml @@ -0,0 +1,74 @@ +<dataset> + + <!-- rule 500 --> + <issues + id="100" + kee="ABCDE" + resource_id="400" + rule_id="500" + severity="BLOCKER" + manual_severity="[false]" + manual_issue="[false]" + description="[null]" + line="200" + cost="4.2" + status="OPEN" + resolution="FIXED" + checksum="XXX" + user_login="arthur" + assignee_login="arthur" + author_login="[null]" + attributes="JIRA=FOO-1234" + created_at="2013-04-16" + updated_at="2013-04-16" + closed_at="2013-04-16" + /> + + <issues + id="101" + kee="ABCDE" + resource_id="400" + rule_id="500" + severity="BLOCKER" + manual_severity="[false]" + manual_issue="[false]" + description="[null]" + line="200" + cost="4.2" + status="OPEN" + resolution="FIXED" + checksum="XXX" + user_login="arthur" + assignee_login="perceval" + author_login="[null]" + attributes="JIRA=FOO-1234" + created_at="2013-04-16" + updated_at="2013-04-16" + closed_at="2013-04-16" + /> + + + <!-- rule 501 --> + <issues + id="102" + kee="ABCDE" + resource_id="400" + rule_id="501" + severity="BLOCKER" + manual_severity="[false]" + manual_issue="[false]" + description="[null]" + line="200" + cost="4.2" + status="OPEN" + resolution="FIXED" + checksum="XXX" + user_login="arthur" + assignee_login="henry" + author_login="[null]" + attributes="JIRA=FOO-1234" + created_at="2013-04-16" + updated_at="2013-04-16" + closed_at="2013-04-16" + /> +</dataset> 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 bde2cab4488..7ca75e4e14a 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 @@ -24,6 +24,7 @@ import com.google.common.base.Preconditions; import org.apache.commons.lang.builder.ReflectionToStringBuilder; import org.sonar.api.rule.RuleKey; +import java.util.Arrays; import java.util.Collection; import java.util.Date; @@ -45,6 +46,8 @@ public class IssueQuery { private final Collection<String> assignees; private final Date createdAfter; private final Date createdBefore; + private final String sort; + private final boolean asc; // max results per page private final int pageSize; @@ -64,6 +67,8 @@ public class IssueQuery { this.assignees = builder.assignees; this.createdAfter = builder.createdAfter; this.createdBefore = builder.createdBefore; + this.sort = builder.sort; + this.asc = builder.asc; this.pageSize = builder.pageSize; this.pageIndex = builder.pageIndex; } @@ -112,6 +117,14 @@ public class IssueQuery { return createdBefore; } + public String sort() { + return sort; + } + + public boolean asc() { + return asc; + } + public int pageSize() { return pageSize; } @@ -134,6 +147,11 @@ public class IssueQuery { * @since 3.6 */ public static class Builder { + + private enum Sort { + created, updated, closed, assignee; + } + private static final int DEFAULT_PAGE_SIZE = 100; private static final int MAX_PAGE_SIZE = 1000; private static final int DEFAULT_PAGE_INDEX = 1; @@ -149,6 +167,8 @@ public class IssueQuery { private Collection<String> assignees; private Date createdAfter; private Date createdBefore; + private String sort; + private boolean asc = false; private int pageSize = DEFAULT_PAGE_SIZE; private int pageIndex = DEFAULT_PAGE_INDEX; @@ -210,6 +230,20 @@ public class IssueQuery { return this; } + public Builder sort(String sort) { + try { + this.sort = Sort.valueOf(sort).name(); + } catch (IllegalArgumentException e){ + throw new IllegalArgumentException("Sort should contain only : " + Arrays.toString(Sort.values()), e); + } + return this; + } + + public Builder asc(boolean asc) { + this.asc = asc; + return this; + } + public Builder pageSize(Integer i) { Preconditions.checkArgument(i == null || i.intValue() > 0, "Page size must be greater than 0 (got " + i + ")"); Preconditions.checkArgument(i == null || i.intValue() < MAX_PAGE_SIZE, "Page size must be less than " + MAX_PAGE_SIZE + " (got " + i + ")"); 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 cb47aec4e18..9d8a9adbdcc 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 @@ -27,6 +27,7 @@ import org.sonar.api.rule.Severity; import java.util.Date; import static org.fest.assertions.Assertions.assertThat; +import static org.fest.assertions.Fail.fail; public class IssueQueryTest { @@ -44,6 +45,7 @@ public class IssueQueryTest { .assignees(Lists.newArrayList("gargantua")) .createdAfter(new Date()) .createdBefore(new Date()) + .sort("assignee") .pageSize(10) .pageIndex(2) .build(); @@ -58,7 +60,56 @@ public class IssueQueryTest { assertThat(query.rules()).containsOnly(RuleKey.of("squid", "AvoidCycle")); assertThat(query.createdAfter()).isNotNull(); assertThat(query.createdBefore()).isNotNull(); + assertThat(query.sort()).isEqualTo("assignee"); assertThat(query.pageSize()).isEqualTo(10); assertThat(query.pageIndex()).isEqualTo(2); } + + @Test + public void should_validate_page_size() throws Exception { + try { + IssueQuery.builder() + .pageSize(0) + .build(); + fail(); + } catch (Exception e) { + assertThat(e).hasMessage("Page size must be greater than 0 (got 0)").isInstanceOf(IllegalArgumentException.class); + } + } + + @Test + public void should_validate_page_size_too_high() throws Exception { + try { + IssueQuery.builder() + .pageSize(10000) + .build(); + fail(); + } catch (Exception e) { + assertThat(e).hasMessage("Page size must be less than 1000 (got 10000)").isInstanceOf(IllegalArgumentException.class); + } + } + + @Test + public void should_validate_page_index() throws Exception { + try { + IssueQuery.builder() + .pageIndex(0) + .build(); + fail(); + } catch (Exception e) { + assertThat(e).hasMessage("Page index must be greater than 0 (got 0)").isInstanceOf(IllegalArgumentException.class); + } + } + + @Test + public void should_validate_sort() throws Exception { + try { + IssueQuery.builder() + .sort("INVALID SORT") + .build(); + fail(); + } catch (Exception e) { + assertThat(e).hasMessage("Sort should contain only : [created, updated, closed, assignee]").isInstanceOf(IllegalArgumentException.class); + } + } } diff --git a/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/IssueQuery.java b/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/IssueQuery.java index 34b822230f5..e4d38dc3bef 100644 --- a/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/IssueQuery.java +++ b/sonar-ws-client/src/main/java/org/sonar/wsclient/issue/IssueQuery.java @@ -90,6 +90,16 @@ public class IssueQuery { return this; } + public IssueQuery sort(String sort) { + params.put("sort", sort); + return this; + } + + public IssueQuery asc(boolean asc) { + params.put("asc", asc); + return this; + } + public IssueQuery pageSize(int pageSize) { params.put("pageSize", pageSize); return this; diff --git a/sonar-ws-client/src/test/java/org/sonar/wsclient/issue/IssueQueryTest.java b/sonar-ws-client/src/test/java/org/sonar/wsclient/issue/IssueQueryTest.java index a82a6658f1b..4a37fed1e80 100644 --- a/sonar-ws-client/src/test/java/org/sonar/wsclient/issue/IssueQueryTest.java +++ b/sonar-ws-client/src/test/java/org/sonar/wsclient/issue/IssueQueryTest.java @@ -43,10 +43,12 @@ public class IssueQueryTest { .statuses("OPEN", "CLOSED") .severities("BLOCKER", "INFO") .userLogins("login1", "login2") + .sort("assignee") + .asc(false) .pageSize(5) .pageIndex(4); - assertThat(query.urlParams()).hasSize(11); + assertThat(query.urlParams()).hasSize(13); assertThat(query.urlParams()).includes(entry("keys", "ABCDE,FGHIJ")); assertThat(query.urlParams()).includes(entry("assignees", "arthur,perceval")); assertThat(query.urlParams()).includes(entry("components", "Action.java,Filter.java")); @@ -55,6 +57,8 @@ public class IssueQueryTest { assertThat(query.urlParams()).includes(entry("statuses", "OPEN,CLOSED")); assertThat(query.urlParams()).includes(entry("severities", "BLOCKER,INFO")); assertThat(query.urlParams()).includes(entry("userLogins", "login1,login2")); + assertThat(query.urlParams()).includes(entry("sort", "assignee")); + assertThat(query.urlParams()).includes(entry("asc", false)); assertThat(query.urlParams()).includes(entry("pageSize", 5)); assertThat(query.urlParams()).includes(entry("pageIndex", 4)); } |