]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-4301 Add sort on issue search page
authorJulien Lancelot <julien.lancelot@gmail.com>
Tue, 21 May 2013 10:12:17 +0000 (12:12 +0200)
committerJulien Lancelot <julien.lancelot@gmail.com>
Tue, 21 May 2013 10:12:17 +0000 (12:12 +0200)
23 files changed:
sonar-core/src/main/java/org/sonar/core/issue/db/IssueDao.java
sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml
sonar-core/src/test/java/org/sonar/core/issue/db/IssueDaoTest.java
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_by_ids.xml
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_returned_sorted_result.xml [deleted file]
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_returned_sorted_result_by_assignee.xml [new file with mode: 0644]
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_returned_sorted_result_by_close_date.xml [new file with mode: 0644]
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_returned_sorted_result_by_creation_date.xml [new file with mode: 0644]
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_returned_sorted_result_by_severity.xml [new file with mode: 0644]
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_returned_sorted_result_by_status.xml [new file with mode: 0644]
sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_returned_sorted_result_by_update_date.xml [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQuery.java
sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueFinder.java
sonar-server/src/main/java/org/sonar/server/issue/PublicRubyIssueService.java
sonar-server/src/main/webapp/WEB-INF/app/controllers/issues_controller.rb
sonar-server/src/main/webapp/WEB-INF/app/helpers/application_helper.rb
sonar-server/src/main/webapp/WEB-INF/app/helpers/issues_helper.rb [new file with mode: 0644]
sonar-server/src/main/webapp/WEB-INF/app/models/issue_filter.rb
sonar-server/src/main/webapp/WEB-INF/app/views/issues/_list.html.erb
sonar-server/src/main/webapp/WEB-INF/app/views/issues/_sidebar.html.erb
sonar-server/src/main/webapp/WEB-INF/app/views/issues/search.html.erb
sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueFinderTest.java
sonar-server/src/test/java/org/sonar/server/issue/PublicRubyIssueServiceTest.java

index 6a67dcf6774ce1d4c21d848f6c86f9ae2e5c197a..2fa1dba1ed13cb82d13a4062671ef91ca7924e59 100644 (file)
@@ -30,6 +30,7 @@ import org.sonar.api.issue.IssueQuery;
 import org.sonar.core.persistence.MyBatis;
 
 import javax.annotation.CheckForNull;
+
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -97,21 +98,22 @@ public class IssueDao implements BatchComponent, ServerComponent {
     return mapper.selectIssueAndComponentIds(query);
   }
 
-  Collection<IssueDto> selectByIds(Collection<Long> ids) {
+  @VisibleForTesting
+  Collection<IssueDto> selectByIds(Collection<Long> ids, IssueQuery.Sort sort, boolean asc) {
     SqlSession session = mybatis.openSession();
     try {
-      return selectByIds(ids, session);
+      return selectByIds(ids, sort, asc, session);
     } finally {
       MyBatis.closeQuietly(session);
     }
   }
 
-  public Collection<IssueDto> selectByIds(Collection<Long> ids, SqlSession session) {
+  public Collection<IssueDto> selectByIds(Collection<Long> ids, IssueQuery.Sort sort, boolean asc, SqlSession session) {
     if (ids.isEmpty()) {
       return Collections.emptyList();
     }
-    List<List<Long>> idsPartition = Lists.partition(newArrayList(ids), 1000);
-    Map<String, List<List<Long>>> params = ImmutableMap.of("ids", idsPartition);
+    Object idsPartition = Lists.partition(newArrayList(ids), 1000);
+    Map<String, Object> params = ImmutableMap.of("ids", idsPartition, "sort", sort, "asc", asc);
     return session.selectList("org.sonar.core.issue.db.IssueMapper.selectByIds", params);
   }
 }
index 40fc177e27da176bdb8395c667444eddfe8f58ba..9717a2417cfb86c8c7946ef7614b1d981086d10c 100644 (file)
     p.kee as componentKey
   </sql>
 
+  <sql id="sortFilter">
+    <if test="sort != null">
+      order by
+      <choose>
+        <when test="'SEVERITY'.equals(sort.name())">
+          i.severity
+        </when>
+        <when test="'STATUS'.equals(sort.name())">
+          i.status
+        </when>
+        <when test="'ASSIGNEE'.equals(sort.name())">
+          i.assignee
+        </when>
+        <when test="'CREATION_DATE'.equals(sort.name())">
+          i.issue_creation_date
+        </when>
+        <when test="'UPDATE_DATE'.equals(sort.name())">
+          i.issue_update_date
+        </when>
+        <when test="'CLOSE_DATE'.equals(sort.name())">
+          i.issue_close_date
+        </when>
+      </choose>
+      <choose>
+        <when test="true.equals(asc)">
+          asc
+        </when>
+        <otherwise>
+          desc
+        </otherwise>
+      </choose>
+    </if>
+  </sql>
+
   <insert id="insert" parameterType="Issue" useGeneratedKeys="false" keyProperty="id">
     INSERT INTO issues (kee, resource_id, rule_id, action_plan_key, severity, manual_severity,
     message, line, effort_to_fix, status,
       and i.rule_id=r.id
       and p.id=i.resource_id
     </where>
+    <include refid="sortFilter"/>
   </select>
 
   <select id="selectIssueAndComponentIds" parameterType="map" resultType="Issue">
     <if test="sort == null">
       order by i.id
     </if>
-    <if test="sort != null">
-      order by
-      <choose>
-        <when test="'CREATION_DATE'.equals(sort.name())">
-          i.issue_creation_date
-        </when>
-        <when test="'UPDATE_DATE'.equals(sort.name())">
-          i.issue_update_date
-        </when>
-        <when test="'CLOSE_DATE'.equals(sort.name())">
-          i.issue_close_date
-        </when>
-        <when test="'ASSIGNEE'.equals(sort.name())">
-          i.assignee
-        </when>
-      </choose>
-      <choose>
-        <when test="true.equals(asc)">
-          asc
-        </when>
-        <otherwise>
-          desc
-        </otherwise>
-      </choose>
-    </if>
+    <include refid="sortFilter"/>
   </sql>
 
 </mapper>
index 2bc8932b731509741a84f980161c8fbc19123694..cfb30567f2100817a94985030275c38b6687b268 100644 (file)
@@ -27,7 +27,6 @@ import org.sonar.api.rule.RuleKey;
 import org.sonar.api.utils.DateUtils;
 import org.sonar.core.persistence.AbstractDaoTestCase;
 
-import java.util.Collection;
 import java.util.List;
 
 import static com.google.common.collect.Lists.newArrayList;
@@ -221,9 +220,33 @@ public class IssueDaoTest extends AbstractDaoTestCase {
     assertThat(dao.select(query)).hasSize(3);
   }
 
+  @Test
+  public void should_select_sort_by_severity() {
+    setupData("shared", "should_select_returned_sorted_result_by_severity");
+
+    IssueQuery query = IssueQuery.builder().sort(IssueQuery.Sort.SEVERITY).asc(true).build();
+    List<IssueDto> results = newArrayList(dao.select(query));
+    assertThat(results).hasSize(3);
+    assertThat(results.get(0).getSeverity()).isEqualTo("BLOCKER");
+    assertThat(results.get(1).getSeverity()).isEqualTo("MAJOR");
+    assertThat(results.get(2).getSeverity()).isEqualTo("MINOR");
+  }
+
+  @Test
+  public void should_select_sort_by_status() {
+    setupData("shared", "should_select_returned_sorted_result_by_status");
+
+    IssueQuery query = IssueQuery.builder().sort(IssueQuery.Sort.STATUS).asc(true).build();
+    List<IssueDto> results = newArrayList(dao.select(query));
+    assertThat(results).hasSize(3);
+    assertThat(results.get(0).getStatus()).isEqualTo("CLOSED");
+    assertThat(results.get(1).getStatus()).isEqualTo("OPEN");
+    assertThat(results.get(2).getStatus()).isEqualTo("REOPEN");
+  }
+
   @Test
   public void should_select_sort_by_assignee() {
-    setupData("shared", "should_select_returned_sorted_result");
+    setupData("shared", "should_select_returned_sorted_result_by_assignee");
 
     IssueQuery query = IssueQuery.builder().sort(IssueQuery.Sort.ASSIGNEE).asc(true).build();
     List<IssueDto> results = newArrayList(dao.select(query));
@@ -233,6 +256,42 @@ public class IssueDaoTest extends AbstractDaoTestCase {
     assertThat(results.get(2).getAssignee()).isEqualTo("perceval");
   }
 
+  @Test
+  public void should_select_sort_by_creation_date() {
+    setupData("shared", "should_select_returned_sorted_result_by_creation_date");
+
+    IssueQuery query = IssueQuery.builder().sort(IssueQuery.Sort.CREATION_DATE).asc(false).build();
+    List<IssueDto> results = newArrayList(dao.select(query));
+    assertThat(results).hasSize(3);
+    assertThat(results.get(0).getId()).isEqualTo(102);
+    assertThat(results.get(1).getId()).isEqualTo(100);
+    assertThat(results.get(2).getId()).isEqualTo(101);
+  }
+
+  @Test
+  public void should_select_sort_by_update_date() {
+    setupData("shared", "should_select_returned_sorted_result_by_update_date");
+
+    IssueQuery query = IssueQuery.builder().sort(IssueQuery.Sort.UPDATE_DATE).asc(false).build();
+    List<IssueDto> results = newArrayList(dao.select(query));
+    assertThat(results).hasSize(3);
+    assertThat(results.get(0).getId()).isEqualTo(102);
+    assertThat(results.get(1).getId()).isEqualTo(100);
+    assertThat(results.get(2).getId()).isEqualTo(101);
+  }
+
+  @Test
+  public void should_select_sort_by_close_date() {
+    setupData("shared", "should_select_returned_sorted_result_by_close_date");
+
+    IssueQuery query = IssueQuery.builder().sort(IssueQuery.Sort.CLOSE_DATE).asc(false).build();
+    List<IssueDto> results = newArrayList(dao.select(query));
+    assertThat(results).hasSize(3);
+    assertThat(results.get(0).getId()).isEqualTo(102);
+    assertThat(results.get(1).getId()).isEqualTo(100);
+    assertThat(results.get(2).getId()).isEqualTo(101);
+  }
+
   @Test
   public void should_select_issue_and_component_ids() {
     setupData("shared", "should_select_issue_and_component_ids");
@@ -259,7 +318,10 @@ public class IssueDaoTest extends AbstractDaoTestCase {
   public void should_select_by_ids() {
     setupData("shared", "should_select_by_ids");
 
-    Collection<IssueDto> results = dao.selectByIds(newArrayList(100l, 101l, 102l));
+    List<IssueDto> results = newArrayList(dao.selectByIds(newArrayList(100l, 101l, 102l), IssueQuery.Sort.CREATION_DATE, false));
     assertThat(results).hasSize(3);
+    assertThat(results.get(0).getId()).isEqualTo(102);
+    assertThat(results.get(1).getId()).isEqualTo(100);
+    assertThat(results.get(2).getId()).isEqualTo(101);
   }
 }
index d2804c856d3af2ef89625c0c4a61f41ef67e585f..3d6ad15c5a062f9afb02b7e23ecdf4f1c11b022b 100644 (file)
       assignee="perceval"
       author_login="[null]"
       attributes="JIRA=FOO-1234"
-      issue_creation_date="2013-04-16"
-      issue_update_date="2013-04-16"
-      issue_close_date="2013-04-16"
-      created_at="2013-04-16"
-      updated_at="2013-04-16"
+      issue_creation_date="2013-04-17"
+      issue_update_date="2013-04-17"
+      issue_close_date="2013-04-17"
+      created_at="2013-04-17"
+      updated_at="2013-04-17"
       />
 
   <issues
       assignee="perceval"
       author_login="[null]"
       attributes="JIRA=FOO-1234"
-      issue_creation_date="2013-04-16"
-      issue_update_date="2013-04-16"
-      issue_close_date="2013-04-16"
-      created_at="2013-04-16"
-      updated_at="2013-04-16"
+      issue_creation_date="2013-04-18"
+      issue_update_date="2013-04-18"
+      issue_close_date="2013-04-18"
+      created_at="2013-04-18"
+      updated_at="2013-04-18"
       />
 </dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_returned_sorted_result.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_returned_sorted_result.xml
deleted file mode 100644 (file)
index 41a97ef..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-<dataset>
-
-  <!-- rule 500 -->
-  <issues
-      id="100"
-      kee="ABCDE-1"
-      resource_id="401"
-      rule_id="500"
-      severity="BLOCKER"
-      manual_severity="[false]"
-      message="[null]"
-      line="200"
-      effort_to_fix="4.2"
-      status="OPEN"
-      resolution="FIXED"
-      checksum="XXX"
-      reporter="arthur"
-      assignee="arthur"
-      author_login="[null]"
-      attributes="JIRA=FOO-1234"
-      issue_creation_date="2013-04-16"
-      issue_update_date="2013-04-16"
-      issue_close_date="2013-04-16"
-      created_at="2013-04-16"
-      updated_at="2013-04-16"
-      />
-
-  <issues
-      id="101"
-      kee="ABCDE-2"
-      resource_id="401"
-      rule_id="500"
-      severity="BLOCKER"
-      manual_severity="[false]"
-      message="[null]"
-      line="200"
-      effort_to_fix="4.2"
-      status="OPEN"
-      resolution="FIXED"
-      checksum="XXX"
-      reporter="arthur"
-      assignee="perceval"
-      author_login="[null]"
-      attributes="JIRA=FOO-1234"
-      issue_creation_date="2013-04-16"
-      issue_update_date="2013-04-16"
-      issue_close_date="2013-04-16"
-      created_at="2013-04-16"
-      updated_at="2013-04-16"
-      />
-
-
-  <!-- rule 501 -->
-  <issues
-      id="102"
-      kee="ABCDE-3"
-      resource_id="401"
-      rule_id="501"
-      severity="BLOCKER"
-      manual_severity="[false]"
-      message="[null]"
-      line="200"
-      effort_to_fix="4.2"
-      status="OPEN"
-      resolution="FIXED"
-      checksum="XXX"
-      reporter="arthur"
-      assignee="henry"
-      author_login="[null]"
-      attributes="JIRA=FOO-1234"
-      issue_creation_date="2013-04-16"
-      issue_update_date="2013-04-16"
-      issue_close_date="2013-04-16"
-      created_at="2013-04-16"
-      updated_at="2013-04-16"
-      />
-</dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_returned_sorted_result_by_assignee.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_returned_sorted_result_by_assignee.xml
new file mode 100644 (file)
index 0000000..41a97ef
--- /dev/null
@@ -0,0 +1,77 @@
+<dataset>
+
+  <!-- rule 500 -->
+  <issues
+      id="100"
+      kee="ABCDE-1"
+      resource_id="401"
+      rule_id="500"
+      severity="BLOCKER"
+      manual_severity="[false]"
+      message="[null]"
+      line="200"
+      effort_to_fix="4.2"
+      status="OPEN"
+      resolution="FIXED"
+      checksum="XXX"
+      reporter="arthur"
+      assignee="arthur"
+      author_login="[null]"
+      attributes="JIRA=FOO-1234"
+      issue_creation_date="2013-04-16"
+      issue_update_date="2013-04-16"
+      issue_close_date="2013-04-16"
+      created_at="2013-04-16"
+      updated_at="2013-04-16"
+      />
+
+  <issues
+      id="101"
+      kee="ABCDE-2"
+      resource_id="401"
+      rule_id="500"
+      severity="BLOCKER"
+      manual_severity="[false]"
+      message="[null]"
+      line="200"
+      effort_to_fix="4.2"
+      status="OPEN"
+      resolution="FIXED"
+      checksum="XXX"
+      reporter="arthur"
+      assignee="perceval"
+      author_login="[null]"
+      attributes="JIRA=FOO-1234"
+      issue_creation_date="2013-04-16"
+      issue_update_date="2013-04-16"
+      issue_close_date="2013-04-16"
+      created_at="2013-04-16"
+      updated_at="2013-04-16"
+      />
+
+
+  <!-- rule 501 -->
+  <issues
+      id="102"
+      kee="ABCDE-3"
+      resource_id="401"
+      rule_id="501"
+      severity="BLOCKER"
+      manual_severity="[false]"
+      message="[null]"
+      line="200"
+      effort_to_fix="4.2"
+      status="OPEN"
+      resolution="FIXED"
+      checksum="XXX"
+      reporter="arthur"
+      assignee="henry"
+      author_login="[null]"
+      attributes="JIRA=FOO-1234"
+      issue_creation_date="2013-04-16"
+      issue_update_date="2013-04-16"
+      issue_close_date="2013-04-16"
+      created_at="2013-04-16"
+      updated_at="2013-04-16"
+      />
+</dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_returned_sorted_result_by_close_date.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_returned_sorted_result_by_close_date.xml
new file mode 100644 (file)
index 0000000..b27051f
--- /dev/null
@@ -0,0 +1,77 @@
+<dataset>
+
+  <!-- rule 500 -->
+  <issues
+      id="100"
+      kee="ABCDE-1"
+      resource_id="401"
+      rule_id="500"
+      severity="BLOCKER"
+      manual_severity="[false]"
+      message="[null]"
+      line="200"
+      effort_to_fix="4.2"
+      status="OPEN"
+      resolution="FIXED"
+      checksum="XXX"
+      reporter="arthur"
+      assignee="arthur"
+      author_login="[null]"
+      attributes="JIRA=FOO-1234"
+      issue_creation_date="2013-04-16"
+      issue_update_date="2013-04-17"
+      issue_close_date="2013-04-17"
+      created_at="2013-04-16"
+      updated_at="2013-04-17"
+      />
+
+  <issues
+      id="101"
+      kee="ABCDE-2"
+      resource_id="401"
+      rule_id="500"
+      severity="BLOCKER"
+      manual_severity="[false]"
+      message="[null]"
+      line="200"
+      effort_to_fix="4.2"
+      status="OPEN"
+      resolution="FIXED"
+      checksum="XXX"
+      reporter="arthur"
+      assignee="perceval"
+      author_login="[null]"
+      attributes="JIRA=FOO-1234"
+      issue_creation_date="2013-04-16"
+      issue_update_date="2013-04-16"
+      issue_close_date="2013-04-16"
+      created_at="2013-04-16"
+      updated_at="2013-04-16"
+      />
+
+
+  <!-- rule 501 -->
+  <issues
+      id="102"
+      kee="ABCDE-3"
+      resource_id="401"
+      rule_id="501"
+      severity="BLOCKER"
+      manual_severity="[false]"
+      message="[null]"
+      line="200"
+      effort_to_fix="4.2"
+      status="OPEN"
+      resolution="FIXED"
+      checksum="XXX"
+      reporter="arthur"
+      assignee="henry"
+      author_login="[null]"
+      attributes="JIRA=FOO-1234"
+      issue_creation_date="2013-04-16"
+      issue_update_date="2013-04-18"
+      issue_close_date="2013-04-18"
+      created_at="2013-04-16"
+      updated_at="2013-04-18"
+      />
+</dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_returned_sorted_result_by_creation_date.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_returned_sorted_result_by_creation_date.xml
new file mode 100644 (file)
index 0000000..d84b71f
--- /dev/null
@@ -0,0 +1,74 @@
+<dataset>
+
+  <issues
+      id="100"
+      kee="ABCDE-1"
+      resource_id="401"
+      rule_id="500"
+      severity="BLOCKER"
+      manual_severity="[false]"
+      message="[null]"
+      line="200"
+      effort_to_fix="4.2"
+      status="OPEN"
+      resolution="FIXED"
+      checksum="XXX"
+      reporter="arthur"
+      assignee="arthur"
+      author_login="[null]"
+      attributes="JIRA=FOO-1234"
+      issue_creation_date="2013-04-15"
+      issue_update_date="2013-04-16"
+      issue_close_date="2013-04-16"
+      created_at="2013-04-15"
+      updated_at="2013-04-16"
+      />
+
+  <issues
+      id="101"
+      kee="ABCDE-2"
+      resource_id="401"
+      rule_id="500"
+      severity="BLOCKER"
+      manual_severity="[false]"
+      message="[null]"
+      line="200"
+      effort_to_fix="4.2"
+      status="OPEN"
+      resolution="FIXED"
+      checksum="XXX"
+      reporter="arthur"
+      assignee="perceval"
+      author_login="[null]"
+      attributes="JIRA=FOO-1234"
+      issue_creation_date="2013-04-14"
+      issue_update_date="2013-04-16"
+      issue_close_date="2013-04-16"
+      created_at="2013-04-14"
+      updated_at="2013-04-16"
+      />
+
+  <issues
+      id="102"
+      kee="ABCDE-3"
+      resource_id="401"
+      rule_id="501"
+      severity="BLOCKER"
+      manual_severity="[false]"
+      message="[null]"
+      line="200"
+      effort_to_fix="4.2"
+      status="OPEN"
+      resolution="FIXED"
+      checksum="XXX"
+      reporter="arthur"
+      assignee="henry"
+      author_login="[null]"
+      attributes="JIRA=FOO-1234"
+      issue_creation_date="2013-04-16"
+      issue_update_date="2013-04-16"
+      issue_close_date="2013-04-16"
+      created_at="2013-04-16"
+      updated_at="2013-04-16"
+      />
+</dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_returned_sorted_result_by_severity.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_returned_sorted_result_by_severity.xml
new file mode 100644 (file)
index 0000000..9dae408
--- /dev/null
@@ -0,0 +1,74 @@
+<dataset>
+
+  <issues
+      id="100"
+      kee="ABCDE-1"
+      resource_id="401"
+      rule_id="500"
+      severity="MINOR"
+      manual_severity="[false]"
+      message="[null]"
+      line="200"
+      effort_to_fix="4.2"
+      status="OPEN"
+      resolution="FIXED"
+      checksum="XXX"
+      reporter="arthur"
+      assignee="arthur"
+      author_login="[null]"
+      attributes="JIRA=FOO-1234"
+      issue_creation_date="2013-04-16"
+      issue_update_date="2013-04-16"
+      issue_close_date="2013-04-16"
+      created_at="2013-04-16"
+      updated_at="2013-04-16"
+      />
+
+  <issues
+      id="101"
+      kee="ABCDE-2"
+      resource_id="401"
+      rule_id="500"
+      severity="BLOCKER"
+      manual_severity="[false]"
+      message="[null]"
+      line="200"
+      effort_to_fix="4.2"
+      status="OPEN"
+      resolution="FIXED"
+      checksum="XXX"
+      reporter="arthur"
+      assignee="perceval"
+      author_login="[null]"
+      attributes="JIRA=FOO-1234"
+      issue_creation_date="2013-04-16"
+      issue_update_date="2013-04-16"
+      issue_close_date="2013-04-16"
+      created_at="2013-04-16"
+      updated_at="2013-04-16"
+      />
+
+  <issues
+      id="102"
+      kee="ABCDE-3"
+      resource_id="401"
+      rule_id="501"
+      severity="MAJOR"
+      manual_severity="[false]"
+      message="[null]"
+      line="200"
+      effort_to_fix="4.2"
+      status="OPEN"
+      resolution="FIXED"
+      checksum="XXX"
+      reporter="arthur"
+      assignee="henry"
+      author_login="[null]"
+      attributes="JIRA=FOO-1234"
+      issue_creation_date="2013-04-16"
+      issue_update_date="2013-04-16"
+      issue_close_date="2013-04-16"
+      created_at="2013-04-16"
+      updated_at="2013-04-16"
+      />
+</dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_returned_sorted_result_by_status.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_returned_sorted_result_by_status.xml
new file mode 100644 (file)
index 0000000..b510a5c
--- /dev/null
@@ -0,0 +1,77 @@
+<dataset>
+
+  <!-- rule 500 -->
+  <issues
+      id="100"
+      kee="ABCDE-1"
+      resource_id="401"
+      rule_id="500"
+      severity="BLOCKER"
+      manual_severity="[false]"
+      message="[null]"
+      line="200"
+      effort_to_fix="4.2"
+      status="OPEN"
+      resolution="FIXED"
+      checksum="XXX"
+      reporter="arthur"
+      assignee="arthur"
+      author_login="[null]"
+      attributes="JIRA=FOO-1234"
+      issue_creation_date="2013-04-16"
+      issue_update_date="2013-04-16"
+      issue_close_date="2013-04-16"
+      created_at="2013-04-16"
+      updated_at="2013-04-16"
+      />
+
+  <issues
+      id="101"
+      kee="ABCDE-2"
+      resource_id="401"
+      rule_id="500"
+      severity="BLOCKER"
+      manual_severity="[false]"
+      message="[null]"
+      line="200"
+      effort_to_fix="4.2"
+      status="CLOSED"
+      resolution="FIXED"
+      checksum="XXX"
+      reporter="arthur"
+      assignee="perceval"
+      author_login="[null]"
+      attributes="JIRA=FOO-1234"
+      issue_creation_date="2013-04-16"
+      issue_update_date="2013-04-16"
+      issue_close_date="2013-04-16"
+      created_at="2013-04-16"
+      updated_at="2013-04-16"
+      />
+
+
+  <!-- rule 501 -->
+  <issues
+      id="102"
+      kee="ABCDE-3"
+      resource_id="401"
+      rule_id="501"
+      severity="BLOCKER"
+      manual_severity="[false]"
+      message="[null]"
+      line="200"
+      effort_to_fix="4.2"
+      status="REOPEN"
+      resolution="FIXED"
+      checksum="XXX"
+      reporter="arthur"
+      assignee="henry"
+      author_login="[null]"
+      attributes="JIRA=FOO-1234"
+      issue_creation_date="2013-04-16"
+      issue_update_date="2013-04-16"
+      issue_close_date="2013-04-16"
+      created_at="2013-04-16"
+      updated_at="2013-04-16"
+      />
+</dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_returned_sorted_result_by_update_date.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueDaoTest/should_select_returned_sorted_result_by_update_date.xml
new file mode 100644 (file)
index 0000000..769bae1
--- /dev/null
@@ -0,0 +1,74 @@
+<dataset>
+
+  <issues
+      id="100"
+      kee="ABCDE-1"
+      resource_id="401"
+      rule_id="500"
+      severity="BLOCKER"
+      manual_severity="[false]"
+      message="[null]"
+      line="200"
+      effort_to_fix="4.2"
+      status="OPEN"
+      resolution="FIXED"
+      checksum="XXX"
+      reporter="arthur"
+      assignee="arthur"
+      author_login="[null]"
+      attributes="JIRA=FOO-1234"
+      issue_creation_date="2013-04-16"
+      issue_update_date="2013-04-17"
+      issue_close_date="2013-04-17"
+      created_at="2013-04-16"
+      updated_at="2013-04-17"
+      />
+
+  <issues
+      id="101"
+      kee="ABCDE-2"
+      resource_id="401"
+      rule_id="500"
+      severity="BLOCKER"
+      manual_severity="[false]"
+      message="[null]"
+      line="200"
+      effort_to_fix="4.2"
+      status="OPEN"
+      resolution="FIXED"
+      checksum="XXX"
+      reporter="arthur"
+      assignee="perceval"
+      author_login="[null]"
+      attributes="JIRA=FOO-1234"
+      issue_creation_date="2013-04-16"
+      issue_update_date="2013-04-16"
+      issue_close_date="2013-04-16"
+      created_at="2013-04-16"
+      updated_at="2013-04-16"
+      />
+
+  <issues
+      id="102"
+      kee="ABCDE-3"
+      resource_id="401"
+      rule_id="501"
+      severity="BLOCKER"
+      manual_severity="[false]"
+      message="[null]"
+      line="200"
+      effort_to_fix="4.2"
+      status="OPEN"
+      resolution="FIXED"
+      checksum="XXX"
+      reporter="arthur"
+      assignee="henry"
+      author_login="[null]"
+      attributes="JIRA=FOO-1234"
+      issue_creation_date="2013-04-16"
+      issue_update_date="2013-04-18"
+      issue_close_date="2013-04-18"
+      created_at="2013-04-16"
+      updated_at="2013-04-18"
+      />
+</dataset>
index 869be8aca7e4281609f5bba0cff76487c06294e2..4a74dedf0fbafe058f9fdeb6a561adf17336701e 100644 (file)
@@ -25,6 +25,7 @@ import org.sonar.api.rule.RuleKey;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
+
 import java.util.Collection;
 import java.util.Date;
 
@@ -39,7 +40,7 @@ public class IssueQuery {
   public static final int MAX_ISSUE_KEYS = 500;
 
   public static enum Sort {
-    CREATION_DATE, UPDATE_DATE, CLOSE_DATE, ASSIGNEE, SEVERITY
+    CREATION_DATE, UPDATE_DATE, CLOSE_DATE, ASSIGNEE, SEVERITY, STATUS
   }
 
   private final Collection<String> issueKeys;
@@ -58,7 +59,7 @@ public class IssueQuery {
   private final Date createdAfter;
   private final Date createdBefore;
   private final Sort sort;
-  private final boolean asc;
+  private final Boolean asc;
   private final String requiredRole;
 
   // max results per page
@@ -154,7 +155,7 @@ public class IssueQuery {
     return sort;
   }
 
-  public boolean asc() {
+  public Boolean asc() {
     return asc;
   }
 
@@ -197,7 +198,7 @@ public class IssueQuery {
     private Date createdAfter;
     private Date createdBefore;
     private Sort sort;
-    private boolean asc = false;
+    private Boolean asc = false;
     private Integer pageSize;
     private Integer pageIndex;
     private String requiredRole;
@@ -297,7 +298,7 @@ public class IssueQuery {
       return this;
     }
 
-    public Builder asc(boolean asc) {
+    public Builder asc(Boolean asc) {
       this.asc = asc;
       return this;
     }
index 515b240daab0488d975662d07b8947356c952142..f0fb6517564f69195cfebb5faf269cd550e7cc57 100644 (file)
@@ -46,6 +46,7 @@ import org.sonar.core.user.AuthorizationDao;
 import org.sonar.server.platform.UserSession;
 
 import javax.annotation.CheckForNull;
+
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
@@ -117,7 +118,7 @@ public class DefaultIssueFinder implements IssueFinder {
       Set<Long> pagedIssueIds = pagedIssueIds(authorizedIssues, paging);
 
       // 4. Load issues and their related data (rules, components, comments, action plans, ...)
-      Collection<IssueDto> pagedIssues = issueDao.selectByIds(pagedIssueIds, sqlSession);
+      Collection<IssueDto> pagedIssues = issueDao.selectByIds(pagedIssueIds, query.sort(), query.asc(), sqlSession);
       Map<String, DefaultIssue> issuesByKey = newHashMap();
       List<Issue> issues = newArrayList();
       Set<Integer> ruleIds = Sets.newHashSet();
index eac63549e2579c7d93a0153ea2cf33be635b76de..f9ffe8071f961d724ef85319c2ba8b9683cf94f7 100644 (file)
@@ -32,6 +32,7 @@ import org.sonar.api.web.UserRole;
 import org.sonar.server.util.RubyUtils;
 
 import javax.annotation.Nullable;
+
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
@@ -84,7 +85,8 @@ public class PublicRubyIssueService implements RubyIssueService {
     builder.pageIndex(RubyUtils.toInteger(props.get("pageIndex")));
     String sort = (String) props.get("sort");
     if (sort != null) {
-      builder.sort(IssueQuery.Sort.valueOf(sort));
+      builder.sort(IssueQuery.Sort.valueOf(sort.toUpperCase()));
+      builder.asc(RubyUtils.toBoolean(props.get("asc")));
     }
     return builder.build();
   }
index c10e77552b69d2d1291d229cb5579b8d8eb1dd7b..ff855c03b5cae22b6ea2fc08d591d93b3f6a3434 100644 (file)
@@ -32,7 +32,6 @@ class IssuesController < ApplicationController
     @filter.criteria=criteria_params
     @filter.execute
 
-    # TODO replace by project from issues result API
     @project = Project.by_key(@filter.criteria('componentRoots')).root_project if @filter.criteria('componentRoots')
   end
 
index 2b8617e2bffc95907d0478d8a0a9d9a2868516b1..013da3c1bb88e45b538e487ad4413cb4dd070600 100644 (file)
@@ -912,5 +912,4 @@ module ApplicationHelper
     html
   end
 
-
 end
diff --git a/sonar-server/src/main/webapp/WEB-INF/app/helpers/issues_helper.rb b/sonar-server/src/main/webapp/WEB-INF/app/helpers/issues_helper.rb
new file mode 100644 (file)
index 0000000..fe629dc
--- /dev/null
@@ -0,0 +1,32 @@
+#
+# Sonar, entreprise quality control 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.
+#
+module IssuesHelper
+
+  def column_html(filter, column_label, column_tooltip, sort)
+    filter_sort = filter.criteria[:sort]
+    filter_asc = filter.criteria[:asc] == 'true' ? true : false
+    html = link_to_function(h(column_label), "refreshList('#{escape_javascript sort}',#{!filter_asc}, #{filter.criteria[:page]||1})", :title => h(column_tooltip))
+    if sort == filter_sort
+      html << (filter_asc ? image_tag("asc12.png") : image_tag("desc12.png"))
+    end
+    html
+  end
+
+end
index 32d30ee96bf512ebc4357a903f5260dd89c4612d..5bce516bb72d45eed943b3e4a7fb672465329f4e 100644 (file)
@@ -20,9 +20,6 @@
 require 'set'
 class IssueFilter
 
-  CRITERIA_SEPARATOR = '|'
-  CRITERIA_KEY_VALUE_SEPARATOR = ','
-
   attr_reader :paging, :issues, :issues_result
 
   def criteria(key=nil)
@@ -75,7 +72,9 @@ class IssueFilter
     @issues_result = nil
     @paging = nil
     @issues = nil
-    criteria['pageSize'] = 25
+    criteria['pageSize'] = 100
+    criteria['sort'] ||= 'CREATION_DATE'
+    criteria['asc'] ||= 'false'
     self
   end
 
index 3bd595a6eacaef837d3d087fd8c4a62aaf3f3e2c..f4e75cd8afe4f294b5fe7b1f1da22c2626d19ca3 100644 (file)
@@ -1,3 +1,22 @@
+<% content_for :script do %>
+  <script>
+    var filterCriteria = <%= @filter.criteria.to_json -%>;
+
+    function refreshList(sort, asc, page) {
+      $j('#issue-filter-foot_pages').hide();
+      $j('#issue-filter-foot_loading').show();
+
+      filterCriteria['sort']=sort;
+      filterCriteria['asc']=asc;
+      filterCriteria['pageIndex']=page;
+      var url=baseUrl + '/issues/search?' + $j.param(filterCriteria);
+
+      window.location = url;
+      return false;
+    }
+  </script>
+<% end %>
+
 <%
    if @filter.issues && !@filter.issues.empty?
     colspan = 8
       <thead>
         <tr>
           <th width="1%" nowrap>
-            <%= message('severity_abbreviated') -%>
+            <%= column_html(@filter, message('severity_abbreviated'), message('severity'), 'SEVERITY') %>
           </th>
           <th width="1%" nowrap>
-            <%= message('status_abbreviated') -%>
+            <%= column_html(@filter, message('status_abbreviated'), message('status'), 'STATUS') %>
           </th>
           <th>
             <%= message('description') -%>
             <%= message('component') -%>
           </th>
           <th>
-            <%= message('issue_filter.header.assignee') -%>
+            <%= column_html(@filter, message('issue_filter.header.assignee'), message('issue_filter.header.assignee'), 'ASSIGNEE') %>
           </th>
           <th>
             <%= message('issue_filter.header.action_plan') -%>
           </th>
           <th>
-            <%= message('issue_filter.header.creation_date') -%>
+            <%= column_html(@filter, message('issue_filter.header.creation_date'), message('issue_filter.header.creation_date'), 'CREATION_DATE') %>
           </th>
           <th>
-            <%= message('issue_filter.header.update_date') -%>
+            <%= column_html(@filter, message('issue_filter.header.update_date'), message('issue_filter.header.update_date'), 'UPDATE_DATE') %>
           </th>
         </tr>
       </thead>
@@ -44,7 +63,7 @@
             <img src="<%= ApplicationController.root_context -%>/images/status/<%= issue.status -%>.png" title="<%= message(issue.status.downcase).capitalize -%>"/>
           </td>
           <td>
-            <%= link_to h(issue.message), :controller => 'issue', :action => 'view', :id => issue.key -%>
+            <%= link_to h(truncate(issue.message, :length => 100)), :controller => 'issue', :action => 'view', :id => issue.key -%>
           </td>
           <td>
             <%= h @filter.issues_result.component(issue).name -%>
@@ -72,7 +91,7 @@
       <% end %>
       </tbody>
 
-      <%= paginate_java(@filter.paging, :colspan => colspan, :include_loading_icon => true) { |label, page_id|
+      <%= paginate_java(@filter.paging, :colspan => colspan, :id => 'issue-filter-foot', :include_loading_icon => true) { |label, page_id|
         link_to(label, @filter.criteria.merge({:pageIndex => page_id}))
       } -%>
 
index 21c332a60c24def064c2f34397c90b47b43d4b20..102b50aab1ff916e3fafbd21a3e879ef52e2fe7e 100644 (file)
@@ -1,10 +1,13 @@
 <ul class="sidebar gray-sidebar">
   <form method="GET" action="<%= ApplicationController.root_context -%>/issues/search" >
+
+    <input type="hidden" name="sort" value="<%= @filter.criteria('sort')-%>"/>
+    <input type="hidden" name="asc" value="<%= @filter.criteria('asc') -%>"/>
+
     <li class="sidebar-title">
       <%= message('issue_filter.new_search') -%>
     </li>
     <li id="criteria-project" class="marginbottom5">
-      <!-- TODO replace by project from issues result API-->
       <% selected_project = @project if @filter.criteria('componentRoots') %>
       <%= message 'issue_filter.criteria.project' -%>:
       <%= resource_select_tag 'componentRoots', :resource_type_property => 'supportsGlobalDashboards', :width => '100%',
index 1234b293a2299edea5cf2eb5a159a190faf56d84..ffc32f12e809caa4291be74cdfd2d52bdae31d31 100644 (file)
@@ -3,9 +3,14 @@
     <%= render :partial => 'issues/sidebar' -%>
   </div>
 
+
   <div class="page-split-right">
     <div id="content">
       <div class="marginbottom10">
+        <% if @filter.issues_result && @filter.issues_result.securityExclusions() %>
+          <p class="notes"><%= message('results_not_display_due_to_security') -%></p>
+        <% end %>
+
         <%= render :partial => 'list' -%>
       </div>
     </div>
index 0c9fdea9f4010bb116230ea8255d26e1099b9765..fd4bca4597a880fe4b7170e18ca3aa5a67dc596a 100644 (file)
@@ -53,6 +53,7 @@ import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anySet;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.*;
 
 public class DefaultIssueFinderTest {
@@ -82,7 +83,7 @@ public class DefaultIssueFinderTest {
       .setStatus("OPEN").setResolution("OPEN");
     List<IssueDto> dtoList = newArrayList(issue1, issue2);
     when(issueDao.selectIssueAndComponentIds(eq(query), any(SqlSession.class))).thenReturn(dtoList);
-    when(issueDao.selectByIds(anyCollection(), any(SqlSession.class))).thenReturn(dtoList);
+    when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(dtoList);
 
     IssueQueryResult results = finder.find(query);
     assertThat(results.issues()).hasSize(2);
@@ -107,11 +108,11 @@ public class DefaultIssueFinderTest {
     List<IssueDto> dtoList = newArrayList(issue1, issue2);
     when(issueDao.selectIssueAndComponentIds(eq(query), any(SqlSession.class))).thenReturn(dtoList);
     when(authorizationDao.keepAuthorizedComponentIds(anySet(), anyInt(), anyString(), any(SqlSession.class))).thenReturn(newHashSet(123));
-    when(issueDao.selectByIds(anyCollection(), any(SqlSession.class))).thenReturn(newArrayList(issue1));
+    when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(newArrayList(issue1));
 
     IssueQueryResult results = finder.find(query);
 
-    verify(issueDao).selectByIds(eq(newHashSet(1L)), any(SqlSession.class));
+    verify(issueDao).selectByIds(eq(newHashSet(1L)), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class));
     assertThat(results.securityExclusions()).isTrue();
   }
 
@@ -131,7 +132,7 @@ public class DefaultIssueFinderTest {
       .setStatus("OPEN").setResolution("OPEN");
     List<IssueDto> dtoList = newArrayList(issue1, issue2);
     when(issueDao.selectIssueAndComponentIds(eq(query), any(SqlSession.class))).thenReturn(dtoList);
-    when(issueDao.selectByIds(anyCollection(), any(SqlSession.class))).thenReturn(dtoList);
+    when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(dtoList);
 
     IssueQueryResult results = finder.find(query);
     assertThat(results.paging().offset()).isEqualTo(0);
@@ -139,7 +140,7 @@ public class DefaultIssueFinderTest {
     assertThat(results.paging().pages()).isEqualTo(2);
 
     // Only one result is expected because the limit is 1
-    verify(issueDao).selectByIds(eq(newHashSet(1L)), any(SqlSession.class));
+    verify(issueDao).selectByIds(eq(newHashSet(1L)), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class));
   }
 
   @Test
@@ -174,7 +175,7 @@ public class DefaultIssueFinderTest {
       .setStatus("OPEN").setResolution("OPEN");
     List<IssueDto> dtoList = newArrayList(issue1, issue2);
     when(issueDao.selectIssueAndComponentIds(eq(query), any(SqlSession.class))).thenReturn(dtoList);
-    when(issueDao.selectByIds(anyCollection(), any(SqlSession.class))).thenReturn(dtoList);
+    when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(dtoList);
 
     IssueQueryResult results = finder.find(query);
     assertThat(results.issues()).hasSize(2);
@@ -202,7 +203,7 @@ public class DefaultIssueFinderTest {
       .setStatus("OPEN").setResolution("OPEN");
     List<IssueDto> dtoList = newArrayList(issue1, issue2);
     when(issueDao.selectIssueAndComponentIds(eq(query), any(SqlSession.class))).thenReturn(dtoList);
-    when(issueDao.selectByIds(anyCollection(), any(SqlSession.class))).thenReturn(dtoList);
+    when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(dtoList);
 
     IssueQueryResult results = finder.find(query);
     assertThat(results.issues()).hasSize(2);
@@ -230,7 +231,7 @@ public class DefaultIssueFinderTest {
       .setStatus("OPEN").setResolution("OPEN");
     List<IssueDto> dtoList = newArrayList(issue1, issue2);
     when(issueDao.selectIssueAndComponentIds(eq(query), any(SqlSession.class))).thenReturn(dtoList);
-    when(issueDao.selectByIds(anyCollection(), any(SqlSession.class))).thenReturn(dtoList);
+    when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(dtoList);
     when(actionPlanService.findByKeys(anyCollection())).thenReturn(newArrayList(actionPlan1, actionPlan2));
 
     IssueQueryResult results = finder.find(query);
@@ -245,7 +246,7 @@ public class DefaultIssueFinderTest {
     grantAccessRights();
     IssueQuery query = IssueQuery.builder().build();
     when(issueDao.selectIssueAndComponentIds(eq(query), any(SqlSession.class))).thenReturn(Collections.<IssueDto>emptyList());
-    when(issueDao.selectByIds(anyCollection(), any(SqlSession.class))).thenReturn(Collections.<IssueDto>emptyList());
+    when(issueDao.selectByIds(anyCollection(), any(IssueQuery.Sort.class), anyBoolean(), any(SqlSession.class))).thenReturn(Collections.<IssueDto>emptyList());
 
 
     IssueQueryResult results = finder.find(query);
index 1267beb69ae15e1cd070007af301322bf9fda885..17c2a5262b27dc2ece84a1addfacdafeb0ba727d 100644 (file)
@@ -74,6 +74,8 @@ public class PublicRubyIssueServiceTest {
     map.put("rules", "squid:AvoidCycle,findbugs:NullReference");
     map.put("pageSize", 10l);
     map.put("pageIndex", 50);
+    map.put("sort", "creation_date");
+    map.put("asc", true);
 
     IssueQuery query = new PublicRubyIssueService(finder).toQuery(map);
     assertThat(query.issueKeys()).containsOnly("ABCDE1234");
@@ -92,6 +94,8 @@ public class PublicRubyIssueServiceTest {
     assertThat(query.createdBefore()).isEqualTo(DateUtils.parseDateTime("2013-04-17T09:08:24+0200"));
     assertThat(query.pageSize()).isEqualTo(10);
     assertThat(query.pageIndex()).isEqualTo(50);
+    assertThat(query.sort()).isEqualTo(IssueQuery.Sort.CREATION_DATE);
+    assertThat(query.asc()).isTrue();
   }
 
   @Test