From 8446f5652a6bd0977b7a6c9b681e7af4d3ec0a74 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Thu, 22 Jan 2015 14:05:05 +0100 Subject: [PATCH] SONAR-5904 Return directories facet in Issues search WS --- .../sonar/server/issue/index/IssueIndex.java | 28 +-- .../server/issue/index/IssueNormalizer.java | 74 ------- .../sonar/server/issue/ws/SearchAction.java | 1 + .../server/platform/ServerComponents.java | 188 ++---------------- .../org/sonar/server/search/FacetValue.java | 8 + .../issue/index/IssueIndexMediumTest.java | 137 +++++++++++++ .../issue/ws/SearchActionMediumTest.java | 19 ++ .../display_directory_facet.json | 13 ++ 8 files changed, 213 insertions(+), 255 deletions(-) delete mode 100644 server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueNormalizer.java create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/display_directory_facet.json diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java index 963afc6c05b..4ee92aac528 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java @@ -181,7 +181,7 @@ public class IssueIndex extends BaseIndex { setFacets(query, options, filters, esQuery, esSearch); SearchResponse response = esSearch.get(); - return new Result(this, response); + return new Result<>(this, response); } private BoolFilterBuilder getFilter(IssueQuery query, QueryContext options) { @@ -196,7 +196,7 @@ public class IssueIndex extends BaseIndex { public void deleteClosedIssuesOfProjectBefore(String uuid, Date beforeDate) { FilterBuilder projectFilter = FilterBuilders.boolFilter().must(FilterBuilders.termsFilter(IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID, uuid)); - FilterBuilder dateFilter = FilterBuilders.rangeFilter(IssueNormalizer.IssueField.ISSUE_CLOSE_DATE.field()).lt(beforeDate.getTime()); + FilterBuilder dateFilter = FilterBuilders.rangeFilter(IssueIndexDefinition.FIELD_ISSUE_FUNC_CLOSED_AT).lt(beforeDate.getTime()); QueryBuilder queryBuilder = QueryBuilders.filteredQuery( QueryBuilders.matchAllQuery(), FilterBuilders.andFilter(projectFilter, dateFilter) @@ -334,22 +334,24 @@ public class IssueIndex extends BaseIndex { StickyFacetBuilder stickyFacetBuilder = stickyFacetBuilder(esQuery, filters); // Execute Term aggregations addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, - IssueFilterParameters.SEVERITIES, IssueNormalizer.IssueField.SEVERITY.field(), Severity.ALL.toArray()); + IssueFilterParameters.SEVERITIES, IssueIndexDefinition.FIELD_ISSUE_SEVERITY, Severity.ALL.toArray()); addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, - IssueFilterParameters.STATUSES, IssueNormalizer.IssueField.STATUS.field(), Issue.STATUSES.toArray()); + IssueFilterParameters.STATUSES, IssueIndexDefinition.FIELD_ISSUE_STATUS, Issue.STATUSES.toArray()); addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, - IssueFilterParameters.COMPONENT_UUIDS, IssueNormalizer.IssueField.COMPONENT.field(), query.componentUuids().toArray()); + IssueFilterParameters.COMPONENT_UUIDS, IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, query.componentUuids().toArray()); addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, - IssueFilterParameters.PROJECT_UUIDS, IssueNormalizer.IssueField.PROJECT.field(), query.projectUuids().toArray()); + IssueFilterParameters.PROJECT_UUIDS, IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID, query.projectUuids().toArray()); addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, - IssueFilterParameters.LANGUAGES, IssueNormalizer.IssueField.LANGUAGE.field(), query.languages().toArray()); + IssueFilterParameters.DIRECTORIES, IssueIndexDefinition.FIELD_ISSUE_DIRECTORY_PATH, query.directories().toArray()); addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, - IssueFilterParameters.RULES, IssueNormalizer.IssueField.RULE_KEY.field(), query.rules().toArray()); + IssueFilterParameters.LANGUAGES, IssueIndexDefinition.FIELD_ISSUE_LANGUAGE, query.languages().toArray()); addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, - IssueFilterParameters.REPORTERS, IssueNormalizer.IssueField.REPORTER.field()); + IssueFilterParameters.RULES, IssueIndexDefinition.FIELD_ISSUE_RULE_KEY, query.rules().toArray()); + addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch, + IssueFilterParameters.REPORTERS, IssueIndexDefinition.FIELD_ISSUE_REPORTER); if (options.facets().contains(IssueFilterParameters.TAGS)) { - esSearch.addAggregation(stickyFacetBuilder.buildStickyFacet(IssueNormalizer.IssueField.TAGS.field(), IssueFilterParameters.TAGS, TAGS_FACET_SIZE, query.tags().toArray())); + esSearch.addAggregation(stickyFacetBuilder.buildStickyFacet(IssueIndexDefinition.FIELD_ISSUE_TAGS, IssueFilterParameters.TAGS, TAGS_FACET_SIZE, query.tags().toArray())); } if (options.facets().contains(IssueFilterParameters.RESOLUTIONS)) { @@ -372,7 +374,7 @@ public class IssueIndex extends BaseIndex { } private AggregationBuilder getAssigneesFacet(IssueQuery query, Map filters, QueryBuilder esQuery) { - String fieldName = IssueNormalizer.IssueField.ASSIGNEE.field(); + String fieldName = IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE; String facetName = IssueFilterParameters.ASSIGNEES; // Same as in super.stickyFacetBuilder @@ -403,7 +405,7 @@ public class IssueIndex extends BaseIndex { } private AggregationBuilder getResolutionFacet(Map filters, QueryBuilder esQuery) { - String fieldName = IssueNormalizer.IssueField.RESOLUTION.field(); + String fieldName = IssueIndexDefinition.FIELD_ISSUE_RESOLUTION; String facetName = IssueFilterParameters.RESOLUTIONS; // Same as in super.stickyFacetBuilder @@ -428,7 +430,7 @@ public class IssueIndex extends BaseIndex { } private AggregationBuilder getActionPlansFacet(IssueQuery query, Map filters, QueryBuilder esQuery) { - String fieldName = IssueNormalizer.IssueField.ACTION_PLAN.field(); + String fieldName = IssueIndexDefinition.FIELD_ISSUE_ACTION_PLAN; String facetName = IssueFilterParameters.ACTION_PLANS; // Same as in super.stickyFacetBuilder diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueNormalizer.java b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueNormalizer.java deleted file mode 100644 index e0d8e6d2ffd..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueNormalizer.java +++ /dev/null @@ -1,74 +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.index; - -import org.elasticsearch.action.update.UpdateRequest; -import org.sonar.core.persistence.Dto; -import org.sonar.server.db.DbClient; -import org.sonar.server.search.BaseNormalizer; -import org.sonar.server.search.IndexField; -import org.sonar.server.search.Indexable; - -import java.util.List; - -public class IssueNormalizer extends BaseNormalizer { - - public IssueNormalizer(DbClient db) { - super(db); - } - - @Override - public List normalize(Dto dto) { - throw new UnsupportedOperationException(); - } - - public static final class IssueField extends Indexable { - - public static final IndexField KEY = addSortable(IndexField.Type.STRING, "key"); - public static final IndexField UPDATED_AT = add(IndexField.Type.DATE, "updatedAt"); - - public static final IndexField PROJECT = addSortable(IndexField.Type.STRING, "project"); - public static final IndexField COMPONENT = add(IndexField.Type.STRING, "component"); - public static final IndexField MODULE = add(IndexField.Type.STRING, "module"); - public static final IndexField MODULE_PATH = add(IndexField.Type.UUID_PATH, "modulePath"); - - public static final IndexField ACTION_PLAN = add(IndexField.Type.STRING, "actionPlan"); - public static final IndexField ASSIGNEE = addSortable(IndexField.Type.STRING, "assignee"); - public static final IndexField ATTRIBUTES = add(IndexField.Type.STRING, "attributes"); - public static final IndexField AUTHOR_LOGIN = add(IndexField.Type.STRING, "authorLogin"); - public static final IndexField DEBT = add(IndexField.Type.LONG, "debt"); - public static final IndexField EFFORT = add(IndexField.Type.DOUBLE, "effort"); - public static final IndexField ISSUE_CREATED_AT = addSortable(IndexField.Type.DATE, "issueCreatedAt"); - public static final IndexField ISSUE_UPDATED_AT = addSortable(IndexField.Type.DATE, "issueUpdatedAt"); - public static final IndexField ISSUE_CLOSE_DATE = addSortable(IndexField.Type.DATE, "issueClosedAt"); - public static final IndexField LINE = addSortable(IndexField.Type.INTEGER, "line"); - public static final IndexField MESSAGE = add(IndexField.Type.STRING, "message"); - public static final IndexField RESOLUTION = add(IndexField.Type.STRING, "resolution"); - public static final IndexField REPORTER = add(IndexField.Type.STRING, "reporter"); - public static final IndexField STATUS = addSortable(IndexField.Type.STRING, "status"); - public static final IndexField SEVERITY = add(IndexField.Type.STRING, "severity"); - public static final IndexField SEVERITY_VALUE = addSortable(IndexField.Type.INTEGER, "severityValue"); - public static final IndexField LANGUAGE = add(IndexField.Type.STRING, "language"); - public static final IndexField TAGS = add(IndexField.Type.STRING, "tags"); - public static final IndexField RULE_KEY = add(IndexField.Type.STRING, "ruleKey"); - public static final IndexField FILE_PATH = addSortable(IndexField.Type.STRING, "filePath"); - } - -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java index 0f615189b8f..58138a592c6 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java @@ -283,6 +283,7 @@ public class SearchAction extends SearchRequestHandler { IssueFilterParameters.ASSIGNEES, IssueFilterParameters.REPORTERS, IssueFilterParameters.COMPONENT_UUIDS, + IssueFilterParameters.DIRECTORIES, IssueFilterParameters.LANGUAGES, IssueFilterParameters.TAGS, }); diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java index c0e2bfda799..9bedf2b28f7 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java @@ -53,13 +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.PreviewDatabaseFactory; -import org.sonar.core.persistence.SemaphoreUpdater; -import org.sonar.core.persistence.SemaphoresImpl; +import org.sonar.core.persistence.*; import org.sonar.core.preview.PreviewCache; import org.sonar.core.profiling.Profiling; import org.sonar.core.purge.PurgeProfiler; @@ -85,12 +79,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; @@ -98,20 +87,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; @@ -123,14 +104,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; @@ -138,22 +112,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; @@ -164,18 +123,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.index.IssueNormalizer; -import org.sonar.server.issue.notification.ChangesOnMyIssueNotificationDispatcher; -import org.sonar.server.issue.notification.IssueChangesEmailTemplate; -import org.sonar.server.issue.notification.IssueNotifications; -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; @@ -196,118 +145,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.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.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; @@ -315,14 +181,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; @@ -332,13 +191,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.ws.ListingWs; import org.sonar.server.ws.WebServiceEngine; @@ -420,7 +273,6 @@ class ServerComponents { ActiveRuleDao.class, // issues - IssueNormalizer.class, IssueIndex.class, IssueDao.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/search/FacetValue.java b/server/sonar-server/src/main/java/org/sonar/server/search/FacetValue.java index af2e132e7db..ad83b07c369 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/search/FacetValue.java +++ b/server/sonar-server/src/main/java/org/sonar/server/search/FacetValue.java @@ -54,4 +54,12 @@ public class FacetValue { public int hashCode() { return key != null ? key.hashCode() : 0; } + + @Override + public String toString() { + return "FacetValue{" + + "key='" + key + '\'' + + ", value=" + value + + '}'; + } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java index 7dce52187eb..af05cb67c1a 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java @@ -147,6 +147,21 @@ public class IssueIndexMediumTest { assertThat(index.search(IssueQuery.builder().projectUuids(newArrayList("unknown")).build(), new QueryContext()).getHits()).isEmpty(); } + @Test + public void facets_on_projects() throws Exception { + ComponentDto project = ComponentTesting.newProjectDto("ABCD"); + ComponentDto project2 = ComponentTesting.newProjectDto("EFGH"); + + indexIssues( + IssueTesting.newDoc("ISSUE1", ComponentTesting.newFileDto(project)), + IssueTesting.newDoc("ISSUE2", ComponentTesting.newFileDto(project)), + IssueTesting.newDoc("ISSUE3", ComponentTesting.newFileDto(project2))); + + Result result = index.search(IssueQuery.builder().build(), new QueryContext().addFacets(newArrayList("projectUuids"))); + assertThat(result.getFacets()).containsOnlyKeys("projectUuids"); + assertThat(result.getFacets().get("projectUuids")).containsOnly(new FacetValue("ABCD", 2), new FacetValue("EFGH", 1)); + } + @Test public void filter_by_modules() throws Exception { ComponentDto project = ComponentTesting.newProjectDto(); @@ -194,6 +209,25 @@ public class IssueIndexMediumTest { assertThat(index.search(IssueQuery.builder().componentUuids(newArrayList("unknown")).build(), new QueryContext()).getHits()).isEmpty(); } + @Test + public void facets_on_components() throws Exception { + ComponentDto project = ComponentTesting.newProjectDto("A"); + ComponentDto file1 = ComponentTesting.newFileDto(project, "ABCD"); + ComponentDto file2 = ComponentTesting.newFileDto(project, "BCDE"); + ComponentDto file3 = ComponentTesting.newFileDto(project, "CDEF"); + + indexIssues( + IssueTesting.newDoc("ISSUE1", project), + IssueTesting.newDoc("ISSUE2", file1), + IssueTesting.newDoc("ISSUE3", file2), + IssueTesting.newDoc("ISSUE4", file2), + IssueTesting.newDoc("ISSUE5", file3)); + + Result result = index.search(IssueQuery.builder().build(), new QueryContext().addFacets(newArrayList("componentUuids"))); + assertThat(result.getFacets()).containsOnlyKeys("componentUuids"); + assertThat(result.getFacets().get("componentUuids")).containsOnly(new FacetValue("A", 1), new FacetValue("ABCD", 1), new FacetValue("BCDE", 2), new FacetValue("CDEF", 1)); + } + @Test public void filter_by_directories() throws Exception { ComponentDto project = ComponentTesting.newProjectDto(); @@ -209,6 +243,21 @@ public class IssueIndexMediumTest { assertThat(index.search(IssueQuery.builder().directories(newArrayList("unknown")).build(), new QueryContext()).getHits()).isEmpty(); } + @Test + public void facets_on_directories() throws Exception { + ComponentDto project = ComponentTesting.newProjectDto(); + ComponentDto file1 = ComponentTesting.newFileDto(project).setPath("src/main/xoo/F1.xoo"); + ComponentDto file2 = ComponentTesting.newFileDto(project).setPath("F2.xoo"); + + indexIssues( + IssueTesting.newDoc("ISSUE1", file1).setDirectoryPath("/src/main/xoo"), + IssueTesting.newDoc("ISSUE2", file2).setDirectoryPath("/")); + + Result result = index.search(IssueQuery.builder().build(), new QueryContext().addFacets(newArrayList("directories"))); + assertThat(result.getFacets()).containsOnlyKeys("directories"); + assertThat(result.getFacets().get("directories")).containsOnly(new FacetValue("/src/main/xoo", 1), new FacetValue("/", 1)); + } + @Test public void filter_by_severities() throws Exception { ComponentDto project = ComponentTesting.newProjectDto(); @@ -223,6 +272,21 @@ public class IssueIndexMediumTest { assertThat(index.search(IssueQuery.builder().severities(newArrayList(Severity.BLOCKER)).build(), new QueryContext()).getHits()).isEmpty(); } + @Test + public void facets_on_severities() throws Exception { + ComponentDto project = ComponentTesting.newProjectDto(); + ComponentDto file = ComponentTesting.newFileDto(project); + + indexIssues( + IssueTesting.newDoc("ISSUE1", file).setSeverity(Severity.INFO), + IssueTesting.newDoc("ISSUE2", file).setSeverity(Severity.INFO), + IssueTesting.newDoc("ISSUE3", file).setSeverity(Severity.MAJOR)); + + Result result = index.search(IssueQuery.builder().build(), new QueryContext().addFacets(newArrayList("severities"))); + assertThat(result.getFacets()).containsOnlyKeys("severities"); + assertThat(result.getFacets().get("severities")).containsOnly(new FacetValue("INFO", 2), new FacetValue("MAJOR", 1)); + } + @Test public void filter_by_statuses() throws Exception { ComponentDto project = ComponentTesting.newProjectDto(); @@ -237,6 +301,21 @@ public class IssueIndexMediumTest { assertThat(index.search(IssueQuery.builder().statuses(newArrayList(Issue.STATUS_CONFIRMED)).build(), new QueryContext()).getHits()).isEmpty(); } + @Test + public void facets_on_statuses() throws Exception { + ComponentDto project = ComponentTesting.newProjectDto(); + ComponentDto file = ComponentTesting.newFileDto(project); + + indexIssues( + IssueTesting.newDoc("ISSUE1", file).setStatus(Issue.STATUS_CLOSED), + IssueTesting.newDoc("ISSUE2", file).setStatus(Issue.STATUS_CLOSED), + IssueTesting.newDoc("ISSUE3", file).setStatus(Issue.STATUS_OPEN)); + + Result result = index.search(IssueQuery.builder().build(), new QueryContext().addFacets(newArrayList("statuses"))); + assertThat(result.getFacets()).containsOnlyKeys("statuses"); + assertThat(result.getFacets().get("statuses")).containsOnly(new FacetValue("CLOSED", 2), new FacetValue("OPEN", 1)); + } + @Test public void filter_by_resolutions() throws Exception { ComponentDto project = ComponentTesting.newProjectDto(); @@ -252,6 +331,21 @@ public class IssueIndexMediumTest { assertThat(index.search(IssueQuery.builder().resolutions(newArrayList(Issue.RESOLUTION_REMOVED)).build(), new QueryContext()).getHits()).isEmpty(); } + @Test + public void facets_on_resolutions() throws Exception { + ComponentDto project = ComponentTesting.newProjectDto(); + ComponentDto file = ComponentTesting.newFileDto(project); + + indexIssues( + IssueTesting.newDoc("ISSUE1", file).setResolution(Issue.RESOLUTION_FALSE_POSITIVE), + IssueTesting.newDoc("ISSUE2", file).setResolution(Issue.RESOLUTION_FALSE_POSITIVE), + IssueTesting.newDoc("ISSUE3", file).setResolution(Issue.RESOLUTION_FIXED)); + + Result result = index.search(IssueQuery.builder().build(), new QueryContext().addFacets(newArrayList("resolutions"))); + assertThat(result.getFacets()).containsOnlyKeys("resolutions"); + assertThat(result.getFacets().get("resolutions")).containsOnly(new FacetValue("FALSE-POSITIVE", 2), new FacetValue("FIXED", 1)); + } + @Test public void filter_by_resolved() throws Exception { ComponentDto project = ComponentTesting.newProjectDto(); @@ -282,6 +376,20 @@ public class IssueIndexMediumTest { assertThat(index.search(IssueQuery.builder().actionPlans(newArrayList("unknown")).build(), new QueryContext()).getHits()).isEmpty(); } + @Test + public void facets_on_action_plans() throws Exception { + ComponentDto project = ComponentTesting.newProjectDto(); + ComponentDto file = ComponentTesting.newFileDto(project); + + indexIssues( + IssueTesting.newDoc("ISSUE1", file).setActionPlanKey("plan1"), + IssueTesting.newDoc("ISSUE2", file).setActionPlanKey("plan2")); + + Result result = index.search(IssueQuery.builder().build(), new QueryContext().addFacets(newArrayList("actionPlans"))); + assertThat(result.getFacets()).containsOnlyKeys("actionPlans"); + assertThat(result.getFacets().get("actionPlans")).containsOnly(new FacetValue("plan1", 1), new FacetValue("plan2", 1)); + } + @Test public void filter_by_planned() throws Exception { ComponentDto project = ComponentTesting.newProjectDto(); @@ -322,6 +430,19 @@ public class IssueIndexMediumTest { assertThat(index.search(IssueQuery.builder().languages(newArrayList("unknown")).build(), new QueryContext()).getHits()).isEmpty(); } + @Test + public void facets_on_languages() throws Exception { + ComponentDto project = ComponentTesting.newProjectDto(); + ComponentDto file = ComponentTesting.newFileDto(project); + RuleKey ruleKey = RuleKey.of("repo", "X1"); + + indexIssues(IssueTesting.newDoc("ISSUE1", file).setRuleKey(ruleKey.toString()).setLanguage("xoo")); + + Result result = index.search(IssueQuery.builder().build(), new QueryContext().addFacets(newArrayList("languages"))); + assertThat(result.getFacets()).containsOnlyKeys("languages"); + assertThat(result.getFacets().get("languages")).containsOnly(new FacetValue("xoo", 1)); + } + @Test public void filter_by_assignees() throws Exception { ComponentDto project = ComponentTesting.newProjectDto(); @@ -337,6 +458,22 @@ public class IssueIndexMediumTest { assertThat(index.search(IssueQuery.builder().assignees(newArrayList("unknown")).build(), new QueryContext()).getHits()).isEmpty(); } + @Test + public void facets_on_assignees() throws Exception { + ComponentDto project = ComponentTesting.newProjectDto(); + ComponentDto file = ComponentTesting.newFileDto(project); + + indexIssues( + IssueTesting.newDoc("ISSUE1", file).setAssignee("steph"), + IssueTesting.newDoc("ISSUE2", file).setAssignee("simon"), + IssueTesting.newDoc("ISSUE3", file).setAssignee("simon"), + IssueTesting.newDoc("ISSUE4", file).setAssignee(null)); + + Result result = index.search(IssueQuery.builder().build(), new QueryContext().addFacets(newArrayList("assignees"))); + assertThat(result.getFacets()).containsOnlyKeys("assignees"); + assertThat(result.getFacets().get("assignees")).containsOnly(new FacetValue("steph", 1), new FacetValue("simon", 2), new FacetValue("", 1)); + } + @Test public void filter_by_assigned() throws Exception { ComponentDto project = ComponentTesting.newProjectDto(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionMediumTest.java index b8b7022cb34..e0820eed055 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionMediumTest.java @@ -493,6 +493,25 @@ public class SearchActionMediumTest { result.assertJson(this.getClass(), "display_zero_facets.json", false); } + @Test + public void display_directory_facet() throws Exception { + ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject")); + setDefaultProjectPermission(project); + ComponentDto directory = insertComponent(ComponentTesting.newDirectory(project, "src/main/java/dir")); + ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent").setPath(directory.path() + "/MyComponent.java")); + IssueDto issue = IssueTesting.newDto(newRule(), file, project).setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2"); + db.issueDao().insert(session, issue); + session.commit(); + tester.get(IssueIndexer.class).indexAll(); + + MockUserSession.set().setLogin("john"); + WsTester.Result result = wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION) + .setParam("resolved", "false") + .setParam(SearchAction.PARAM_FACETS, "directories") + .execute(); + result.assertJson(this.getClass(), "display_directory_facet.json", false); + } + @Test public void hide_rules() throws Exception { ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject")); diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/display_directory_facet.json b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/display_directory_facet.json new file mode 100644 index 00000000000..53f067312f7 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/display_directory_facet.json @@ -0,0 +1,13 @@ +{ + "facets": [ + { + "property": "directories", + "values": [ + { + "val": "/src/main/java/dir", + "count": 1 + } + ] + } + ] +} -- 2.39.5