]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5893 Add internal web service to list n most violated tags on a component
authorJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>
Wed, 7 Jan 2015 16:33:27 +0000 (17:33 +0100)
committerJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>
Wed, 7 Jan 2015 16:41:23 +0000 (17:41 +0100)
13 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/ComponentTagsAction.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-component-tags.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/ComponentTagsActionTest.java [new file with mode: 0644]
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/resources/org/sonar/server/issue/ws/ComponentTagsActionTest/component-tags.json [new file with mode: 0644]

index 83d0785d475e8e2db208297fbf9528bab241186e..2974678533ae3bf572dccf204570e5ac18fef3a4 100644 (file)
@@ -370,4 +370,8 @@ public class IssueService implements ServerComponent {
       session.close();
     }
   }
+
+  public Map<String, Long> listTagsForComponent(String componentUuid, int pageSize) {
+    return indexClient.get(IssueIndex.class).listTagsForComponent(componentUuid, pageSize);
+  }
 }
index 0a4f530099eb35ac91b4286907e90393a9a6a3db..c443c82115833bfdab9c5b39d27f778cc386af91 100644 (file)
@@ -61,6 +61,7 @@ import org.sonar.server.user.UserSession;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Date;
 import java.util.List;
@@ -272,32 +273,38 @@ public class IssueIndex extends BaseIndex<Issue, FakeIssueDto, String> {
   }
 
   private void addComponentRelatedFilters(IssueQuery query, Map<String, FilterBuilder> filters) {
+    Collection<String> componentUuids = query.componentUuids();
     if (BooleanUtils.isTrue(query.onComponentOnly())) {
       Set<String> allComponents = Sets.newHashSet();
       allComponents.addAll(query.projectUuids());
       allComponents.addAll(query.moduleUuids());
-      allComponents.addAll(query.componentUuids());
+      allComponents.addAll(componentUuids);
       filters.put(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, matchFilter(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, allComponents));
     } else {
       filters.put(IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID, matchFilter(IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID, query.projectUuids()));
       filters.put(IssueIndexDefinition.FIELD_ISSUE_MODULE_UUID, matchFilter(IssueIndexDefinition.FIELD_ISSUE_MODULE_UUID, query.moduleUuids()));
 
-      FilterBuilder componentFilter = matchFilter(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, query.componentUuids());
-      FilterBuilder modulePathFilter = matchFilter(IssueIndexDefinition.FIELD_ISSUE_MODULE_PATH, query.componentUuids());
-      FilterBuilder compositeFilter = null;
-      if (componentFilter != null) {
-        if (modulePathFilter != null) {
-          compositeFilter = FilterBuilders.orFilter(componentFilter, modulePathFilter);
-        } else {
-          compositeFilter = componentFilter;
-        }
-      } else if (modulePathFilter != null) {
-        compositeFilter = modulePathFilter;
-      }
+      FilterBuilder compositeFilter = componentFilter(componentUuids);
       filters.put(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, compositeFilter);
     }
   }
 
+  private FilterBuilder componentFilter(Collection<String> componentUuids) {
+    FilterBuilder componentFilter = matchFilter(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, componentUuids);
+    FilterBuilder modulePathFilter = matchFilter(IssueIndexDefinition.FIELD_ISSUE_MODULE_PATH, componentUuids);
+    FilterBuilder compositeFilter = null;
+    if (componentFilter != null) {
+      if (modulePathFilter != null) {
+        compositeFilter = FilterBuilders.orFilter(componentFilter, modulePathFilter);
+      } else {
+        compositeFilter = componentFilter;
+      }
+    } else if (modulePathFilter != null) {
+      compositeFilter = modulePathFilter;
+    }
+    return compositeFilter;
+  }
+
   private FilterBuilder getAuthorizationFilter(QueryContext options) {
     String user = options.getUserLogin();
     Set<String> groups = options.getUserGroups();
@@ -504,4 +511,25 @@ public class IssueIndex extends BaseIndex<Issue, FakeIssueDto, String> {
       }
     });
   }
