]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5603 Get issues from E/S in Unresolved Issues per Assignee widget
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 1 Oct 2014 12:11:16 +0000 (14:11 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 2 Oct 2014 13:27:04 +0000 (15:27 +0200)
Conflicts:
server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java

19 files changed:
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/issues/UnresolvedIssuesPerAssigneeWidget.java
plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/issues/unresolved_issues_per_assignee.html.erb
server/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java
server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java
server/sonar-server/src/main/java/org/sonar/server/issue/IssueStatsFinder.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/issue/actionplan/ActionPlanService.java
server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java
server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/IssueStatsFinderTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java
sonar-core/src/main/java/org/sonar/core/issue/db/IssueStatsColumn.java [deleted file]
sonar-core/src/main/java/org/sonar/core/issue/db/IssueStatsDao.java [deleted file]
sonar-core/src/main/java/org/sonar/core/issue/db/IssueStatsMapper.java [deleted file]
sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java
sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java
sonar-core/src/main/resources/org/sonar/core/issue/db/IssueStatsMapper.xml [deleted file]
sonar-core/src/test/java/org/sonar/core/issue/db/IssueStatsDaoTest.java [deleted file]

index a8f8e8d41e62a09d7bad07cccc2c739925aebbda..2067c798b993c0e3ef42565f9d982159b60b4e70 100644 (file)
@@ -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");
   }
 }
index 5e1cadae3d995eae528ec5b98d744000959a89b7..fec0255334faadf91fcbf4a44e7cb125fe9d7d7c 100644 (file)
@@ -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
 %>
 
 <h3><%= message('widget.reviews_per_developer.name') -%></h3>
 
-<% if assignees.size ==0 %>
+<% if issues_by_assignee.size ==0 %>
   <span class="empty_widget"><%= message('issue_filter.no_issues') -%></span>
 
 <% else %>
     </thead>
     <tbody>
       <%
-         issues_by_assignee.sort{|h1,h2| h2[1] <=> h1[1]}.each do |assignee, count|
+         issues_by_assignee.each do |assignee, count|
+          if assignee
       %>
         <tr class="<%= cycle 'even', 'odd', :name => (div_id) -%>">
           <td class="max-width">
-            <%= 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)) -%>
           </td>
           <td class="nowrap">
             <%= '+' if @dashboard_configuration.selected_period? -%><%= count.to_s -%>
@@ -65,9 +60,8 @@
           <% end %>
         </tr>
       <%
-        end
-        # And now show the unassigned issues counter
-        unless counter_no_assignee==0
+         # And now show the unassigned issues counter
+         else
       %>
         <tr class="<%= cycle 'even', 'odd', :name => (div_id) -%>">
           <td class="max-width">
@@ -82,7 +76,8 @@
             </td>
           <% end %>
         </tr>
-      <% end %>
+      <% end
+        end %>
     </tbody>
   </table>
 
index 271849db29c1d1cf2720a495ca460225ead58898..3934e2ffae1c0905cbbeb67b0ad674cf0bd4aa76 100644 (file)
@@ -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<String, Object> params) {
-    return issueStatsFinder.findIssueAssignees(toQuery(params));
+  public Map<String, Integer> findIssueAssignees(Map<String, Object> params) {
+    return issueService.findIssueAssignees(toQuery(params));
   }
 
   public List<Transition> listTransitions(String issueKey) {
index 94e9fc9f8fc3cae6d8973030181aeb4f48ebab11..a281c14f5fa2fb6ebd2b1cb6812062ccd65f147e 100644 (file)
@@ -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<String> findSeveritiesByComponent(String componentKey, @Nullable Date periodDate, DbSession session) {
     Multiset<String> 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<String, Integer> findIssueAssignees(IssueQuery query) {
+    Map<String, Integer> result = newLinkedHashMap();
+    List<FacetValue> 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 (file)
index 0eec6f6..0000000
+++ /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<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);
-    }
-  }
-}
index a85a7dcb37bebbbfb713628b36429f60042eacfe..e25c14ea3c555d767a9747308752dcd6a3a91969 100644 (file)
@@ -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<IssueDto> dtos = issueDao.selectIssues(query);
     IssueChangeContext context = IssueChangeContext.createUser(new Date(), userSession.login());
     List<DefaultIssue> issues = newArrayList();
index 5fb9a69306d41f827b917ffe74237294e57a2a33..33e60a8998111de891d05cb665615fd60cf63311 100644 (file)
@@ -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<Issue, IssueDto, String> {
     return null;
   }
 
+  public List<FacetValue> 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<FacetValue> 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<Issue> search(IssueQuery query, QueryContext options) {
     SearchRequestBuilder esSearch = getClient()
       .prepareSearch(this.getIndexName())
index 085a8582f789600a66c57c20e3348aae4dce2948..a765263734efa8c1c55823dd7cb69b29719d8963 100644 (file)
@@ -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);
index 049e76e0b93d87138ebf44cbd708518d89c500ae..beb3c74216db5749708e551411d245faa1ec5bd9 100644 (file)
@@ -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.<String, Object>of("issues", "ABCD"));
-    verify(issueStatsFinder).findIssueAssignees(any(IssueQuery.class));
+    verify(issueService).findIssueAssignees(any(IssueQuery.class));
   }
 
   @Test
index 10e667292ac362f2aded91ebf266dcbfea897e0d..c2fe8dddf1dfd5d80e15b816a524ff64afc88888 100644 (file)
@@ -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<String, Integer> 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 (file)
index d52209e..0000000
+++ /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.<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");
-  }
-}
index 0901f951c28e8e5279fb753dddbe9371575aa56f..00ac616f8b33394a46872c6366a03a508ec7d02a 100644 (file)
@@ -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<String> 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<FacetValue> 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 (file)
index 50a85b4..0000000
+++ /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<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
deleted file mode 100644 (file)
index 81db075..0000000
+++ /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<Object> 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 (file)
index 14bdfc9..0000000
+++ /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<Object> selectIssuesColumn(@Param("query") IssueQuery query, @Param("column") String column, @Param("componentRootKeys") Collection<String> componentRootKeys,
-                                  @Nullable @Param("userId") Integer userId, @Param("role") String role);
-
-}
index 5ac15ee88e6a01d035a4dca44f05455cc9f4997e..c097fd6e167730706c25a3b064bc6b9fcabcabdd 100644 (file)
@@ -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,
index 051892c4cc908b7b6d839e4b6b34e872e65f9f82..86a1bb4861f79f54e480a6ae97371b29515805fa 100644 (file)
@@ -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 (file)
index a721832..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-<?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">
-    SELECT
-    <if test="'ASSIGNEE'.equals(column)">
-      i.assignee
-    </if>
-    FROM issues i
-    <include refid="org.sonar.core.issue.db.IssueMapper.selectQueryConditions"/>
-  </select>
-
-</mapper>
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 (file)
index 304185d..0000000
+++ /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<Object> results = dao.selectIssuesColumn(query, IssueStatsColumn.ASSIGNEE, null);
-    assertThat(results).hasSize(3);
-    // 2 perceval, and one null
-    assertThat(results).containsOnly("perceval", null);
-  }
-}