aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/unresolved_issues_per_assignee.html.erb11
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/db/IssueStatsColumn.java37
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/db/IssueStatsDao.java52
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/db/IssueStatsMapper.java33
-rw-r--r--sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java6
-rw-r--r--sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java2
-rw-r--r--sonar-core/src/main/resources/org/sonar/core/issue/db/IssueStatsMapper.xml15
-rw-r--r--sonar-core/src/test/java/org/sonar/core/issue/db/IssueStatsDaoTest.java51
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStatsDaoTest/should_select_assignees.xml80
-rw-r--r--sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueFinder.java9
-rw-r--r--sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueQueryResult.java (renamed from sonar-core/src/main/java/org/sonar/core/issue/DefaultIssueQueryResult.java)5
-rw-r--r--sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java8
-rw-r--r--sonar-server/src/main/java/org/sonar/server/issue/IssueStatsFinder.java92
-rw-r--r--sonar-server/src/main/java/org/sonar/server/issue/PublicRubyIssueService.java2
-rw-r--r--sonar-server/src/main/java/org/sonar/server/platform/Platform.java1
-rw-r--r--sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueFinderTest.java30
-rw-r--r--sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java3
-rw-r--r--sonar-server/src/test/java/org/sonar/server/issue/IssueStatsFinderTest.java54
18 files changed, 468 insertions, 23 deletions
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/unresolved_issues_per_assignee.html.erb b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/unresolved_issues_per_assignee.html.erb
index a34ac48d079..e8aa7cafb25 100644
--- a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/unresolved_issues_per_assignee.html.erb
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/unresolved_issues_per_assignee.html.erb
@@ -6,14 +6,13 @@
options['createdAfter'] = Api::Utils.format_datetime(@dashboard_configuration.from_datetime)
options['createdBefore'] = Api::Utils.format_datetime(DateTime.now)
end
- results = Api.issues.find(options)
- issues = results.issues()
+ results = Internal.issues.findIssueAssignees(options)
+ assignees = results.results
- unless issues.empty?
+ unless assignees.empty?
issues_by_assignee = {}
counter_no_assignee = 0
- issues.each do |issue|
- assignee = issue.assignee()
+ assignees.each do |assignee|
if assignee
counter = issues_by_assignee[assignee]
if counter
@@ -35,7 +34,7 @@
<h3><%= message('widget.unresolved_issues_per_assignee.name') -%></h3>
-<% if issues.size ==0 %>
+<% if assignees.size ==0 %>
<span class="empty_widget"><%= message('widget.unresolved_issues_per_assignee.name') -%></span>
<% else %>
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueStatsColumn.java b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueStatsColumn.java
new file mode 100644
index 00000000000..8e335a021a5
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueStatsColumn.java
@@ -0,0 +1,37 @@
+/*
+ * SonarQube, open source software quality management 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.
+ */
+
+package org.sonar.core.issue.db;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+public class IssueStatsColumn {
+
+ private IssueStatsColumn() {
+ // utility
+ }
+
+ public static final String ASSIGNEE = "ASSIGNEE";
+
+ public static final List<String> ALL = ImmutableList.of(ASSIGNEE);
+
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueStatsDao.java b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueStatsDao.java
new file mode 100644
index 00000000000..b5cfc3dfdc8
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueStatsDao.java
@@ -0,0 +1,52 @@
+/*
+ * SonarQube, open source software quality management 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.
+ */
+
+package org.sonar.core.issue.db;
+
+import org.apache.ibatis.session.SqlSession;
+import org.sonar.api.ServerComponent;
+import org.sonar.api.issue.IssueQuery;
+import org.sonar.core.persistence.MyBatis;
+
+import javax.annotation.Nullable;
+
+import java.util.List;
+
+/**
+ * @since 3.6
+ */
+public class IssueStatsDao implements ServerComponent {
+
+ private final MyBatis mybatis;
+
+ public IssueStatsDao(MyBatis mybatis) {
+ this.mybatis = mybatis;
+ }
+
+ public List<Object> selectIssuesColumn(IssueQuery query, String column, @Nullable Integer userId) {
+ SqlSession session = mybatis.openSession();
+ try {
+ IssueStatsMapper mapper = session.getMapper(IssueStatsMapper.class);
+ return mapper.selectIssuesColumn(query, column, userId, query.requiredRole());
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueStatsMapper.java b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueStatsMapper.java
new file mode 100644
index 00000000000..95c89e48edc
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueStatsMapper.java
@@ -0,0 +1,33 @@
+/*
+ * SonarQube, open source software quality management 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.
+ */
+
+package org.sonar.core.issue.db;
+
+import org.apache.ibatis.annotations.Param;
+import org.sonar.api.issue.IssueQuery;
+
+import java.util.List;
+
+public interface IssueStatsMapper {
+
+ List<Object> selectIssuesColumn(@Param("query") IssueQuery query, @Param("column") String column,
+ @Param("userId") Integer userId, @Param("role") String role);
+
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java b/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java
index e6ec895fb13..33fa7b7708a 100644
--- a/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java
+++ b/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java
@@ -24,10 +24,7 @@ import org.sonar.core.dashboard.ActiveDashboardDao;
import org.sonar.core.dashboard.DashboardDao;
import org.sonar.core.duplication.DuplicationDao;
import org.sonar.core.graph.jdbc.GraphDao;
-import org.sonar.core.issue.db.ActionPlanDao;
-import org.sonar.core.issue.db.ActionPlanStatsDao;
-import org.sonar.core.issue.db.IssueChangeDao;
-import org.sonar.core.issue.db.IssueDao;
+import org.sonar.core.issue.db.*;
import org.sonar.core.measure.MeasureFilterDao;
import org.sonar.core.properties.PropertiesDao;
import org.sonar.core.purge.PurgeDao;
@@ -60,6 +57,7 @@ public final class DaoUtils {
DuplicationDao.class,
GraphDao.class,
IssueDao.class,
+ IssueStatsDao.class,
IssueChangeDao.class,
LoadedTemplateDao.class,
MeasureFilterDao.class,
diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java b/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java
index 542b29a7cf7..af3619bd53c 100644
--- a/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java
+++ b/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java
@@ -120,7 +120,7 @@ public class MyBatis implements BatchComponent, ServerComponent {
loadAlias(conf, "ActionPlanStats", ActionPlanStatsDto.class);
Class<?>[] mappers = {ActiveDashboardMapper.class, AuthorMapper.class, DashboardMapper.class,
- DependencyMapper.class, DuplicationMapper.class, GraphDtoMapper.class, IssueMapper.class, IssueChangeMapper.class, LoadedTemplateMapper.class,
+ DependencyMapper.class, DuplicationMapper.class, GraphDtoMapper.class, IssueMapper.class, IssueStatsMapper.class, IssueChangeMapper.class, LoadedTemplateMapper.class,
MeasureFilterMapper.class, PropertiesMapper.class, PurgeMapper.class, ResourceKeyUpdaterMapper.class, ResourceIndexerMapper.class, ResourceMapper.class,
ResourceSnapshotMapper.class, RoleMapper.class, RuleMapper.class, SchemaMigrationMapper.class,
SemaphoreMapper.class, UserMapper.class, WidgetMapper.class, WidgetPropertyMapper.class, MeasureMapper.class, SnapshotDataMapper.class,
diff --git a/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueStatsMapper.xml b/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueStatsMapper.xml
new file mode 100644
index 00000000000..beab090bafd
--- /dev/null
+++ b/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueStatsMapper.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mappei.dtd">
+
+<mapper namespace="org.sonar.core.issue.db.IssueStatsMapper">
+
+ <select id="selectIssuesColumn" parameterType="map" resultType="Object" fetchSize="100000">
+ select
+ <if test="'ASSIGNEE'.equals(column)">
+ i.assignee
+ </if>
+ <include refid="org.sonar.core.issue.db.IssueMapper.selectQueryConditions"/>
+ </select>
+
+</mapper> \ No newline at end of file
diff --git a/sonar-core/src/test/java/org/sonar/core/issue/db/IssueStatsDaoTest.java b/sonar-core/src/test/java/org/sonar/core/issue/db/IssueStatsDaoTest.java
new file mode 100644
index 00000000000..4ce7c628f0b
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/core/issue/db/IssueStatsDaoTest.java
@@ -0,0 +1,51 @@
+/*
+ * SonarQube, open source software quality management 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.
+ */
+
+package org.sonar.core.issue.db;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.issue.IssueQuery;
+import org.sonar.core.persistence.AbstractDaoTestCase;
+
+import java.util.List;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class IssueStatsDaoTest extends AbstractDaoTestCase {
+
+ IssueStatsDao dao;
+
+ @Before
+ public void createDao() {
+ dao = new IssueStatsDao(getMyBatis());
+ }
+
+ @Test
+ public void should_select_assignees(){
+ setupData("should_select_assignees");
+
+ IssueQuery query = IssueQuery.builder().requiredRole("user").build();
+ List<Object> results = dao.selectIssuesColumn(query, IssueStatsColumn.ASSIGNEE, null);
+ assertThat(results).hasSize(3);
+ // 2 perceval, and one null
+ assertThat(results).containsOnly("perceval", null);
+ }
+}
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStatsDaoTest/should_select_assignees.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStatsDaoTest/should_select_assignees.xml
new file mode 100644
index 00000000000..313895235a4
--- /dev/null
+++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueStatsDaoTest/should_select_assignees.xml
@@ -0,0 +1,80 @@
+<dataset>
+
+ <group_roles id="1" group_id="[null]" resource_id="399" role="user"/>
+ <snapshots id="100" project_id="399" root_snapshot_id="[null]" parent_snapshot_id="[null]" root_project_id="399" path="" islast="[true]" />
+
+ <issues
+ id="100"
+ kee="ABCDE-1"
+ component_id="401"
+ root_component_id="399"
+ 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]"
+ issue_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"
+ component_id="401"
+ root_component_id="399"
+ 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="[null]"
+ author_login="[null]"
+ issue_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"
+ component_id="401"
+ root_component_id="399"
+ 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="perceval"
+ author_login="[null]"
+ issue_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-server/src/main/java/org/sonar/server/issue/DefaultIssueFinder.java b/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueFinder.java
index f7682b6693c..fcdf4e5dbd6 100644
--- a/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueFinder.java
+++ b/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueFinder.java
@@ -32,7 +32,6 @@ import org.sonar.api.user.UserFinder;
import org.sonar.api.utils.Paging;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.issue.DefaultIssueComment;
-import org.sonar.core.issue.DefaultIssueQueryResult;
import org.sonar.core.issue.db.IssueChangeDao;
import org.sonar.core.issue.db.IssueDao;
import org.sonar.core.issue.db.IssueDto;
@@ -93,6 +92,7 @@ public class DefaultIssueFinder implements IssueFinder {
return dto.toDefaultIssue();
}
+ @Override
public IssueQueryResult find(IssueQuery query) {
LOG.debug("IssueQuery : {}", query);
SqlSession sqlSession = myBatis.openSession();
@@ -142,17 +142,14 @@ public class DefaultIssueFinder implements IssueFinder {
}
}
- DefaultIssueQueryResult defaultIssueQueryResult = new DefaultIssueQueryResult();
- defaultIssueQueryResult.setMaxResultsReached(authorizedIssues.size() == query.maxResults());
- defaultIssueQueryResult.setIssues(issues)
+ return new DefaultIssueQueryResult(issues)
+ .setMaxResultsReached(authorizedIssues.size() == query.maxResults())
.addRules(findRules(ruleIds))
.addComponents(findComponents(componentIds))
.addProjects(findProjects(projectIds))
.addActionPlans(findActionPlans(actionPlanKeys))
.addUsers(findUsers(users))
.setPaging(paging);
-
- return defaultIssueQueryResult;
} finally {
MyBatis.closeQuietly(sqlSession);
}
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssueQueryResult.java b/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueQueryResult.java
index aabd51bca84..4a404362024 100644
--- a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssueQueryResult.java
+++ b/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueQueryResult.java
@@ -18,7 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-package org.sonar.core.issue;
+package org.sonar.server.issue;
import com.google.common.collect.Maps;
import org.sonar.api.component.Component;
@@ -47,9 +47,8 @@ public class DefaultIssueQueryResult implements IssueQueryResult {
private boolean maxResultsReached;
private Paging paging;
- public DefaultIssueQueryResult setIssues(List<Issue> issues){
+ public DefaultIssueQueryResult(List<Issue> issues){
this.issues = issues;
- return this;
}
public DefaultIssueQueryResult addRules(Collection<Rule> rules){
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java b/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java
index c81de2d16df..b6ee9e55fdc 100644
--- a/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java
+++ b/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java
@@ -55,18 +55,24 @@ public class InternalRubyIssueService implements ServerComponent {
private final IssueService issueService;
private final IssueCommentService commentService;
private final ActionPlanService actionPlanService;
+ private final IssueStatsFinder issueStatsFinder;
private final ResourceDao resourceDao;
public InternalRubyIssueService(IssueService issueService,
IssueCommentService commentService,
ActionPlanService actionPlanService,
- ResourceDao resourceDao) {
+ IssueStatsFinder issueStatsFinder, ResourceDao resourceDao) {
this.issueService = issueService;
this.commentService = commentService;
this.actionPlanService = actionPlanService;
+ this.issueStatsFinder = issueStatsFinder;
this.resourceDao = resourceDao;
}
+ public IssueStatsFinder.IssueStatsResult findIssueAssignees(Map<String, Object> params){
+ return issueStatsFinder.findIssueAssignees(PublicRubyIssueService.toQuery(params));
+ }
+
public List<Transition> listTransitions(String issueKey) {
return issueService.listTransitions(issueKey);
}
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/IssueStatsFinder.java b/sonar-server/src/main/java/org/sonar/server/issue/IssueStatsFinder.java
new file mode 100644
index 00000000000..a19829113f0
--- /dev/null
+++ b/sonar-server/src/main/java/org/sonar/server/issue/IssueStatsFinder.java
@@ -0,0 +1,92 @@
+/*
+ * SonarQube, open source software quality management 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.
+ */
+
+package org.sonar.server.issue;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.sonar.api.ServerComponent;
+import org.sonar.api.issue.IssueQuery;
+import org.sonar.api.user.User;
+import org.sonar.api.user.UserFinder;
+import org.sonar.core.issue.db.IssueStatsColumn;
+import org.sonar.core.issue.db.IssueStatsDao;
+import org.sonar.server.platform.UserSession;
+
+import javax.annotation.CheckForNull;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class IssueStatsFinder implements ServerComponent {
+
+ private final IssueStatsDao issuestatsDao;
+ private final UserFinder userFinder;
+
+ public IssueStatsFinder(IssueStatsDao issuestatsDao, UserFinder userFinder) {
+ this.issuestatsDao = issuestatsDao;
+ this.userFinder = userFinder;
+ }
+
+ public IssueStatsResult findIssueAssignees(IssueQuery query) {
+ List<Object> results = issuestatsDao.selectIssuesColumn(query, IssueStatsColumn.ASSIGNEE, UserSession.get().userId());
+
+ Set<String> users = Sets.newHashSet();
+ for (Object result : results) {
+ if (result != null) {
+ users.add((String) result);
+ }
+ }
+
+ return new IssueStatsResult(results).addUsers(findUsers(users));
+ }
+
+ private Collection<User> findUsers(Set<String> logins) {
+ return userFinder.findByLogins(Lists.newArrayList(logins));
+ }
+
+ public static class IssueStatsResult {
+ private final Map<String, User> usersByLogin = Maps.newHashMap();
+ private List<Object> results;
+
+ public IssueStatsResult(List<Object> results) {
+ this.results = results;
+ }
+
+ public IssueStatsResult addUsers(Collection<User> users) {
+ for (User user : users) {
+ usersByLogin.put(user.login(), user);
+ }
+ return this;
+ }
+
+ public List<Object> results() {
+ return results;
+ }
+
+ @CheckForNull
+ public User user(String login) {
+ return usersByLogin.get(login);
+ }
+ }
+}
diff --git a/sonar-server/src/main/java/org/sonar/server/issue/PublicRubyIssueService.java b/sonar-server/src/main/java/org/sonar/server/issue/PublicRubyIssueService.java
index 0b8909dd1d0..1832775006f 100644
--- a/sonar-server/src/main/java/org/sonar/server/issue/PublicRubyIssueService.java
+++ b/sonar-server/src/main/java/org/sonar/server/issue/PublicRubyIssueService.java
@@ -63,7 +63,7 @@ public class PublicRubyIssueService implements RubyIssueService {
return finder.find(toQuery(params));
}
- IssueQuery toQuery(Map<String, Object> props) {
+ static IssueQuery toQuery(Map<String, Object> props) {
IssueQuery.Builder builder = IssueQuery.builder();
builder.requiredRole(UserRole.CODEVIEWER);
builder.issueKeys(RubyUtils.toStrings(props.get("issues")));
diff --git a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java
index a1605e29579..0691c29d5f2 100644
--- a/sonar-server/src/main/java/org/sonar/server/platform/Platform.java
+++ b/sonar-server/src/main/java/org/sonar/server/platform/Platform.java
@@ -263,6 +263,7 @@ public final class Platform {
servicesContainer.addSingleton(IssueService.class);
servicesContainer.addSingleton(IssueCommentService.class);
servicesContainer.addSingleton(DefaultIssueFinder.class);
+ servicesContainer.addSingleton(IssueStatsFinder.class);
servicesContainer.addSingleton(PublicRubyIssueService.class);
servicesContainer.addSingleton(InternalRubyIssueService.class);
servicesContainer.addSingleton(ActionPlanService.class);
diff --git a/sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueFinderTest.java b/sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueFinderTest.java
index d3419a8082e..0123c9f1083 100644
--- a/sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueFinderTest.java
+++ b/sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueFinderTest.java
@@ -19,6 +19,7 @@
*/
package org.sonar.server.issue;
+import com.google.common.collect.Lists;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import org.sonar.api.component.Component;
@@ -27,6 +28,7 @@ import org.sonar.api.issue.Issue;
import org.sonar.api.issue.IssueQuery;
import org.sonar.api.issue.IssueQueryResult;
import org.sonar.api.rules.Rule;
+import org.sonar.api.user.User;
import org.sonar.api.user.UserFinder;
import org.sonar.core.component.ComponentDto;
import org.sonar.core.issue.DefaultActionPlan;
@@ -37,6 +39,7 @@ import org.sonar.core.persistence.MyBatis;
import org.sonar.core.resource.ResourceDao;
import org.sonar.core.rule.DefaultRuleFinder;
import org.sonar.core.user.AuthorizationDao;
+import org.sonar.core.user.DefaultUser;
import java.util.Collections;
import java.util.List;
@@ -48,6 +51,7 @@ import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyCollection;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.anyListOf;
import static org.mockito.Mockito.*;
public class DefaultIssueFinderTest {
@@ -242,6 +246,32 @@ public class DefaultIssueFinderTest {
}
@Test
+ public void should_get_user_from_result() {
+ when(userFinder.findByLogins(anyListOf(String.class))).thenReturn(Lists.<User>newArrayList(
+ new DefaultUser().setLogin("perceval").setName("Perceval"),
+ new DefaultUser().setLogin("arthur").setName("Roi Arthur")
+ ));
+
+ IssueQuery query = IssueQuery.builder().build();
+
+ IssueDto issue1 = new IssueDto().setId(1L).setRuleId(50).setComponentId(123).setRootComponentId(100).setKee("ABC").setAssignee("perceval")
+ .setRuleKey_unit_test_only("squid", "AvoidCycle")
+ .setStatus("OPEN").setResolution("OPEN");
+ IssueDto issue2 = new IssueDto().setId(2L).setRuleId(50).setComponentId(123).setRootComponentId(100).setKee("DEF").setReporter("arthur")
+ .setRuleKey_unit_test_only("squid", "AvoidCycle")
+ .setStatus("OPEN").setResolution("OPEN");
+ List<IssueDto> dtoList = newArrayList(issue1, issue2);
+ when(issueDao.selectByIds(anyCollection(), any(SqlSession.class))).thenReturn(dtoList);
+
+ IssueQueryResult results = finder.find(query);
+ assertThat(results.issues()).hasSize(2);
+
+ assertThat(results.users()).hasSize(2);
+ assertThat(results.user("perceval").name()).isEqualTo("Perceval");
+ assertThat(results.user("arthur").name()).isEqualTo("Roi Arthur");
+ }
+
+ @Test
public void should_get_empty_result_when_no_issue() {
IssueQuery query = IssueQuery.builder().build();
when(issueDao.selectIssues(eq(query), anyInt(), any(SqlSession.class))).thenReturn(Collections.<IssueDto>emptyList());
diff --git a/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java b/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java
index 24e5ebabe0c..f49177d8989 100644
--- a/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java
+++ b/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java
@@ -43,12 +43,13 @@ public class InternalRubyIssueServiceTest {
private IssueCommentService commentService = mock(IssueCommentService.class);
private ActionPlanService actionPlanService = mock(ActionPlanService.class);
private ResourceDao resourceDao = mock(ResourceDao.class);
+ private IssueStatsFinder issueStatsFinder = mock(IssueStatsFinder.class);
@Before
public void before() {
ResourceDto project = new ResourceDto().setKey("org.sonar.Sample");
when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(project);
- internalRubyIssueService = new InternalRubyIssueService(issueService, commentService, actionPlanService, resourceDao);
+ internalRubyIssueService = new InternalRubyIssueService(issueService, commentService, actionPlanService, issueStatsFinder, resourceDao);
}
@Test
diff --git a/sonar-server/src/test/java/org/sonar/server/issue/IssueStatsFinderTest.java b/sonar-server/src/test/java/org/sonar/server/issue/IssueStatsFinderTest.java
new file mode 100644
index 00000000000..15497ff2c2a
--- /dev/null
+++ b/sonar-server/src/test/java/org/sonar/server/issue/IssueStatsFinderTest.java
@@ -0,0 +1,54 @@
+/*
+ * SonarQube, open source software quality management 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.
+ */
+
+package org.sonar.server.issue;
+
+import com.google.common.collect.Lists;
+import org.junit.Test;
+import org.sonar.api.issue.IssueQuery;
+import org.sonar.api.user.User;
+import org.sonar.api.user.UserFinder;
+import org.sonar.core.issue.db.IssueStatsDao;
+import org.sonar.core.user.DefaultUser;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class IssueStatsFinderTest {
+
+ private IssueStatsDao issuestatsDao = mock(IssueStatsDao.class);
+ private UserFinder userFinder = mock(UserFinder.class);
+
+ @Test
+ public void should_find_assignees(){
+ when(issuestatsDao.selectIssuesColumn(any(IssueQuery.class), anyString(), anyInt())).thenReturn(Lists.<Object>newArrayList("perceval", "perceval", "arthur", null));
+ when(userFinder.findByLogins(anyListOf(String.class))).thenReturn(Lists.<User>newArrayList(
+ new DefaultUser().setLogin("perceval").setName("Perceval"),
+ new DefaultUser().setLogin("arthur").setName("Roi Arthur")
+ ));
+
+ IssueStatsFinder issueStatsFinder = new IssueStatsFinder(issuestatsDao, userFinder);
+ IssueStatsFinder.IssueStatsResult issueStatsResult = issueStatsFinder.findIssueAssignees(IssueQuery.builder().build());
+ assertThat(issueStatsResult.results()).hasSize(4);
+ assertThat(issueStatsResult.user("arthur").name()).isEqualTo("Roi Arthur");
+ }
+}