From c7e2e99344f9ac49cd664a85193a260bdc37599e Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Wed, 1 Oct 2014 14:11:16 +0200 Subject: [PATCH] SONAR-5603 Get issues from E/S in Unresolved Issues per Assignee widget Conflicts: server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java --- .../UnresolvedIssuesPerAssigneeWidget.java | 2 +- .../unresolved_issues_per_assignee.html.erb | 47 +++++----- .../issue/InternalRubyIssueService.java | 8 +- .../org/sonar/server/issue/IssueService.java | 31 ++++-- .../sonar/server/issue/IssueStatsFinder.java | 94 ------------------- .../issue/actionplan/ActionPlanService.java | 1 + .../sonar/server/issue/index/IssueIndex.java | 34 +++++++ .../server/platform/ServerComponents.java | 1 - .../issue/InternalRubyIssueServiceTest.java | 7 +- .../server/issue/IssueServiceMediumTest.java | 22 +++++ .../server/issue/IssueStatsFinderTest.java | 53 ----------- .../issue/index/IssueIndexMediumTest.java | 30 +++++- .../sonar/core/issue/db/IssueStatsColumn.java | 37 -------- .../sonar/core/issue/db/IssueStatsDao.java | 52 ---------- .../sonar/core/issue/db/IssueStatsMapper.java | 36 ------- .../org/sonar/core/persistence/DaoUtils.java | 15 +-- .../org/sonar/core/persistence/MyBatis.java | 2 +- .../sonar/core/issue/db/IssueStatsMapper.xml | 16 ---- .../core/issue/db/IssueStatsDaoTest.java | 51 ---------- 19 files changed, 134 insertions(+), 405 deletions(-) delete mode 100644 server/sonar-server/src/main/java/org/sonar/server/issue/IssueStatsFinder.java delete mode 100644 server/sonar-server/src/test/java/org/sonar/server/issue/IssueStatsFinderTest.java delete mode 100644 sonar-core/src/main/java/org/sonar/core/issue/db/IssueStatsColumn.java delete mode 100644 sonar-core/src/main/java/org/sonar/core/issue/db/IssueStatsDao.java delete mode 100644 sonar-core/src/main/java/org/sonar/core/issue/db/IssueStatsMapper.java delete mode 100644 sonar-core/src/main/resources/org/sonar/core/issue/db/IssueStatsMapper.xml delete mode 100644 sonar-core/src/test/java/org/sonar/core/issue/db/IssueStatsDaoTest.java diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/issues/UnresolvedIssuesPerAssigneeWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/issues/UnresolvedIssuesPerAssigneeWidget.java index a8f8e8d41e6..2067c798b99 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/issues/UnresolvedIssuesPerAssigneeWidget.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/issues/UnresolvedIssuesPerAssigneeWidget.java @@ -25,6 +25,6 @@ import org.sonar.plugins.core.widgets.CoreWidget; @WidgetCategory({"Issues"}) public class UnresolvedIssuesPerAssigneeWidget extends CoreWidget { public UnresolvedIssuesPerAssigneeWidget() { - super("reviews_per_developer", "Unresolved issues per assignee", "/org/sonar/plugins/core/widgets/issues/unresolved_issues_per_assignee.html.erb"); + super("reviews_per_developer", "Unresolved issues per assignee", "/Users/julienlancelot/Dev/Sources/sonarqube/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/unresolved_issues_per_assignee.html.erb"); } } 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 5e1cadae3d9..fec0255334f 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 @@ -1,4 +1,4 @@ -<% +<% options = {} options['resolved'] = 'false' options['componentRoots'] = @project.key @@ -6,35 +6,29 @@ options['createdAfter'] = Api::Utils.format_datetime(@dashboard_configuration.from_datetime) options['createdBefore'] = Api::Utils.format_datetime(DateTime.now) end - results = Internal.issues.findIssueAssignees(options) - assignees = results.results + issues_by_assignee = Internal.issues.findIssueAssignees(options) - unless assignees.empty? - issues_by_assignee = {} - counter_no_assignee = 0 - assignees.each do |assignee| - if assignee - counter = issues_by_assignee[assignee] - if counter - issues_by_assignee[assignee] = counter+1 - else - issues_by_assignee[assignee] = 1 - end - else - counter_no_assignee += 1 - end + unless issues_by_assignee.empty? + users = Internal.users_api.find({'logins', issues_by_assignee.keySet()}) + users_by_login = {} + users.each do |u| + users_by_login[u.login()] = u end + + # puts "### " + users_by_login.inspect + + counter_no_assignee = issues_by_assignee.get(nil) counter_max = 0 - counter_max = issues_by_assignee.values.max unless issues_by_assignee.empty? + counter_max = issues_by_assignee.values().max unless issues_by_assignee.empty? counter_max = counter_no_assignee if counter_no_assignee > counter_max - + div_id = "unresolved-issues-per-assignee-widget-#{widget.id.to_s}" end %>