+
+  public Map<String, Long> listTagsForComponent(String componentUuid, int pageSize) {
+    SearchRequestBuilder count = getClient().prepareSearch(IssueIndexDefinition.INDEX)
+      .setTypes(IssueIndexDefinition.TYPE_ISSUE)
+      .setQuery(QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(),
+        FilterBuilders.boolFilter()
+          .must(getAuthorizationFilter(new QueryContext()))
+          .must(componentFilter(Arrays.asList(componentUuid)))));
+    TermsBuilder aggreg = AggregationBuilders.terms("_ref")
+      .field(IssueIndexDefinition.FIELD_ISSUE_TAGS)
+      .size(pageSize)
+      .order(Order.count(false))
+      .minDocCount(1L);
+    Terms result = count.addAggregation(aggreg).get().getAggregations().get("_ref");
+
+    Map<String, Long> map = Maps.newHashMap();
+    for (Bucket bucket: result.getBuckets()) {
+      map.put(bucket.getKey(), bucket.getDocCount());
+    }
+    return map;
+  }
 }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/ComponentTagsAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/ComponentTagsAction.java
new file mode 100644 (file)
index 0000000..e642686
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * 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.Request;
+import org.sonar.api.server.ws.RequestHandler;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.server.ws.WebService.NewAction;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.server.issue.IssueService;
+
+import java.util.Map;
+
+/**
+ * List issue tags matching a given query.
+ * @since 5.1
+ */
+public class ComponentTagsAction implements RequestHandler {
+
+  private final IssueService service;
+
+  public ComponentTagsAction(IssueService service) {
+    this.service = service;
+  }
+
+  void define(WebService.NewController controller) {
+    NewAction action = controller.createAction("component_tags")
+      .setHandler(this)
+      .setSince("5.1")
+      .setInternal(true)
+      .setDescription("List tags for the issues under a given component (including issues on the descendants of the component)")
+      .setResponseExample(Resources.getResource(getClass(), "example-component-tags.json"));
+    action.createParam("componentUuid")
+      .setDescription("A component UUID")
+      .setRequired(true)
+      .setExampleValue("7d8749e8-3070-4903-9188-bdd82933bb92");
+    action.createParam("ps")
+      .setDescription("The maximum size of the list to return")
+      .setExampleValue("25")
+      .setDefaultValue("10");
+  }
+
+  @Override
+  public void handle(Request request, Response response) throws Exception {
+    String componentUuid = request.mandatoryParam("componentUuid");
+    int pageSize = request.mandatoryParamAsInt("ps");
+    JsonWriter json = response.newJsonWriter().beginObject().name("tags").beginArray();
+    for (Map.Entry<String, Long> tag : service.listTagsForComponent(componentUuid, pageSize).entrySet()) {
+      json.beginObject()
+        .prop("key", tag.getKey())
+        .prop("value", tag.getValue())
+        .endObject();
+    }
+    json.endArray()
+      .endObject()
+      .close();
+  }
+
+}
index c17a6f782bb7d85213d323147ce780b74ecdb280..ef3146df4d6c584b14d460a3d574465ee997c231 100644 (file)
@@ -46,12 +46,14 @@ public class IssuesWs implements WebService {
   private final SearchAction esSearchAction;
   private final TagsAction tagsAction;
   private final SetTagsAction setTagsAction;
+  private final ComponentTagsAction componentTagsAction;
 
-  public IssuesWs(IssueShowAction showAction, SearchAction searchAction, TagsAction tagsAction, SetTagsAction setTagsAction) {
+  public IssuesWs(IssueShowAction showAction, SearchAction searchAction, TagsAction tagsAction, SetTagsAction setTagsAction, ComponentTagsAction componentTagsAction) {
     this.showAction = showAction;
     this.esSearchAction = searchAction;
     this.tagsAction = tagsAction;
     this.setTagsAction = setTagsAction;
+    this.componentTagsAction = componentTagsAction;
   }
 
   @Override
@@ -64,6 +66,7 @@ public class IssuesWs implements WebService {
     esSearchAction.define(controller);
     tagsAction.define(controller);
     setTagsAction.define(controller);
+    componentTagsAction.define(controller);
 
     defineChangelogAction(controller);
     defineAssignAction(controller);
index 5f2acb4df47d1cf370e5befb5291910a6e1c0bb5..033e11142621d75e3f8e7c6c912840245fa1d66f 100644 (file)
@@ -54,7 +54,13 @@ 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.*;
+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.PreviewDatabaseFactory;
+import org.sonar.core.persistence.SemaphoreUpdater;
+import org.sonar.core.persistence.SemaphoresImpl;
 import org.sonar.core.preview.PreviewCache;
 import org.sonar.core.profiling.Profiling;
 import org.sonar.core.purge.PurgeProfiler;
@@ -80,7 +86,11 @@ 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.*;
+import org.sonar.server.batch.BatchIndex;
+import org.sonar.server.batch.BatchWs;
+import org.sonar.server.batch.GlobalReferentialsAction;
+import org.sonar.server.batch.ProjectReferentialsAction;
+import org.sonar.server.batch.ProjectReferentialsLoader;
 import org.sonar.server.charts.ChartFactory;
 import org.sonar.server.component.ComponentCleanerService;
 import org.sonar.server.component.ComponentService;
@@ -88,14 +98,31 @@ 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.*;
-import org.sonar.server.computation.*;
+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.computation.AnalysisReportQueue;
+import org.sonar.server.computation.AnalysisReportService;
+import org.sonar.server.computation.AnalysisReportTaskCleaner;
+import org.sonar.server.computation.AnalysisReportTaskLauncher;
+import org.sonar.server.computation.ComputationService;
+import org.sonar.server.computation.ComputeEngineIssueStorageFactory;
 import org.sonar.server.computation.db.AnalysisReportDao;
-import org.sonar.server.computation.step.*;
-import org.sonar.server.computation.ws.QueueWsAction;
-import org.sonar.server.computation.ws.HistoryWsAction;
+import org.sonar.server.computation.step.CleanReportStep;
+import org.sonar.server.computation.step.ComponentIndexationInDatabaseStep;
+import org.sonar.server.computation.step.ComputationStepRegistry;
+import org.sonar.server.computation.step.DataCleanerStep;
+import org.sonar.server.computation.step.DigestReportStep;
+import org.sonar.server.computation.step.IndexProjectIssuesStep;
+import org.sonar.server.computation.step.InvalidatePreviewCacheStep;
+import org.sonar.server.computation.step.SwitchSnapshotStep;
+import org.sonar.server.computation.step.SynchronizeProjectPermissionsStep;
 import org.sonar.server.computation.ws.ComputationWebService;
+import org.sonar.server.computation.ws.HistoryWsAction;
 import org.sonar.server.computation.ws.IsQueueEmptyWsAction;
+import org.sonar.server.computation.ws.QueueWsAction;
 import org.sonar.server.computation.ws.SubmitReportWsAction;
 import org.sonar.server.config.ws.PropertiesWs;
 import org.sonar.server.dashboard.db.DashboardDao;
@@ -108,7 +135,14 @@ 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.*;
+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.design.FileDesignWidget;
 import org.sonar.server.duplication.ws.DuplicationsJsonWriter;
 import org.sonar.server.duplication.ws.DuplicationsParser;
@@ -116,14 +150,34 @@ 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.*;
+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.actionplan.ActionPlanService;
 import org.sonar.server.issue.actionplan.ActionPlanWs;
 import org.sonar.server.issue.db.IssueDao;
 import org.sonar.server.issue.filter.IssueFilterService;
 import org.sonar.server.issue.filter.IssueFilterWriter;
 import org.sonar.server.issue.filter.IssueFilterWs;
-import org.sonar.server.issue.index.*;
+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.index.IssueNormalizer;
+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;
@@ -146,34 +200,118 @@ 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.*;
+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.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.*;
-import org.sonar.server.qualityprofile.*;
+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.qualityprofile.db.ActiveRuleDao;
 import org.sonar.server.qualityprofile.index.ActiveRuleIndex;
 import org.sonar.server.qualityprofile.index.ActiveRuleNormalizer;
-import org.sonar.server.qualityprofile.ws.*;
-import org.sonar.server.rule.*;
+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.rule.db.RuleDao;
 import org.sonar.server.rule.index.RuleIndex;
 import org.sonar.server.rule.index.RuleNormalizer;
-import org.sonar.server.rule.ws.*;
-import org.sonar.server.search.*;
+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.RuleMapping;
+import org.sonar.server.rule.ws.RulesWebService;
+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.source.HtmlSourceDecorator;
 import org.sonar.server.source.IndexSourceLinesStep;
 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.*;
+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.ShowAction;
-import org.sonar.server.startup.*;
+import org.sonar.server.source.ws.SourcesWs;
+import org.sonar.server.startup.CleanPreviewAnalysisCache;
+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.test.CoverageService;
-import org.sonar.server.test.ws.*;
+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.text.MacroInterpreter;
 import org.sonar.server.text.RubyTextService;
 import org.sonar.server.ui.JRubyI18n;
@@ -181,7 +319,14 @@ 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.*;
+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.db.GroupDao;
 import org.sonar.server.user.db.UserDao;
 import org.sonar.server.user.db.UserGroupDao;
@@ -191,7 +336,13 @@ 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.*;
+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.ws.ListingWs;
 import org.sonar.server.ws.WebServiceEngine;
 
@@ -525,6 +676,7 @@ class ServerComponents {
     pico.addSingleton(org.sonar.server.issue.ws.SearchAction.class);
     pico.addSingleton(org.sonar.server.issue.ws.TagsAction.class);
     pico.addSingleton(SetTagsAction.class);
+    pico.addSingleton(ComponentTagsAction.class);
     pico.addSingleton(IssueService.class);
     pico.addSingleton(IssueActionsWriter.class);
     pico.addSingleton(IssueQueryService.class);
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/issue/ws/example-component-tags.json b/server/sonar-server/src/main/resources/org/sonar/server/issue/ws/example-component-tags.json
new file mode 100644 (file)
index 0000000..3e00535
--- /dev/null
@@ -0,0 +1,7 @@
+{
+  "tags": [
+    { key: "convention", value: 42 },
+    { key: "cwe", value: 12 },
+    { key: "security", value: 5 }
+  ]
+}
\ No newline at end of file
index 9d9f0b25bfbaa394a5940103e5f4aaebb128cfdf..eccf43c8f4a5ba6ff5703f7aa434506d7e9e7229 100644 (file)
@@ -68,6 +68,7 @@ import java.util.Map;
 
 import static org.fest.assertions.Assertions.assertThat;
 import static org.fest.assertions.Fail.fail;
+import static org.fest.assertions.MapAssert.entry;
 
 public class IssueServiceMediumTest {
 
@@ -488,4 +489,19 @@ public class IssueServiceMediumTest {
     service.setTags(issue.getKey(), ImmutableSet.<String>of());
     assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).tags()).isEmpty();
   }
+
+  @Test
+  public void list_component_tags() {
+    db.issueDao().insert(session,
+      IssueTesting.newDto(rule, file, project).setTags(ImmutableSet.of("convention", "java8", "bug")),
+      IssueTesting.newDto(rule, file, project).setTags(ImmutableSet.of("convention", "bug")),
+      IssueTesting.newDto(rule, file, project),
+      IssueTesting.newDto(rule, file, project).setTags(ImmutableSet.of("convention")));
+    session.commit();
+    index();
+
+    assertThat(service.listTagsForComponent(project.uuid(), 5)).includes(entry("convention", 3L), entry("bug", 2L), entry("java8", 1L));
+    assertThat(service.listTagsForComponent(project.uuid(), 2)).includes(entry("convention", 3L), entry("bug", 2L)).excludes(entry("java8", 1L));
+    assertThat(service.listTagsForComponent("other", 10)).isEmpty();
+  }
 }
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/ComponentTagsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/ComponentTagsActionTest.java
new file mode 100644 (file)
index 0000000..1dd86e2
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * 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.collect.ImmutableMap;
+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.Action;
+import org.sonar.api.server.ws.WebService.Param;
+import org.sonar.server.issue.IssueService;
+import org.sonar.server.ws.WsTester;
+
+import java.util.Map;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ComponentTagsActionTest {
+
+  @Mock
+  private IssueService service;
+
+  private ComponentTagsAction componentTagsAction;
+
+  private WsTester tester;
+
+  @Before
+  public void setUp() {
+    componentTagsAction = new ComponentTagsAction(service);
+    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));
+  }
+
+  @Test
+  public void should_define() throws Exception {
+    Action action = tester.controller("api/issues").action("component_tags");
+    assertThat(action.description()).isNotEmpty();
+    assertThat(action.responseExampleAsString()).isNotEmpty();
+    assertThat(action.isPost()).isFalse();
+    assertThat(action.isInternal()).isTrue();
+    assertThat(action.handler()).isEqualTo(componentTagsAction);
+    assertThat(action.params()).hasSize(2);
+
+    Param query = action.param("componentUuid");
+    assertThat(query.isRequired()).isTrue();
+    assertThat(query.description()).isNotEmpty();
+    assertThat(query.exampleValue()).isNotEmpty();
+    Param pageSize = action.param("ps");
+    assertThat(pageSize.isRequired()).isFalse();
+    assertThat(pageSize.defaultValue()).isEqualTo("10");
+    assertThat(pageSize.description()).isNotEmpty();
+    assertThat(pageSize.exampleValue()).isNotEmpty();
+  }
+
+  @Test
+  public void should_return_empty_list() throws Exception {
+    tester.newGetRequest("api/issues", "component_tags").setParam("componentUuid", "polop").execute().assertJson("{tags:[]}");
+  }
+
+  @Test
+  public void should_return_tag_list() throws Exception {
+    Map<String, Long> tags = ImmutableMap.<String, Long>builder()
+      .put("convention", 2771L)
+      .put("brain-overload", 998L)
+      .put("cwe", 89L)
+      .put("bug", 32L)
+      .put("cert", 2L)
+      .build();
+    when(service.listTagsForComponent("polop", 5)).thenReturn(tags);
+    tester.newGetRequest("api/issues", "component_tags").setParam("componentUuid", "polop").setParam("ps", "5").execute()
+      .assertJson(getClass(), "component-tags.json");
+    verify(service).listTagsForComponent("polop", 5);
+  }
+}
index b791878ab8d73efad392bb7522a049a0ecffa23c..799c7f266a80543fe9f13fed0e7ed09cd680a19a 100644 (file)
@@ -150,7 +150,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 TagsAction(null), new SetTagsAction(null), new ComponentTagsAction(null)
       ));
   }
 
