summaryrefslogtreecommitdiffstats
path: root/sonar-core/src
diff options
context:
space:
mode:
authorFabrice Bellingard <bellingard@gmail.com>2012-01-12 18:30:40 +0100
committerFabrice Bellingard <bellingard@gmail.com>2012-01-12 18:40:45 +0100
commitc82a64fb8ea2dc4b031dd1bb5e766c846fdbd443 (patch)
treee431daf56f84d610736a6500ed3c4e3bf2d5abc0 /sonar-core/src
parentd9336198cba4299a3fd2f0b911c9ce06a6514009 (diff)
downloadsonarqube-c82a64fb8ea2dc4b031dd1bb5e766c846fdbd443.tar.gz
sonarqube-c82a64fb8ea2dc4b031dd1bb5e766c846fdbd443.zip
SONAR-3012 New widget to monitor the review activity
- 5 new metrics added - Decorator implemented to compute those metrics - Widget implemented to report those metrics
Diffstat (limited to 'sonar-core/src')
-rw-r--r--sonar-core/src/main/java/org/sonar/core/review/ReviewDao.java28
-rw-r--r--sonar-core/src/main/java/org/sonar/core/review/ReviewDto.java3
-rw-r--r--sonar-core/src/main/java/org/sonar/core/review/ReviewMapper.java6
-rw-r--r--sonar-core/src/main/java/org/sonar/core/review/ReviewQuery.java69
-rw-r--r--sonar-core/src/main/resources/org/sonar/core/review/ReviewMapper.xml39
-rw-r--r--sonar-core/src/test/java/org/sonar/core/review/ReviewDaoTest.java77
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/review/ReviewDaoTest/shared.xml46
7 files changed, 208 insertions, 60 deletions
diff --git a/sonar-core/src/main/java/org/sonar/core/review/ReviewDao.java b/sonar-core/src/main/java/org/sonar/core/review/ReviewDao.java
index 27ea0b80764..438bb911021 100644
--- a/sonar-core/src/main/java/org/sonar/core/review/ReviewDao.java
+++ b/sonar-core/src/main/java/org/sonar/core/review/ReviewDao.java
@@ -19,13 +19,14 @@
*/
package org.sonar.core.review;
-import com.google.common.collect.Lists;
+import java.util.List;
+
import org.apache.ibatis.session.SqlSession;
import org.sonar.api.BatchComponent;
import org.sonar.api.ServerComponent;
import org.sonar.core.persistence.MyBatis;
-import java.util.List;
+import com.google.common.collect.Lists;
public class ReviewDao implements BatchComponent, ServerComponent {
private final MyBatis mybatis;
@@ -44,29 +45,36 @@ public class ReviewDao implements BatchComponent, ServerComponent {
}
}
- public List<ReviewDto> selectByResource(int resourceId) {
+ public List<ReviewDto> selectByQuery(ReviewQuery query) {
SqlSession sqlSession = mybatis.openSession();
try {
ReviewMapper mapper = sqlSession.getMapper(ReviewMapper.class);
- return mapper.selectByResource(resourceId);
+ List<ReviewDto> result;
+ if (query.needToPartitionQuery()) {
+ result = Lists.newArrayList();
+ for (ReviewQuery partitionedQuery : query.partition()) {
+ result.addAll(mapper.selectByQuery(partitionedQuery));
+ }
+ } else {
+ result = mapper.selectByQuery(query);
+ }
+ return result;
} finally {
sqlSession.close();
}
}
- public List<ReviewDto> selectByQuery(ReviewQuery query) {
+ public Integer countByQuery(ReviewQuery query) {
SqlSession sqlSession = mybatis.openSession();
try {
ReviewMapper mapper = sqlSession.getMapper(ReviewMapper.class);
- List<ReviewDto> result;
+ Integer result = 0;
if (query.needToPartitionQuery()) {
- result = Lists.newArrayList();
for (ReviewQuery partitionedQuery : query.partition()) {
- result.addAll(mapper.selectByQuery(partitionedQuery));
+ result += mapper.countByQuery(partitionedQuery);
}
-
} else {
- result = mapper.selectByQuery(query);
+ result = mapper.countByQuery(query);
}
return result;
} finally {
diff --git a/sonar-core/src/main/java/org/sonar/core/review/ReviewDto.java b/sonar-core/src/main/java/org/sonar/core/review/ReviewDto.java
index 7f349a6b627..9fe21da9b2e 100644
--- a/sonar-core/src/main/java/org/sonar/core/review/ReviewDto.java
+++ b/sonar-core/src/main/java/org/sonar/core/review/ReviewDto.java
@@ -34,6 +34,9 @@ public final class ReviewDto {
public static final String STATUS_RESOLVED = "RESOLVED";
public static final String STATUS_CLOSED = "CLOSED";
+ public static final String RESOLUTION_FALSE_POSITIVE = "FALSE-POSITIVE";
+ public static final String RESOLUTION_FIXED = "FIXED";
+
private Long id;
private Integer userId;
private Integer assigneeId;
diff --git a/sonar-core/src/main/java/org/sonar/core/review/ReviewMapper.java b/sonar-core/src/main/java/org/sonar/core/review/ReviewMapper.java
index 89c7a6a84c5..1048128212f 100644
--- a/sonar-core/src/main/java/org/sonar/core/review/ReviewMapper.java
+++ b/sonar-core/src/main/java/org/sonar/core/review/ReviewMapper.java
@@ -19,8 +19,6 @@
*/
package org.sonar.core.review;
-import org.apache.ibatis.annotations.Param;
-
import java.util.List;
/**
@@ -29,7 +27,7 @@ import java.util.List;
public interface ReviewMapper {
ReviewDto selectById(long id);
- List<ReviewDto> selectByResource(int resourceId);
-
List<ReviewDto> selectByQuery(ReviewQuery query);
+
+ Integer countByQuery(ReviewQuery query);
}
diff --git a/sonar-core/src/main/java/org/sonar/core/review/ReviewQuery.java b/sonar-core/src/main/java/org/sonar/core/review/ReviewQuery.java
index afaa5ec369d..9481adc008e 100644
--- a/sonar-core/src/main/java/org/sonar/core/review/ReviewQuery.java
+++ b/sonar-core/src/main/java/org/sonar/core/review/ReviewQuery.java
@@ -19,12 +19,13 @@
*/
package org.sonar.core.review;
-import com.google.common.collect.Lists;
-import org.sonar.core.persistence.DatabaseUtils;
-
import java.util.Collection;
import java.util.List;
+import org.sonar.core.persistence.DatabaseUtils;
+
+import com.google.common.collect.Lists;
+
/**
* @since 2.13
*/
@@ -35,27 +36,35 @@ public final class ReviewQuery {
private Integer userId;
private List<Integer> violationPermanentIds;
private Integer ruleId;
- private String status;
- private String resolution;
+ private List<String> statuses;
+ private List<String> resolutions;
+ private Boolean noAssignee;
+ private Boolean planned;
private ReviewQuery() {
}
- private ReviewQuery(ReviewQuery other, List<Integer> permanentIds) {
+ private ReviewQuery(ReviewQuery other) {
this.manualViolation = other.manualViolation;
this.manualSeverity = other.manualSeverity;
this.resourceId = other.resourceId;
this.userId = other.userId;
- this.violationPermanentIds = permanentIds;
+ this.violationPermanentIds = other.violationPermanentIds;
this.ruleId = other.ruleId;
- this.status = other.status;
- this.resolution = other.resolution;
+ this.statuses = other.statuses;
+ this.resolutions = other.resolutions;
+ this.noAssignee = other.noAssignee;
+ this.planned = other.planned;
}
public static ReviewQuery create() {
return new ReviewQuery();
}
+ public static ReviewQuery copy(ReviewQuery reviewQuery) {
+ return new ReviewQuery(reviewQuery);
+ }
+
public Boolean getManualViolation() {
return manualViolation;
}
@@ -74,12 +83,15 @@ public final class ReviewQuery {
return this;
}
- public String getStatus() {
- return status;
+ public List<String> getStatuses() {
+ return statuses;
}
- public ReviewQuery setStatus(String status) {
- this.status = status;
+ public ReviewQuery addStatus(String status) {
+ if (statuses == null) {
+ statuses = Lists.newArrayList();
+ }
+ statuses.add(status);
return this;
}
@@ -110,12 +122,15 @@ public final class ReviewQuery {
return this;
}
- public String getResolution() {
- return resolution;
+ public List<String> getResolutions() {
+ return resolutions;
}
- public ReviewQuery setResolution(String resolution) {
- this.resolution = resolution;
+ public ReviewQuery addResolution(String resolution) {
+ if (resolutions == null) {
+ resolutions = Lists.newArrayList();
+ }
+ resolutions.add(resolution);
return this;
}
@@ -128,6 +143,24 @@ public final class ReviewQuery {
return this;
}
+ public Boolean getNoAssignee() {
+ return noAssignee;
+ }
+
+ public ReviewQuery setNoAssignee() {
+ this.noAssignee = Boolean.TRUE;
+ return this;
+ }
+
+ public Boolean getPlanned() {
+ return planned;
+ }
+
+ public ReviewQuery setPlanned() {
+ this.planned = Boolean.TRUE;
+ return this;
+ }
+
boolean needToPartitionQuery() {
return violationPermanentIds != null && violationPermanentIds.size() > DatabaseUtils.MAX_IN_ELEMENTS;
}
@@ -136,7 +169,7 @@ public final class ReviewQuery {
List<List<Integer>> partitions = Lists.partition(violationPermanentIds, DatabaseUtils.MAX_IN_ELEMENTS);
ReviewQuery[] result = new ReviewQuery[partitions.size()];
for (int index = 0; index < partitions.size(); index++) {
- result[index] = new ReviewQuery(this, partitions.get(index));
+ result[index] = ReviewQuery.copy(this).setViolationPermanentIds(partitions.get(index));
}
return result;
diff --git a/sonar-core/src/main/resources/org/sonar/core/review/ReviewMapper.xml b/sonar-core/src/main/resources/org/sonar/core/review/ReviewMapper.xml
index 2f897ff6e1c..8af600daad9 100644
--- a/sonar-core/src/main/resources/org/sonar/core/review/ReviewMapper.xml
+++ b/sonar-core/src/main/resources/org/sonar/core/review/ReviewMapper.xml
@@ -26,17 +26,10 @@
<include refid="reviewColumns"/>
from reviews where id=#{id}
</select>
-
- <select id="selectByResource" parameterType="int" resultMap="reviewResultMap">
- select
- <include refid="reviewColumns"/>
- from reviews where resource_id=#{id}
- </select>
-
- <select id="selectByQuery" parameterType="org.sonar.core.review.ReviewQuery" resultMap="reviewResultMap">
- select
- <include refid="reviewColumns"/>
+
+ <sql id="selectOrCountFromWhere">
from reviews
+ <if test="planned != null">R, action_plans_reviews APR</if>
<where>
<if test="userId != null">user_id = #{userId}</if>
<if test="violationPermanentIds != null">AND rule_failure_permanent_id in
@@ -46,11 +39,33 @@
</if>
<if test="ruleId != null">AND rule_id = #{ruleId}</if>
<if test="resourceId != null">AND resource_id = #{resourceId}</if>
- <if test="status != null">AND status = #{status}</if>
+ <if test="statuses != null">AND
+ <foreach item="status" index="index" collection="statuses"
+ open="(" separator=" OR " close=")">status = #{status}
+ </foreach>
+ </if>
<if test="manualViolation != null">AND manual_violation = #{manualViolation}</if>
<if test="manualSeverity != null">AND manual_severity = #{manualSeverity}</if>
- <if test="resolution != null">AND resolution = #{resolution}</if>
+ <if test="resolutions != null">AND
+ <foreach item="resolution" index="index" collection="resolutions"
+ open="(" separator=" OR " close=")">resolution = #{resolution}
+ </foreach>
+ </if>
+ <if test="noAssignee != null">AND assignee_id IS NULL</if>
+ <if test="planned != null">AND R.id = APR.review_id</if>
</where>
+ </sql>
+
+ <select id="selectByQuery" parameterType="org.sonar.core.review.ReviewQuery" resultMap="reviewResultMap">
+ select
+ <include refid="reviewColumns"/>
+ <include refid="selectOrCountFromWhere"/>
+ </select>
+
+
+ <select id="countByQuery" parameterType="org.sonar.core.review.ReviewQuery" resultType="Integer">
+ select count(id)
+ <include refid="selectOrCountFromWhere"/>
</select>
</mapper>
diff --git a/sonar-core/src/test/java/org/sonar/core/review/ReviewDaoTest.java b/sonar-core/src/test/java/org/sonar/core/review/ReviewDaoTest.java
index de1806a978d..3a307c0f253 100644
--- a/sonar-core/src/test/java/org/sonar/core/review/ReviewDaoTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/review/ReviewDaoTest.java
@@ -19,18 +19,22 @@
*/
package org.sonar.core.review;
-import com.google.common.collect.Lists;
+import static org.hamcrest.Matchers.anyOf;
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+
+import java.util.List;
+
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.junit.Before;
import org.junit.Test;
import org.sonar.core.persistence.DaoTestCase;
-import java.util.List;
-
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertThat;
+import com.google.common.collect.Lists;
public class ReviewDaoTest extends DaoTestCase {
@@ -48,7 +52,7 @@ public class ReviewDaoTest extends DaoTestCase {
ReviewDto reviewDto = dao.selectById(100L);
assertThat(reviewDto.getId(), is(100L));
assertThat(reviewDto.getStatus(), is("OPEN"));
- assertThat(reviewDto.getResolution(), is("RESOLVE"));
+ assertThat(reviewDto.getResolution(), is(nullValue()));
assertThat(reviewDto.getProjectId(), is(20));
assertThat(reviewDto.getViolationPermanentId(), is(1));
assertThat(reviewDto.getSeverity(), is("BLOCKER"));
@@ -66,10 +70,10 @@ public class ReviewDaoTest extends DaoTestCase {
}
@Test
- public void shouldSelectByResource() {
+ public void shouldSelectByQuery() {
setupData("shared");
- List<ReviewDto> reviewDtos = dao.selectByResource(400);
+ List<ReviewDto> reviewDtos = dao.selectByQuery(ReviewQuery.create().setResourceId(400));
assertThat(reviewDtos.size(), is(2));
for (ReviewDto reviewDto : reviewDtos) {
assertThat(reviewDto.getId(), anyOf(is(100L), is(101L)));
@@ -78,19 +82,62 @@ public class ReviewDaoTest extends DaoTestCase {
}
@Test
- public void shouldSelectByQuery() {
+ public void shouldSelectByQueryWithStatuses() {
setupData("shared");
- List<ReviewDto> reviewDtos = dao.selectByQuery(ReviewQuery.create().setResourceId(400));
+ List<ReviewDto> reviewDtos = dao.selectByQuery(ReviewQuery.create().addStatus(ReviewDto.STATUS_OPEN)
+ .addStatus(ReviewDto.STATUS_REOPENED));
+ assertThat(reviewDtos.size(), is(3));
+ for (ReviewDto reviewDto : reviewDtos) {
+ assertThat(reviewDto.getId(), anyOf(is(100L), is(102L), is(103L)));
+ }
+ }
+
+ @Test
+ public void shouldSelectByQueryWithResolutions() {
+ setupData("shared");
+
+ List<ReviewDto> reviewDtos = dao.selectByQuery(ReviewQuery.create().addResolution(ReviewDto.RESOLUTION_FALSE_POSITIVE)
+ .addResolution(ReviewDto.RESOLUTION_FIXED));
+ assertThat(reviewDtos.size(), is(2));
+ for (ReviewDto reviewDto : reviewDtos) {
+ assertThat(reviewDto.getId(), anyOf(is(101L), is(104L)));
+ }
+ }
+
+ @Test
+ public void shouldSelectByQueryWithNoAssignee() {
+ setupData("shared");
+
+ List<ReviewDto> reviewDtos = dao.selectByQuery(ReviewQuery.create().setNoAssignee());
+ assertThat(reviewDtos.size(), is(2));
+ for (ReviewDto reviewDto : reviewDtos) {
+ assertThat(reviewDto.getId(), anyOf(is(101L), is(103L)));
+ }
+ }
+
+ @Test
+ public void shouldSelectByQueryWithPlanned() {
+ setupData("shared");
+
+ List<ReviewDto> reviewDtos = dao.selectByQuery(ReviewQuery.create().setPlanned());
assertThat(reviewDtos.size(), is(2));
for (ReviewDto reviewDto : reviewDtos) {
assertThat(reviewDto.getId(), anyOf(is(100L), is(101L)));
- assertThat(reviewDto.getResourceId(), is(400));
}
}
@Test
- public void shouldSelectByQuery_booleanCriteria() {
+ public void shouldCountByQuery() {
+ setupData("shared");
+
+ Integer count = dao.countByQuery(ReviewQuery.create().addStatus(ReviewDto.STATUS_OPEN)
+ .addStatus(ReviewDto.STATUS_REOPENED));
+ assertThat(count, is(3));
+ }
+
+ @Test
+ public void shouldSelectByQueryWithBooleanCriteria() {
setupData("shared");
List<ReviewDto> reviewDtos = dao.selectByQuery(ReviewQuery.create().setResourceId(400).setManualViolation(true));
@@ -111,12 +158,16 @@ public class ReviewDaoTest extends DaoTestCase {
}
ReviewQuery query = ReviewQuery.create().setViolationPermanentIds(permanentIds);
+ // test select query
List<ReviewDto> reviewDtos = dao.selectByQuery(query);
assertThat(reviewDtos.size(), is(3));
assertThat(reviewDtos, hasItem(new ReviewMatcherByViolationPermanentId(100)));
assertThat(reviewDtos, hasItem(new ReviewMatcherByViolationPermanentId(1300)));
assertThat(reviewDtos, hasItem(new ReviewMatcherByViolationPermanentId(3200)));
+
+ // and test count query
+ assertThat(dao.countByQuery(query), is(3));
}
static class ReviewMatcherByViolationPermanentId extends BaseMatcher<ReviewDto> {
diff --git a/sonar-core/src/test/resources/org/sonar/core/review/ReviewDaoTest/shared.xml b/sonar-core/src/test/resources/org/sonar/core/review/ReviewDaoTest/shared.xml
index 70c101759de..376ff96eb8c 100644
--- a/sonar-core/src/test/resources/org/sonar/core/review/ReviewDaoTest/shared.xml
+++ b/sonar-core/src/test/resources/org/sonar/core/review/ReviewDaoTest/shared.xml
@@ -1,17 +1,21 @@
<dataset>
+ <action_plans_reviews action_plan_id="1" review_id="100"/>
+ <action_plans_reviews action_plan_id="2" review_id="101"/>
+
<!-- First resource -->
<reviews
id="100"
status="OPEN"
rule_failure_permanent_id="1"
- resolution="RESOLVE"
+ resolution="[null]"
created_at="[null]"
updated_at="[null]"
project_id="20"
resource_line="200"
severity="BLOCKER"
user_id="300"
+ assignee_id="300"
resource_id="400"
rule_id="500"
manual_violation="[true]"
@@ -21,13 +25,14 @@
id="101"
status="CLOSED"
rule_failure_permanent_id="1"
- resolution="RESOLVE"
+ resolution="FIXED"
created_at="[null]"
updated_at="[null]"
project_id="30"
resource_line="120"
severity="MAJOR"
user_id="300"
+ assignee_id="[null]"
resource_id="400"
rule_id="505"
manual_violation="[false]"
@@ -39,16 +44,51 @@
id="102"
status="OPEN"
rule_failure_permanent_id="1"
- resolution="RESOLVE"
+ resolution="[null]"
created_at="[null]"
updated_at="[null]"
project_id="20"
resource_line="200"
severity="BLOCKER"
user_id="300"
+ assignee_id="300"
resource_id="401"
rule_id="500"
manual_violation="[true]"
manual_severity="[false]"/>
+
+ <reviews
+ id="103"
+ status="REOPENED"
+ rule_failure_permanent_id="1"
+ resolution="[null]"
+ created_at="[null]"
+ updated_at="[null]"
+ project_id="20"
+ resource_line="200"
+ severity="BLOCKER"
+ user_id="300"
+ assignee_id="[null]"
+ resource_id="401"
+ rule_id="500"
+ manual_violation="[false]"
+ manual_severity="[false]"/>
+
+ <reviews
+ id="104"
+ status="RESOLVED"
+ rule_failure_permanent_id="1"
+ resolution="FALSE-POSITIVE"
+ created_at="[null]"
+ updated_at="[null]"
+ project_id="20"
+ resource_line="200"
+ severity="BLOCKER"
+ user_id="300"
+ assignee_id="300"
+ resource_id="401"
+ rule_id="500"
+ manual_violation="[false]"
+ manual_severity="[false]"/>
</dataset>