diff options
author | Julien Lancelot <julien.lancelot@gmail.com> | 2013-05-27 16:44:07 +0200 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@gmail.com> | 2013-05-27 16:44:07 +0200 |
commit | 97090aad19a494b36f7fc514f7eb60405bc6156f (patch) | |
tree | 79612e1dd982dd7a02d48071e29e98cbed649acf /sonar-server | |
parent | 3e4d1ad986e546bf6699ce12555218ac09f546ee (diff) | |
download | sonarqube-97090aad19a494b36f7fc514f7eb60405bc6156f.tar.gz sonarqube-97090aad19a494b36f7fc514f7eb60405bc6156f.zip |
SONAR-4292 Create a IssueStatsFinder to be used by UnresolvedIssuesPerAssigneeWidget
Diffstat (limited to 'sonar-server')
9 files changed, 356 insertions, 9 deletions
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-server/src/main/java/org/sonar/server/issue/DefaultIssueQueryResult.java b/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueQueryResult.java new file mode 100644 index 00000000000..4a404362024 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueQueryResult.java @@ -0,0 +1,166 @@ +/* + * 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.Maps; +import org.sonar.api.component.Component; +import org.sonar.api.issue.ActionPlan; +import org.sonar.api.issue.Issue; +import org.sonar.api.issue.IssueQueryResult; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rules.Rule; +import org.sonar.api.user.User; +import org.sonar.api.utils.Paging; + +import javax.annotation.CheckForNull; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +public class DefaultIssueQueryResult implements IssueQueryResult { + + private List<Issue> issues; + private final Map<RuleKey, Rule> rulesByKey = Maps.newHashMap(); + private final Map<String, Component> componentsByKey = Maps.newHashMap(); + private final Map<String, Component> projectsByKey = Maps.newHashMap(); + private final Map<String, ActionPlan> actionPlansByKey = Maps.newHashMap(); + private final Map<String, User> usersByLogin = Maps.newHashMap(); + private boolean maxResultsReached; + private Paging paging; + + public DefaultIssueQueryResult(List<Issue> issues){ + this.issues = issues; + } + + public DefaultIssueQueryResult addRules(Collection<Rule> rules){ + for (Rule rule : rules) { + rulesByKey.put(rule.ruleKey(), rule); + } + return this; + } + + public DefaultIssueQueryResult addComponents(Collection<Component> components){ + for (Component component : components) { + componentsByKey.put(component.key(), component); + } + return this; + } + + public DefaultIssueQueryResult addProjects(Collection<Component> projects){ + for (Component project : projects) { + projectsByKey.put(project.key(), project); + } + return this; + } + + public DefaultIssueQueryResult addActionPlans(Collection<ActionPlan> actionPlans){ + for (ActionPlan actionPlan : actionPlans) { + actionPlansByKey.put(actionPlan.key(), actionPlan); + } + return this; + } + + public DefaultIssueQueryResult addUsers(Collection<User> users){ + for (User user : users) { + usersByLogin.put(user.login(), user); + } + return this; + } + + public DefaultIssueQueryResult setMaxResultsReached(boolean maxResultsReached){ + this.maxResultsReached = maxResultsReached; + return this; + } + + public DefaultIssueQueryResult setPaging(Paging paging){ + this.paging = paging; + return this; + } + + @Override + public List<Issue> issues() { + return issues; + } + + @Override + public Rule rule(Issue issue) { + return rulesByKey.get(issue.ruleKey()); + } + + @Override + public Collection<Rule> rules() { + return rulesByKey.values(); + } + + @Override + public Component component(Issue issue) { + return componentsByKey.get(issue.componentKey()); + } + + @Override + public Collection<Component> components() { + return componentsByKey.values(); + } + + @Override + public Component project(Issue issue) { + return projectsByKey.get(issue.projectKey()); + } + + @Override + public Collection<Component> projects() { + return projectsByKey.values(); + } + + @Override + public ActionPlan actionPlan(Issue issue) { + return actionPlansByKey.get(issue.actionPlanKey()); + } + + @Override + public Collection<ActionPlan> actionPlans() { + return actionPlansByKey.values(); + } + + @Override + public Collection<User> users() { + return usersByLogin.values(); + } + + @Override + @CheckForNull + public User user(String login) { + return usersByLogin.get(login); + } + + @Override + public boolean maxResultsReached() { + return maxResultsReached; + } + + @Override + public Paging paging() { + return paging; + } + + +} 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"); + } +} |