index 3e5cca639bba7c6f676a8a29175778984edc7c29..dda5f3d4f8185c350f08ce2a70cd47bb618b445b 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)));
+        tagsAction, new SetTagsAction(null), new ComponentTagsAction(null)));
   }
 
   @Test
index 8c3f3d0e4bab5621002649a680899e50f93c93a4..42d276ae99b1fc435b8b54f14d3f910c10b89324 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)));
+    tester = new WsTester(new IssuesWs(showAction, searchAction, new TagsAction(null), new SetTagsAction(null), new ComponentTagsAction(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(16);
+    assertThat(controller.actions()).hasSize(17);
   }
 
   @Test
index f54e1f6adee4caf27fdfaff25ec37827a4b530d2..5c9c9abcb7c1f42e0ae3de770853eafc5a5eb78e 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 TagsAction(null), setTagsAction, new ComponentTagsAction(null)));
   }
 
   @Test
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/ComponentTagsActionTest/component-tags.json b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/ComponentTagsActionTest/component-tags.json
new file mode 100644 (file)
index 0000000..38e018c
--- /dev/null
@@ -0,0 +1,9 @@
+{
+  tags: [
+    { key: "convention", value: 2771 },
+    { key: "brain-overload", value: 998 },
+    { key: "cwe", value: 89 },
+    { key: "bug", value: 32 },
+    { key: "cert", value: 2 }
+  ]
+}
\ No newline at end of file