]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6150 Search authors in issues index
authorJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>
Thu, 5 Feb 2015 11:07:31 +0000 (12:07 +0100)
committerJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>
Thu, 5 Feb 2015 14:47:53 +0000 (15:47 +0100)
16 files changed:
server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java
server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java
server/sonar-server/src/main/java/org/sonar/server/issue/ws/AuthorsAction.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssuesWs.java
server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
server/sonar-server/src/main/resources/org/sonar/server/issue/ws/example-authors.json [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/ws/AuthorsActionTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/issue/ws/ComponentTagsActionTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowActionTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueTagsActionTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssuesWsTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/ws/SetTagsActionTest.java
server/sonar-server/src/test/java/org/sonar/server/user/ws/UpdateActionTest.java
server/sonar-server/src/test/java/org/sonar/server/user/ws/UsersWsTest.java
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/AuthorsActionTest/authors.json [new file with mode: 0644]

index a35e89e78149d6ac5b919839805586eaf37bce60..0c893e2fe79c11ff046cd0a106bdff27fcb63c73 100644 (file)
@@ -64,12 +64,7 @@ import org.sonar.server.user.index.UserIndex;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 import static com.google.common.collect.Maps.newLinkedHashMap;
 
@@ -363,6 +358,10 @@ public class IssueService implements ServerComponent {
     return indexClient.get(IssueIndex.class).listTagsMatching(query, pageSize);
   }
 
+  public Collection<String> listAuthors(@Nullable String query, int pageSize) {
+    return indexClient.get(IssueIndex.class).listAuthorsMatching(query, pageSize);
+  }
+
   public Collection<String> setTags(String issueKey, Collection<String> tags) {
     verifyLoggedIn();
 
index 9a27f23c87125fdc7ad2b697b9197d08fdf1c466..406def9df59e4c2cbeaebca01b21cf9e6cbe9bf7 100644 (file)
@@ -618,11 +618,19 @@ public class IssueIndex extends BaseIndex<Issue, FakeIssueDto, String> {
   }
 
   public Collection<String> listTagsMatching(@Nullable String query, int pageSize) {
+    return listTermsMatching(IssueIndexDefinition.FIELD_ISSUE_TAGS, query, pageSize);
+  }
+
+  public Collection<String> listAuthorsMatching(@Nullable String query, int pageSize) {
+    return listTermsMatching(IssueIndexDefinition.FIELD_ISSUE_AUTHOR_LOGIN, query, pageSize);
+  }
+
+  private Collection<String> listTermsMatching(String fieldName, @Nullable String query, int pageSize) {
     SearchRequestBuilder count = getClient().prepareSearch(IssueIndexDefinition.INDEX)
       .setTypes(IssueIndexDefinition.TYPE_ISSUE)
       .setQuery(QueryBuilders.matchAllQuery());
     TermsBuilder aggreg = AggregationBuilders.terms("_ref")
-      .field(IssueIndexDefinition.FIELD_ISSUE_TAGS)
+      .field(fieldName)
       .size(pageSize)
       .order(Order.term(true))
       .minDocCount(1L);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/AuthorsAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/AuthorsAction.java
new file mode 100644 (file)
index 0000000..3d500c0
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * 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.ws;
+
+import com.google.common.io.Resources;
+import org.sonar.api.server.ws.*;
+import org.sonar.api.server.ws.WebService.NewAction;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.server.issue.IssueService;
+
+public class AuthorsAction implements RequestHandler {
+
+  private static final String PARAM_PAGE_SIZE = "ps";
+  private static final String PARAM_QUERY = "q";
+  private final IssueService service;
+
+  public AuthorsAction(IssueService service) {
+    this.service = service;
+  }
+
+  @Override
+  public void handle(Request request, Response response) throws Exception {
+    String query = request.param(PARAM_QUERY);
+    int pageSize = request.mandatoryParamAsInt(PARAM_PAGE_SIZE);
+
+    JsonWriter json = response.newJsonWriter()
+      .beginObject()
+      .name("authors")
+      .beginArray();
+
+    for (String login : service.listAuthors(query, pageSize)) {
+      json.value(login);
+    }
+
+    json.endArray().endObject().close();
+  }
+
+  void define(WebService.NewController controller) {
+    NewAction action = controller.createAction("authors")
+      .setSince("5.1")
+      .setDescription("Search SCM accounts which match a given query")
+      .setResponseExample(Resources.getResource(this.getClass(), "example-authors.json"))
+      .setHandler(this);
+
+    action.createParam(PARAM_QUERY)
+      .setDescription("A pattern to match SCM accounts against")
+      .setExampleValue("luke");
+    action.createParam(PARAM_PAGE_SIZE)
+      .setDescription("The size of the list to return")
+      .setExampleValue("25")
+      .setDefaultValue("10");
+  }
+}
index ef3146df4d6c584b14d460a3d574465ee997c231..8efe7e1f039dabd51a31a97d53099f7adb21f8e3 100644 (file)
@@ -47,13 +47,17 @@ public class IssuesWs implements WebService {
   private final TagsAction tagsAction;
   private final SetTagsAction setTagsAction;
   private final ComponentTagsAction componentTagsAction;
+  private final AuthorsAction authorsAction;
 
-  public IssuesWs(IssueShowAction showAction, SearchAction searchAction, TagsAction tagsAction, SetTagsAction setTagsAction, ComponentTagsAction componentTagsAction) {
+
+  public IssuesWs(IssueShowAction showAction, SearchAction searchAction, TagsAction tagsAction, SetTagsAction setTagsAction, ComponentTagsAction componentTagsAction,
+      AuthorsAction authorsAction) {
     this.showAction = showAction;
     this.esSearchAction = searchAction;
     this.tagsAction = tagsAction;
     this.setTagsAction = setTagsAction;
     this.componentTagsAction = componentTagsAction;
+    this.authorsAction = authorsAction;
   }
 
   @Override
@@ -67,6 +71,7 @@ public class IssuesWs implements WebService {
     tagsAction.define(controller);
     setTagsAction.define(controller);
     componentTagsAction.define(controller);
+    authorsAction.define(controller);
 
     defineChangelogAction(controller);
     defineAssignAction(controller);
index c059d2a5cac5f83a8f872073178d144292a8440f..9de6fee0478d759ef990f4ee7358de2445c133e6 100644 (file)
@@ -53,12 +53,7 @@ import org.sonar.core.measure.db.MeasureFilterDao;
 import org.sonar.core.metric.DefaultMetricFinder;
 import org.sonar.core.notification.DefaultNotificationManager;
 import org.sonar.core.permission.PermissionFacade;
-import org.sonar.core.persistence.DaoUtils;
-import org.sonar.core.persistence.DatabaseVersion;
-import org.sonar.core.persistence.DefaultDatabase;
-import org.sonar.core.persistence.MyBatis;
-import org.sonar.core.persistence.SemaphoreUpdater;
-import org.sonar.core.persistence.SemaphoresImpl;
+import org.sonar.core.persistence.*;
 import org.sonar.core.profiling.Profiling;
 import org.sonar.core.purge.PurgeProfiler;
 import org.sonar.core.qualitygate.db.ProjectQgateAssociationDao;
@@ -83,12 +78,7 @@ import org.sonar.server.activity.index.ActivityNormalizer;
 import org.sonar.server.activity.ws.ActivitiesWebService;
 import org.sonar.server.activity.ws.ActivityMapping;
 import org.sonar.server.authentication.ws.AuthenticationWs;
-import org.sonar.server.batch.BatchIndex;
-import org.sonar.server.batch.BatchWs;
-import org.sonar.server.batch.GlobalRepositoryAction;
-import org.sonar.server.batch.IssuesAction;
-import org.sonar.server.batch.ProjectRepositoryAction;
-import org.sonar.server.batch.ProjectRepositoryLoader;
+import org.sonar.server.batch.*;
 import org.sonar.server.charts.ChartFactory;
 import org.sonar.server.component.ComponentCleanerService;
 import org.sonar.server.component.ComponentService;
@@ -96,20 +86,12 @@ import org.sonar.server.component.DefaultComponentFinder;
 import org.sonar.server.component.DefaultRubyComponentService;
 import org.sonar.server.component.db.ComponentDao;
 import org.sonar.server.component.db.SnapshotDao;
-import org.sonar.server.component.ws.ComponentAppAction;
-import org.sonar.server.component.ws.ComponentsWs;
-import org.sonar.server.component.ws.EventsWs;
-import org.sonar.server.component.ws.ProjectsWs;
-import org.sonar.server.component.ws.ResourcesWs;
+import org.sonar.server.component.ws.*;
 import org.sonar.server.computation.AnalysisReportQueue;
 import org.sonar.server.computation.AnalysisReportQueueCleaner;
 import org.sonar.server.computation.ComputationThreadLauncher;
 import org.sonar.server.computation.db.AnalysisReportDao;
-import org.sonar.server.computation.ws.ComputationWebService;
-import org.sonar.server.computation.ws.HistoryWsAction;
-import org.sonar.server.computation.ws.IsQueueEmptyWebService;
-import org.sonar.server.computation.ws.QueueWsAction;
-import org.sonar.server.computation.ws.SubmitReportWsAction;
+import org.sonar.server.computation.ws.*;
 import org.sonar.server.config.ws.PropertiesWs;
 import org.sonar.server.dashboard.db.DashboardDao;
 import org.sonar.server.dashboard.db.WidgetDao;
@@ -121,14 +103,7 @@ import org.sonar.server.db.DbClient;
 import org.sonar.server.db.EmbeddedDatabaseFactory;
 import org.sonar.server.db.migrations.DatabaseMigrations;
 import org.sonar.server.db.migrations.DatabaseMigrator;
-import org.sonar.server.debt.DebtCharacteristicsXMLImporter;
-import org.sonar.server.debt.DebtModelBackup;
-import org.sonar.server.debt.DebtModelLookup;
-import org.sonar.server.debt.DebtModelOperations;
-import org.sonar.server.debt.DebtModelPluginRepository;
-import org.sonar.server.debt.DebtModelService;
-import org.sonar.server.debt.DebtModelXMLExporter;
-import org.sonar.server.debt.DebtRulesXMLImporter;
+import org.sonar.server.debt.*;
 import org.sonar.server.design.FileDesignWidget;
 import org.sonar.server.duplication.ws.DuplicationsJsonWriter;
 import org.sonar.server.duplication.ws.DuplicationsParser;
@@ -136,22 +111,7 @@ import org.sonar.server.duplication.ws.DuplicationsWs;
 import org.sonar.server.es.EsClient;
 import org.sonar.server.es.IndexCreator;
 import org.sonar.server.es.IndexRegistry;
-import org.sonar.server.issue.ActionService;
-import org.sonar.server.issue.AddTagsAction;
-import org.sonar.server.issue.AssignAction;
-import org.sonar.server.issue.CommentAction;
-import org.sonar.server.issue.InternalRubyIssueService;
-import org.sonar.server.issue.IssueBulkChangeService;
-import org.sonar.server.issue.IssueChangelogFormatter;
-import org.sonar.server.issue.IssueChangelogService;
-import org.sonar.server.issue.IssueCommentService;
-import org.sonar.server.issue.IssueQueryService;
-import org.sonar.server.issue.IssueService;
-import org.sonar.server.issue.PlanAction;
-import org.sonar.server.issue.RemoveTagsAction;
-import org.sonar.server.issue.ServerIssueStorage;
-import org.sonar.server.issue.SetSeverityAction;
-import org.sonar.server.issue.TransitionAction;
+import org.sonar.server.issue.*;
 import org.sonar.server.issue.actionplan.ActionPlanService;
 import org.sonar.server.issue.actionplan.ActionPlanWs;
 import org.sonar.server.issue.db.IssueDao;
@@ -162,16 +122,8 @@ import org.sonar.server.issue.index.IssueAuthorizationIndexer;
 import org.sonar.server.issue.index.IssueIndex;
 import org.sonar.server.issue.index.IssueIndexDefinition;
 import org.sonar.server.issue.index.IssueIndexer;
-import org.sonar.server.issue.notification.ChangesOnMyIssueNotificationDispatcher;
-import org.sonar.server.issue.notification.IssueChangesEmailTemplate;
-import org.sonar.server.issue.notification.NewFalsePositiveNotificationDispatcher;
-import org.sonar.server.issue.notification.NewIssuesEmailTemplate;
-import org.sonar.server.issue.notification.NewIssuesNotificationDispatcher;
-import org.sonar.server.issue.ws.ComponentTagsAction;
-import org.sonar.server.issue.ws.IssueActionsWriter;
-import org.sonar.server.issue.ws.IssueShowAction;
-import org.sonar.server.issue.ws.IssuesWs;
-import org.sonar.server.issue.ws.SetTagsAction;
+import org.sonar.server.issue.notification.*;
+import org.sonar.server.issue.ws.*;
 import org.sonar.server.language.ws.LanguageWs;
 import org.sonar.server.language.ws.ListAction;
 import org.sonar.server.measure.MeasureFilterEngine;
@@ -192,117 +144,35 @@ import org.sonar.server.platform.ws.L10nWs;
 import org.sonar.server.platform.ws.RestartHandler;
 import org.sonar.server.platform.ws.ServerWs;
 import org.sonar.server.platform.ws.SystemWs;
-import org.sonar.server.plugins.InstalledPluginReferentialFactory;
-import org.sonar.server.plugins.PluginDownloader;
-import org.sonar.server.plugins.ServerExtensionInstaller;
-import org.sonar.server.plugins.ServerPluginJarInstaller;
-import org.sonar.server.plugins.ServerPluginJarsInstaller;
-import org.sonar.server.plugins.ServerPluginRepository;
-import org.sonar.server.plugins.UpdateCenterClient;
-import org.sonar.server.plugins.UpdateCenterMatrixFactory;
+import org.sonar.server.plugins.*;
 import org.sonar.server.properties.ProjectSettingsFactory;
 import org.sonar.server.qualitygate.QgateProjectFinder;
 import org.sonar.server.qualitygate.QualityGates;
 import org.sonar.server.qualitygate.RegisterQualityGates;
-import org.sonar.server.qualitygate.ws.QGatesAppAction;
-import org.sonar.server.qualitygate.ws.QGatesCopyAction;
-import org.sonar.server.qualitygate.ws.QGatesCreateAction;
-import org.sonar.server.qualitygate.ws.QGatesCreateConditionAction;
-import org.sonar.server.qualitygate.ws.QGatesDeleteConditionAction;
-import org.sonar.server.qualitygate.ws.QGatesDeselectAction;
-import org.sonar.server.qualitygate.ws.QGatesDestroyAction;
-import org.sonar.server.qualitygate.ws.QGatesListAction;
-import org.sonar.server.qualitygate.ws.QGatesRenameAction;
-import org.sonar.server.qualitygate.ws.QGatesSearchAction;
-import org.sonar.server.qualitygate.ws.QGatesSelectAction;
-import org.sonar.server.qualitygate.ws.QGatesSetAsDefaultAction;
-import org.sonar.server.qualitygate.ws.QGatesShowAction;
-import org.sonar.server.qualitygate.ws.QGatesUnsetDefaultAction;
-import org.sonar.server.qualitygate.ws.QGatesUpdateConditionAction;
-import org.sonar.server.qualitygate.ws.QGatesWs;
-import org.sonar.server.qualityprofile.BuiltInProfiles;
-import org.sonar.server.qualityprofile.QProfileBackuper;
-import org.sonar.server.qualityprofile.QProfileCopier;
-import org.sonar.server.qualityprofile.QProfileExporters;
-import org.sonar.server.qualityprofile.QProfileFactory;
-import org.sonar.server.qualityprofile.QProfileLoader;
-import org.sonar.server.qualityprofile.QProfileLookup;
-import org.sonar.server.qualityprofile.QProfileProjectLookup;
-import org.sonar.server.qualityprofile.QProfileProjectOperations;
-import org.sonar.server.qualityprofile.QProfileReset;
-import org.sonar.server.qualityprofile.QProfileService;
-import org.sonar.server.qualityprofile.QProfiles;
-import org.sonar.server.qualityprofile.RegisterQualityProfiles;
-import org.sonar.server.qualityprofile.RuleActivator;
-import org.sonar.server.qualityprofile.RuleActivatorContextFactory;
+import org.sonar.server.qualitygate.ws.*;
+import org.sonar.server.qualityprofile.*;
 import org.sonar.server.qualityprofile.db.ActiveRuleDao;
 import org.sonar.server.qualityprofile.index.ActiveRuleIndex;
 import org.sonar.server.qualityprofile.index.ActiveRuleNormalizer;
-import org.sonar.server.qualityprofile.ws.BulkRuleActivationActions;
-import org.sonar.server.qualityprofile.ws.ProfilesWs;
-import org.sonar.server.qualityprofile.ws.QProfileRestoreBuiltInAction;
-import org.sonar.server.qualityprofile.ws.QProfilesWs;
-import org.sonar.server.qualityprofile.ws.RuleActivationActions;
-import org.sonar.server.rule.DefaultRuleFinder;
-import org.sonar.server.rule.DeprecatedRulesDefinitionLoader;
-import org.sonar.server.rule.RegisterRules;
-import org.sonar.server.rule.RubyRuleService;
-import org.sonar.server.rule.RuleCreator;
-import org.sonar.server.rule.RuleDefinitionsLoader;
-import org.sonar.server.rule.RuleDeleter;
-import org.sonar.server.rule.RuleOperations;
-import org.sonar.server.rule.RuleRepositories;
-import org.sonar.server.rule.RuleService;
-import org.sonar.server.rule.RuleUpdater;
+import org.sonar.server.qualityprofile.ws.*;
+import org.sonar.server.rule.*;
 import org.sonar.server.rule.db.RuleDao;
 import org.sonar.server.rule.index.RuleIndex;
 import org.sonar.server.rule.index.RuleNormalizer;
-import org.sonar.server.rule.ws.ActiveRuleCompleter;
-import org.sonar.server.rule.ws.AppAction;
-import org.sonar.server.rule.ws.DeleteAction;
-import org.sonar.server.rule.ws.RepositoriesAction;
-import org.sonar.server.rule.ws.RuleMapping;
-import org.sonar.server.rule.ws.RulesWebService;
+import org.sonar.server.rule.ws.*;
 import org.sonar.server.rule.ws.SearchAction;
 import org.sonar.server.rule.ws.TagsAction;
-import org.sonar.server.rule.ws.UpdateAction;
-import org.sonar.server.search.IndexClient;
-import org.sonar.server.search.IndexQueue;
-import org.sonar.server.search.IndexSynchronizer;
-import org.sonar.server.search.SearchClient;
-import org.sonar.server.search.SearchHealth;
+import org.sonar.server.search.*;
 import org.sonar.server.source.HtmlSourceDecorator;
 import org.sonar.server.source.SourceService;
 import org.sonar.server.source.index.SourceLineIndex;
 import org.sonar.server.source.index.SourceLineIndexDefinition;
 import org.sonar.server.source.index.SourceLineIndexer;
-import org.sonar.server.source.ws.HashAction;
-import org.sonar.server.source.ws.IndexAction;
-import org.sonar.server.source.ws.LinesAction;
-import org.sonar.server.source.ws.RawAction;
-import org.sonar.server.source.ws.ScmAction;
-import org.sonar.server.source.ws.ScmWriter;
+import org.sonar.server.source.ws.*;
 import org.sonar.server.source.ws.ShowAction;
-import org.sonar.server.source.ws.SourcesWs;
-import org.sonar.server.startup.CopyRequirementsFromCharacteristicsToRules;
-import org.sonar.server.startup.GeneratePluginIndex;
-import org.sonar.server.startup.JdbcDriverDeployer;
-import org.sonar.server.startup.LogServerId;
-import org.sonar.server.startup.RegisterDashboards;
-import org.sonar.server.startup.RegisterDebtModel;
-import org.sonar.server.startup.RegisterMetrics;
-import org.sonar.server.startup.RegisterNewMeasureFilters;
-import org.sonar.server.startup.RegisterPermissionTemplates;
-import org.sonar.server.startup.RegisterServletFilters;
-import org.sonar.server.startup.RenameDeprecatedPropertyKeys;
-import org.sonar.server.startup.ServerMetadataPersister;
+import org.sonar.server.startup.*;
 import org.sonar.server.test.CoverageService;
-import org.sonar.server.test.ws.CoverageShowAction;
-import org.sonar.server.test.ws.CoverageWs;
-import org.sonar.server.test.ws.TestsCoveredFilesAction;
-import org.sonar.server.test.ws.TestsShowAction;
-import org.sonar.server.test.ws.TestsTestCasesAction;
-import org.sonar.server.test.ws.TestsWs;
+import org.sonar.server.test.ws.*;
 import org.sonar.server.text.MacroInterpreter;
 import org.sonar.server.text.RubyTextService;
 import org.sonar.server.ui.JRubyI18n;
@@ -310,14 +180,7 @@ import org.sonar.server.ui.JRubyProfiling;
 import org.sonar.server.ui.PageDecorations;
 import org.sonar.server.ui.Views;
 import org.sonar.server.updatecenter.ws.UpdateCenterWs;
-import org.sonar.server.user.DefaultUserService;
-import org.sonar.server.user.DoPrivileged;
-import org.sonar.server.user.GroupMembershipFinder;
-import org.sonar.server.user.GroupMembershipService;
-import org.sonar.server.user.NewUserNotifier;
-import org.sonar.server.user.SecurityRealmFactory;
-import org.sonar.server.user.UserService;
-import org.sonar.server.user.UserUpdater;
+import org.sonar.server.user.*;
 import org.sonar.server.user.db.GroupDao;
 import org.sonar.server.user.db.UserDao;
 import org.sonar.server.user.db.UserGroupDao;
@@ -327,13 +190,7 @@ import org.sonar.server.user.index.UserIndexer;
 import org.sonar.server.user.ws.FavoritesWs;
 import org.sonar.server.user.ws.UserPropertiesWs;
 import org.sonar.server.user.ws.UsersWs;
-import org.sonar.server.util.BooleanTypeValidation;
-import org.sonar.server.util.FloatTypeValidation;
-import org.sonar.server.util.IntegerTypeValidation;
-import org.sonar.server.util.StringListTypeValidation;
-import org.sonar.server.util.StringTypeValidation;
-import org.sonar.server.util.TextTypeValidation;
-import org.sonar.server.util.TypeValidations;
+import org.sonar.server.util.*;
 import org.sonar.server.view.index.ViewIndex;
 import org.sonar.server.view.index.ViewIndexDefinition;
 import org.sonar.server.view.index.ViewIndexer;
@@ -621,6 +478,7 @@ class ServerComponents {
     pico.addSingleton(UsersWs.class);
     pico.addSingleton(org.sonar.server.user.ws.CreateAction.class);
     pico.addSingleton(org.sonar.server.user.ws.UpdateAction.class);
+    pico.addSingleton(org.sonar.server.issue.ws.AuthorsAction.class);
     pico.addSingleton(FavoritesWs.class);
     pico.addSingleton(UserPropertiesWs.class);
     pico.addSingleton(UserIndexDefinition.class);
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/issue/ws/example-authors.json b/server/sonar-server/src/main/resources/org/sonar/server/issue/ws/example-authors.json
new file mode 100644 (file)
index 0000000..7750054
--- /dev/null
@@ -0,0 +1,15 @@
+{
+  "authors": {
+    "more": true,
+    "results": [
+      {
+        "key": "leia.organa",
+        "text": "leia.organa"
+      },
+      {
+        "key": "luke@skywalker.name",
+        "text": "luke@skywalker.name"
+      }
+    ]
+  }
+}
index cc3ffa6f4783c85e1437be6923877899b7ffeae3..b405eef9efd916fbafe06f15447e87aa5e025feb 100644 (file)
@@ -74,7 +74,10 @@ import java.util.Map;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.entry;
 import static org.junit.Assert.fail;
-import static org.sonar.server.source.index.SourceLineIndexDefinition.*;
+import static org.sonar.server.source.index.SourceLineIndexDefinition.FIELD_FILE_UUID;
+import static org.sonar.server.source.index.SourceLineIndexDefinition.FIELD_LINE;
+import static org.sonar.server.source.index.SourceLineIndexDefinition.FIELD_PROJECT_UUID;
+import static org.sonar.server.source.index.SourceLineIndexDefinition.FIELD_SCM_AUTHOR;
 
 public class IssueServiceMediumTest {
 
@@ -587,6 +590,23 @@ public class IssueServiceMediumTest {
     assertThat(service.listTagsForComponent("other", 10)).isEmpty();
   }
 
+  @Test
+  public void list_authors() {
+    RuleDto rule = newRule();
+    ComponentDto project = newProject();
+    ComponentDto file = newFile(project);
+    saveIssue(IssueTesting.newDto(rule, file, project).setAuthorLogin("luke.skywalker"));
+    saveIssue(IssueTesting.newDto(rule, file, project).setAuthorLogin("luke@skywalker.name"));
+    saveIssue(IssueTesting.newDto(rule, file, project));
+    saveIssue(IssueTesting.newDto(rule, file, project).setAuthorLogin("anakin@skywalker.name"));
+
+    assertThat(service.listAuthors(null, 5)).containsExactly("anakin@skywalker.name", "luke.skywalker", "luke@skywalker.name");
+    assertThat(service.listAuthors(null, 2)).containsExactly("anakin@skywalker.name", "luke.skywalker");
+    assertThat(service.listAuthors("uke", 5)).containsExactly("luke.skywalker", "luke@skywalker.name");
+    assertThat(service.listAuthors(null, 1)).containsExactly("anakin@skywalker.name");
+    assertThat(service.listAuthors(null, Integer.MAX_VALUE)).containsExactly("anakin@skywalker.name", "luke.skywalker", "luke@skywalker.name");
+  }
+
   private RuleDto newRule() {
     RuleDto rule = RuleTesting.newXooX1();
     tester.get(RuleDao.class).insert(session, rule);
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/AuthorsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/AuthorsActionTest.java
new file mode 100644 (file)
index 0000000..33acb9a
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * 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.ws;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.server.issue.IssueService;
+import org.sonar.server.ws.WsTester;
+
+import java.util.Arrays;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class AuthorsActionTest {
+
+  WebService.Controller controller;
+
+  WsTester tester;
+
+  @Mock
+  IssueService service;
+
+  @Before
+  public void setUp() throws Exception {
+    tester = new WsTester(new IssuesWs(mock(IssueShowAction.class), new SearchAction(null, null, null, null, null, null, null, null, null, null, null),
+      mock(TagsAction.class), mock(SetTagsAction.class), mock(ComponentTagsAction.class),
+      new AuthorsAction(service)));
+    controller = tester.controller("api/issues");
+  }
+
+  @Test
+  public void fetch_authors() throws Exception {
+    String query = "luke";
+    int pageSize = 42;
+    when(service.listAuthors(query, pageSize))
+      .thenReturn(Arrays.asList("luke.skywalker", "luke@skywalker.name"));
+
+    tester.newGetRequest("api/issues", "authors")
+      .setParam("q", query)
+      .setParam("ps", Integer.toString(pageSize))
+      .execute()
+      .assertJson(getClass(), "authors.json");
+
+    verify(service).listAuthors(query, pageSize);
+  }
+}
index bd0e07d1491ecb746c0e7693cff46be5a9f26b70..31bb581a73061800da00299f94f27146e9a752ef 100644 (file)
@@ -52,7 +52,7 @@ public class ComponentTagsActionTest {
     tester = new WsTester(
       new IssuesWs(new IssueShowAction(null, null, null, null, null, null, null, null, null, null, null),
         new SearchAction(null, null, null, null, null, null, null, null, null, null,null),
-        new TagsAction(null), new SetTagsAction(null), componentTagsAction));
+        new TagsAction(null), new SetTagsAction(null), componentTagsAction, new AuthorsAction(null)));
   }
 
   @Test
index 799c7f266a80543fe9f13fed0e7ed09cd680a19a..5dd1de61d0bb3ae308ce8252841e8cd8c712fcdf 100644 (file)
@@ -49,12 +49,7 @@ import org.sonar.server.component.ComponentTesting;
 import org.sonar.server.component.db.ComponentDao;
 import org.sonar.server.db.DbClient;
 import org.sonar.server.debt.DebtModelService;
-import org.sonar.server.issue.ActionService;
-import org.sonar.server.issue.IssueChangelog;
-import org.sonar.server.issue.IssueChangelogService;
-import org.sonar.server.issue.IssueCommentService;
-import org.sonar.server.issue.IssueQueryService;
-import org.sonar.server.issue.IssueService;
+import org.sonar.server.issue.*;
 import org.sonar.server.issue.actionplan.ActionPlanService;
 import org.sonar.server.rule.Rule;
 import org.sonar.server.rule.RuleService;
@@ -150,7 +145,7 @@ public class IssueShowActionTest {
       new SearchAction(mock(DbClient.class), mock(IssueChangeDao.class), mock(IssueService.class), mock(IssueActionsWriter.class), mock(IssueQueryService.class),
         mock(RuleService.class),
         mock(ActionPlanService.class), mock(UserFinder.class), mock(I18n.class), mock(Durations.class), mock(Languages.class)),
-      new TagsAction(null), new SetTagsAction(null), new ComponentTagsAction(null)
+      new TagsAction(null), new SetTagsAction(null), new ComponentTagsAction(null), new AuthorsAction(null)
       ));
   }
 
index fccb68f4ea4b48476bba1e0e4e1296c9452ac2fa..45f97790bde59e705119ebc110f09152abf6ab65 100644 (file)
@@ -50,7 +50,7 @@ public class IssueTagsActionTest {
     tester = new WsTester(
       new IssuesWs(new IssueShowAction(null, null, null, null, null, null, null, null, null, null, null),
         new SearchAction(null, null, null, null, null, null, null, null, null, null,null),
-        tagsAction, new SetTagsAction(null), new ComponentTagsAction(null)));
+        tagsAction, new SetTagsAction(null), new ComponentTagsAction(null), new AuthorsAction(null)));
   }
 
   @Test
index 4c71a10c75d0826ec1e23000c1c53aa5d8736fc4..f3d0d3e73f9e879ffe78480bbbe0652952be0915 100644 (file)
@@ -61,7 +61,7 @@ public class IssuesWsTest {
     SearchAction searchAction = new SearchAction(mock(DbClient.class), mock(IssueChangeDao.class), mock(IssueService.class), mock(IssueActionsWriter.class),
       mock(IssueQueryService.class), mock(RuleService.class),
       mock(ActionPlanService.class), mock(UserFinder.class), mock(I18n.class), mock(Durations.class), mock(Languages.class));
-    tester = new WsTester(new IssuesWs(showAction, searchAction, new TagsAction(null), new SetTagsAction(null), new ComponentTagsAction(null)));
+    tester = new WsTester(new IssuesWs(showAction, searchAction, new TagsAction(null), new SetTagsAction(null), new ComponentTagsAction(null), new AuthorsAction(null)));
   }
 
   @Test
@@ -70,7 +70,7 @@ public class IssuesWsTest {
     assertThat(controller).isNotNull();
     assertThat(controller.description()).isNotEmpty();
     assertThat(controller.since()).isEqualTo("3.6");
-    assertThat(controller.actions()).hasSize(17);
+    assertThat(controller.actions()).hasSize(18);
   }
 
   @Test
index abaf650afb6c5cafb9eb99e5aac4a90a8447a258..2dd3a9f2bf34d1d08f37ab13ff045e6bbffeea32 100644 (file)
@@ -50,7 +50,7 @@ public class SetTagsActionTest {
     tester = new WsTester(
       new IssuesWs(new IssueShowAction(null, null, null, null, null, null, null, null, null, null, null),
         new SearchAction(null, null, null, null, null, null, null, null, null, null,null),
-        new TagsAction(null), setTagsAction, new ComponentTagsAction(null)));
+        new TagsAction(null), setTagsAction, new ComponentTagsAction(null), new AuthorsAction(null)));
   }
 
   @Test
index c56aeb5d441ae18bc2fd83a9ec395647a9da38f1..897ad17e38607b66465e01f06860fc3061f9de64 100644 (file)
@@ -39,7 +39,9 @@ import java.util.Map;
 import static com.google.common.collect.Lists.newArrayList;
 import static com.google.common.collect.Maps.newHashMap;
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 @RunWith(MockitoJUnitRunner.class)
 public class UpdateActionTest {
index ac816d868e1853a44cc6905a65220e1263148bd6..4a0b093b327ce3a1c2f17434419718cc65ba71a2 100644 (file)
@@ -83,5 +83,4 @@ public class UsersWsTest {
     assertThat(action.handler()).isInstanceOf(RailsHandler.class);
     assertThat(action.params()).hasSize(2);
   }
-
 }
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/AuthorsActionTest/authors.json b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/AuthorsActionTest/authors.json
new file mode 100644 (file)
index 0000000..190c076
--- /dev/null
@@ -0,0 +1,6 @@
+{
+  "authors": [
+    "luke.skywalker",
+    "luke@skywalker.name"
+  ]
+}