<%= message('widget.reviews_per_developer.name') -%>

-<% if assignees.size ==0 %> +<% if issues_by_assignee.size ==0 %> <%= message('issue_filter.no_issues') -%> <% else %> @@ -49,11 +43,12 @@ <% - issues_by_assignee.sort{|h1,h2| h2[1] <=> h1[1]}.each do |assignee, count| + issues_by_assignee.each do |assignee, count| + if assignee %> - <%= link_to results.user(assignee).name, url_for_issues(options.merge(:assignees => assignee)) -%> + <%= link_to users_by_login[assignee].name(), url_for_issues(options.merge(:assignees => assignee)) -%> <%= '+' if @dashboard_configuration.selected_period? -%><%= count.to_s -%> @@ -65,9 +60,8 @@ <% end %> <% - end - # And now show the unassigned issues counter - unless counter_no_assignee==0 + # And now show the unassigned issues counter + else %> @@ -82,7 +76,8 @@ <% end %> - <% end %> + <% end + end %> diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java index 271849db29c..3934e2ffae1 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java @@ -86,7 +86,6 @@ public class InternalRubyIssueService implements ServerComponent { private final IssueCommentService commentService; private final IssueChangelogService changelogService; private final ActionPlanService actionPlanService; - private final IssueStatsFinder issueStatsFinder; private final ResourceDao resourceDao; private final ActionService actionService; private final IssueFilterService issueFilterService; @@ -96,13 +95,12 @@ public class InternalRubyIssueService implements ServerComponent { IssueService issueService, IssueCommentService commentService, IssueChangelogService changelogService, ActionPlanService actionPlanService, - IssueStatsFinder issueStatsFinder, ResourceDao resourceDao, ActionService actionService, + ResourceDao resourceDao, ActionService actionService, IssueFilterService issueFilterService, IssueBulkChangeService issueBulkChangeService) { this.issueService = issueService; this.commentService = commentService; this.changelogService = changelogService; this.actionPlanService = actionPlanService; - this.issueStatsFinder = issueStatsFinder; this.resourceDao = resourceDao; this.actionService = actionService; this.issueFilterService = issueFilterService; @@ -113,8 +111,8 @@ public class InternalRubyIssueService implements ServerComponent { return issueService.getByKey(issueKey); } - public IssueStatsFinder.IssueStatsResult findIssueAssignees(Map params) { - return issueStatsFinder.findIssueAssignees(toQuery(params)); + public Map findIssueAssignees(Map params) { + return issueService.findIssueAssignees(toQuery(params)); } public List listTransitions(String issueKey) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java index 94e9fc9f8fc..a281c14f5fa 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java @@ -52,16 +52,16 @@ import org.sonar.server.db.DbClient; import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.issue.actionplan.ActionPlanService; import org.sonar.server.issue.index.IssueIndex; +import org.sonar.server.search.FacetValue; import org.sonar.server.search.IndexClient; import org.sonar.server.search.QueryContext; import org.sonar.server.user.UserSession; import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.List; +import java.util.*; + +import static com.google.common.collect.Maps.newLinkedHashMap; public class IssueService implements ServerComponent { @@ -74,7 +74,7 @@ public class IssueService implements ServerComponent { private final IssueNotifications issueNotifications; private final ActionPlanService actionPlanService; private final RuleFinder ruleFinder; - private final IssueDao issueDao; + private final IssueDao deprecatedIssueDao; private final UserFinder userFinder; private final PreviewCache dryRunCache; @@ -85,7 +85,7 @@ public class IssueService implements ServerComponent { IssueNotifications issueNotifications, ActionPlanService actionPlanService, RuleFinder ruleFinder, - IssueDao issueDao, + IssueDao deprecatedIssueDao, UserFinder userFinder, PreviewCache dryRunCache) { this.dbClient = dbClient; @@ -96,7 +96,7 @@ public class IssueService implements ServerComponent { this.actionPlanService = actionPlanService; this.ruleFinder = ruleFinder; this.issueNotifications = issueNotifications; - this.issueDao = issueDao; + this.deprecatedIssueDao = deprecatedIssueDao; this.userFinder = userFinder; this.dryRunCache = dryRunCache; } @@ -274,7 +274,7 @@ public class IssueService implements ServerComponent { // TODO result should be replaced by an aggregation object in IssueIndex public RulesAggregation findRulesByComponent(String componentKey, @Nullable Date periodDate, DbSession session) { RulesAggregation rulesAggregation = new RulesAggregation(); - for (RuleDto ruleDto : issueDao.findRulesByComponent(componentKey, periodDate, session)) { + for (RuleDto ruleDto : deprecatedIssueDao.findRulesByComponent(componentKey, periodDate, session)) { rulesAggregation.add(ruleDto); } return rulesAggregation; @@ -283,12 +283,25 @@ public class IssueService implements ServerComponent { // TODO result should be replaced by an aggregation object in IssueIndex public Multiset findSeveritiesByComponent(String componentKey, @Nullable Date periodDate, DbSession session) { Multiset aggregation = HashMultiset.create(); - for (String severity : issueDao.findSeveritiesByComponent(componentKey, periodDate, session)) { + for (String severity : deprecatedIssueDao.findSeveritiesByComponent(componentKey, periodDate, session)) { aggregation.add(severity); } return aggregation; } + public Map findIssueAssignees(IssueQuery query) { + Map result = newLinkedHashMap(); + List facetValues = indexClient.get(IssueIndex.class).listAssignees(query); + for (FacetValue facetValue : facetValues) { + if (facetValue.getKey().equals("_notAssigned_")) { + result.put(null, facetValue.getValue()); + } else { + result.put(facetValue.getKey(), facetValue.getValue()); + } + } + return result; + } + public Issue getByKey(String key) { return indexClient.get(IssueIndex.class).getByKey(key); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueStatsFinder.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueStatsFinder.java deleted file mode 100644 index 0eec6f684ce..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueStatsFinder.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 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.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.user.UserSession; - -import javax.annotation.CheckForNull; - -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * @since 3.6 - */ -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 results = issuestatsDao.selectIssuesColumn(query, IssueStatsColumn.ASSIGNEE, UserSession.get().userId()); - - Set users = Sets.newHashSet(); - for (Object result : results) { - if (result != null) { - users.add((String) result); - } - } - - return new IssueStatsResult(results).addUsers(findUsers(users)); - } - - private Collection findUsers(Set logins) { - return userFinder.findByLogins(Lists.newArrayList(logins)); - } - - public static class IssueStatsResult { - private final Map usersByLogin = Maps.newHashMap(); - private List results; - - public IssueStatsResult(List results) { - this.results = results; - } - - public IssueStatsResult addUsers(Collection users) { - for (User user : users) { - usersByLogin.put(user.login(), user); - } - return this; - } - - public List results() { - return results; - } - - @CheckForNull - public User user(String login) { - return usersByLogin.get(login); - } - } -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/actionplan/ActionPlanService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/actionplan/ActionPlanService.java index a85a7dcb37b..e25c14ea3c5 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/actionplan/ActionPlanService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/actionplan/ActionPlanService.java @@ -94,6 +94,7 @@ public class ActionPlanService implements ServerComponent { private void unplanIssues(DefaultActionPlan actionPlan, UserSession userSession) { // Get all issues linked to this plan (need to disable pagination and authorization check) IssueQuery query = IssueQuery.builder().actionPlans(Arrays.asList(actionPlan.key())).requiredRole(null).build(); + // TODO use IssueService List dtos = issueDao.selectIssues(query); IssueChangeContext context = IssueChangeContext.createUser(new Date(), userSession.login()); List issues = newArrayList(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java index 5fb9a69306d..33e60a89981 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java @@ -28,6 +28,8 @@ import org.elasticsearch.action.search.SearchType; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.index.query.*; import org.elasticsearch.search.aggregations.AggregationBuilders; +import org.elasticsearch.search.aggregations.bucket.missing.InternalMissing; +import org.elasticsearch.search.aggregations.bucket.terms.Terms; import org.elasticsearch.search.sort.FieldSortBuilder; import org.elasticsearch.search.sort.SortBuilders; import org.elasticsearch.search.sort.SortOrder; @@ -114,6 +116,38 @@ public class IssueIndex extends BaseIndex { return null; } + public List listAssignees(IssueQuery query) { + QueryContext queryContext = new QueryContext().setPage(1, 0); + + SearchRequestBuilder esSearch = getClient() + .prepareSearch(this.getIndexName()) + .setTypes(this.getIndexType()) + .setIndices(this.getIndexName()); + + QueryBuilder esQuery = QueryBuilders.matchAllQuery(); + BoolFilterBuilder esFilter = getFilter(query, queryContext); + if (esFilter.hasClauses()) { + esSearch.setQuery(QueryBuilders.filteredQuery(esQuery, esFilter)); + } else { + esSearch.setQuery(esQuery); + } + esSearch.addAggregation(AggregationBuilders.terms(IssueNormalizer.IssueField.ASSIGNEE.field()) + .size(Integer.MAX_VALUE) + .field(IssueNormalizer.IssueField.ASSIGNEE.field())); + esSearch.addAggregation(AggregationBuilders.missing("notAssigned") + .field(IssueNormalizer.IssueField.ASSIGNEE.field())); + + SearchResponse response = getClient().execute(esSearch); + Terms aggregation = (Terms) response.getAggregations().getAsMap().get(IssueNormalizer.IssueField.ASSIGNEE.field()); + List facetValues = newArrayList(); + for (Terms.Bucket value : aggregation.getBuckets()) { + facetValues.add(new FacetValue(value.getKey(), (int) value.getDocCount()).setSort(FacetValue.Sort.BY_VALUE)); + } + facetValues.add(new FacetValue("_notAssigned_", (int) ((InternalMissing) response.getAggregations().get("notAssigned")).getDocCount())); + + return facetValues; + } + public Result search(IssueQuery query, QueryContext options) { SearchRequestBuilder esSearch = getClient() .prepareSearch(this.getIndexName()) diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java index 085a8582f78..a765263734e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java @@ -488,7 +488,6 @@ class ServerComponents { pico.addSingleton(FunctionExecutor.class); pico.addSingleton(IssueWorkflow.class); pico.addSingleton(IssueCommentService.class); - pico.addSingleton(IssueStatsFinder.class); pico.addSingleton(InternalRubyIssueService.class); pico.addSingleton(IssueChangelogService.class); pico.addSingleton(IssueNotifications.class); diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java index 049e76e0b93..beb3c74216d 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java @@ -79,9 +79,6 @@ public class InternalRubyIssueServiceTest { @Mock ResourceDao resourceDao; - @Mock - IssueStatsFinder issueStatsFinder; - @Mock ActionService actionService; @@ -97,7 +94,7 @@ public class InternalRubyIssueServiceTest { public void setUp() { ResourceDto project = new ResourceDto().setKey("org.sonar.Sample"); when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(project); - service = new InternalRubyIssueService(issueService, commentService, changelogService, actionPlanService, issueStatsFinder, resourceDao, actionService, + service = new InternalRubyIssueService(issueService, commentService, changelogService, actionPlanService, resourceDao, actionService, issueFilterService, issueBulkChangeService); } @@ -110,7 +107,7 @@ public class InternalRubyIssueServiceTest { @Test public void find_issue_assignees() throws Exception { service.findIssueAssignees(ImmutableMap.of("issues", "ABCD")); - verify(issueStatsFinder).findIssueAssignees(any(IssueQuery.class)); + verify(issueService).findIssueAssignees(any(IssueQuery.class)); } @Test diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java index 10e667292ac..c2fe8dddf1d 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java @@ -58,6 +58,7 @@ import org.sonar.server.user.MockUserSession; import java.util.Date; import java.util.List; +import java.util.Map; import java.util.UUID; import static org.fest.assertions.Assertions.assertThat; @@ -391,6 +392,27 @@ public class IssueServiceMediumTest { assertThat(result).hasSize(1); } + @Test + public void find_issue_assignees() throws Exception { + db.issueDao().insert(session, + IssueTesting.newDto(rule, file, project).setAssignee("steph"), + IssueTesting.newDto(rule, file, project).setAssignee("simon"), + IssueTesting.newDto(rule, file, project), + IssueTesting.newDto(rule, file, project).setAssignee("steph")); + session.commit(); + + Map results = service.findIssueAssignees(IssueQuery.builder().build()); + + assertThat(results).hasSize(3); + assertThat(results.get("steph")).isEqualTo(2); + assertThat(results.get("simon")).isEqualTo(1); + assertThat(results.get(null)).isEqualTo(1); + + assertThat(results.keySet().toArray()[0]).isEqualTo("steph"); + assertThat(results.keySet().toArray()[1]).isEqualTo("simon"); + assertThat(results.keySet().toArray()[2]).isNull(); + } + private IssueDto newIssue() { return new IssueDto() .setIssueCreationDate(DateUtils.parseDate("2014-09-04")) diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueStatsFinderTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueStatsFinderTest.java deleted file mode 100644 index d52209ecaec..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueStatsFinderTest.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 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.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.newArrayList("perceval", "perceval", "arthur", null)); - when(userFinder.findByLogins(anyListOf(String.class))).thenReturn(Lists.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"); - } -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java index 0901f951c28..00ac616f8b3 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java @@ -50,10 +50,10 @@ import org.sonar.server.issue.db.IssueDao; import org.sonar.server.platform.BackendCleanup; import org.sonar.server.rule.RuleTesting; import org.sonar.server.rule.db.RuleDao; +import org.sonar.server.search.FacetValue; import org.sonar.server.search.IndexDefinition; import org.sonar.server.search.QueryContext; import org.sonar.server.search.Result; -import org.sonar.server.search.SearchClient; import org.sonar.server.tester.ServerTester; import org.sonar.server.user.MockUserSession; @@ -644,7 +644,6 @@ public class IssueIndexMediumTest { List issueKeys = newArrayList(); for (int i = 0; i < numberOfIssues; i++) { IssueDto issue = IssueTesting.newDto(rule, resource, project); - issue.setIssueAttributes("attr" + i); tester.get(IssueDao.class).insert(session, issue); issueKeys.add(issue.getKey()); } @@ -669,9 +668,30 @@ public class IssueIndexMediumTest { } assertThat(index.countAll()).isEqualTo(numberOfIssues); + } + + @Test + public void list_assignees() throws Exception { + db.issueDao().insert(session, + IssueTesting.newDto(rule, file, project).setAssignee("steph").setStatus(Issue.STATUS_OPEN), + IssueTesting.newDto(rule, file, project).setAssignee("simon").setStatus(Issue.STATUS_OPEN), + IssueTesting.newDto(rule, file, project).setStatus(Issue.STATUS_OPEN), + IssueTesting.newDto(rule, file, project).setAssignee("steph").setStatus(Issue.STATUS_OPEN), + // julien should not be returned as the issue is closed + IssueTesting.newDto(rule, file, project).setAssignee("julien").setStatus(Issue.STATUS_CLOSED) + ); + session.commit(); + + List results = index.listAssignees(IssueQuery.builder().statuses(newArrayList(Issue.STATUS_OPEN)).build()); + + assertThat(results).hasSize(3); + assertThat(results.get(0).getKey()).isEqualTo("steph"); + assertThat(results.get(0).getValue()).isEqualTo(2); + + assertThat(results.get(1).getKey()).isEqualTo("simon"); + assertThat(results.get(1).getValue()).isEqualTo(1); - SearchClient elastic = tester.get(SearchClient.class); - IndicesStatsResponse stats = elastic.admin().indices().prepareStats(IndexDefinition.ISSUES.getIndexName()).get(); - System.out.println("stats = " + stats); + assertThat(results.get(2).getKey()).isEqualTo("_notAssigned_"); + assertThat(results.get(2).getValue()).isEqualTo(1); } } 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 deleted file mode 100644 index 50a85b40ab3..00000000000 --- a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueStatsColumn.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 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 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 deleted file mode 100644 index 81db0758bf7..00000000000 --- a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueStatsDao.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 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.core.persistence.MyBatis; -import org.sonar.server.issue.IssueQuery; - -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 selectIssuesColumn(IssueQuery query, String column, @Nullable Integer userId) { - SqlSession session = mybatis.openSession(false); - try { - IssueStatsMapper mapper = session.getMapper(IssueStatsMapper.class); - return mapper.selectIssuesColumn(query, column, query.componentRoots(), 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 deleted file mode 100644 index 14bdfc99604..00000000000 --- a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueStatsMapper.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 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.server.issue.IssueQuery; - -import javax.annotation.Nullable; - -import java.util.Collection; -import java.util.List; - -public interface IssueStatsMapper { - - List selectIssuesColumn(@Param("query") IssueQuery query, @Param("column") String column, @Param("componentRootKeys") Collection componentRootKeys, - @Nullable @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 5ac15ee88e6..c097fd6e167 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,13 +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.IssueFilterDao; -import org.sonar.core.issue.db.IssueFilterFavouriteDao; -import org.sonar.core.issue.db.IssueStatsDao; +import org.sonar.core.issue.db.*; import org.sonar.core.notification.db.NotificationQueueDao; import org.sonar.core.permission.PermissionDao; import org.sonar.core.permission.PermissionTemplateDao; @@ -46,11 +40,7 @@ import org.sonar.core.source.db.SnapshotDataDao; import org.sonar.core.source.db.SnapshotSourceDao; import org.sonar.core.technicaldebt.db.CharacteristicDao; import org.sonar.core.template.LoadedTemplateDao; -import org.sonar.core.user.AuthorDao; -import org.sonar.core.user.AuthorizationDao; -import org.sonar.core.user.GroupMembershipDao; -import org.sonar.core.user.RoleDao; -import org.sonar.core.user.UserDao; +import org.sonar.core.user.*; import java.util.List; @@ -74,7 +64,6 @@ public final class DaoUtils { GraphDao.class, GroupMembershipDao.class, IssueDao.class, - IssueStatsDao.class, IssueChangeDao.class, IssueFilterDao.class, IssueFilterFavouriteDao.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 051892c4cc9..86a1bb4861f 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 @@ -169,7 +169,7 @@ public class MyBatis implements BatchComponent, ServerComponent { loadMapper(conf, "org.sonar.core.permission.PermissionMapper"); Class[] mappers = {ActivityMapper.class, ActiveDashboardMapper.class, AuthorMapper.class, DashboardMapper.class, DependencyMapper.class, DuplicationMapper.class, GraphDtoMapper.class, - IssueMapper.class, IssueAuthorizationMapper.class, IssueStatsMapper.class, IssueChangeMapper.class, IssueFilterMapper.class, IssueFilterFavouriteMapper.class, + IssueMapper.class, IssueAuthorizationMapper.class, IssueChangeMapper.class, IssueFilterMapper.class, IssueFilterFavouriteMapper.class, LoadedTemplateMapper.class, MeasureFilterMapper.class, Migration44Mapper.class, PermissionTemplateMapper.class, PropertiesMapper.class, PurgeMapper.class, ResourceKeyUpdaterMapper.class, ResourceIndexerMapper.class, ResourceSnapshotMapper.class, RoleMapper.class, RuleMapper.class, SchemaMigrationMapper.class, SemaphoreMapper.class, UserMapper.class, GroupMapper.class, WidgetMapper.class, WidgetPropertyMapper.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 deleted file mode 100644 index a721832a98a..00000000000 --- a/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueStatsMapper.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - 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 deleted file mode 100644 index 304185d13e1..00000000000 --- a/sonar-core/src/test/java/org/sonar/core/issue/db/IssueStatsDaoTest.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 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.core.persistence.AbstractDaoTestCase; -import org.sonar.server.issue.IssueQuery; - -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 results = dao.selectIssuesColumn(query, IssueStatsColumn.ASSIGNEE, null); - assertThat(results).hasSize(3); - // 2 perceval, and one null - assertThat(results).containsOnly("perceval", null); - } -} -- 2.39.5