]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6089 Allow to search Issues by Views / Sub-Views
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 29 Jan 2015 07:20:35 +0000 (08:20 +0100)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 29 Jan 2015 14:44:04 +0000 (15:44 +0100)
server/sonar-server/src/main/java/org/sonar/server/issue/IssueQuery.java
server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java
server/sonar-server/src/test/java/org/sonar/server/component/ComponentTesting.java
server/sonar-server/src/test/java/org/sonar/server/computation/step/PurgeRemovedViewsStepTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java

index dea020a2f4141b16971bd95765250fef83a89f44..5116a2054a5dbc5534bc3c07e5338e747926bb9f 100644 (file)
@@ -63,6 +63,7 @@ public class IssueQuery {
   private final Collection<String> projects;
   private final Collection<String> directories;
   private final Collection<String> files;
+  private final Collection<String> views;
   private final Collection<RuleKey> rules;
   private final Collection<String> actionPlans;
   private final Collection<String> reporters;
@@ -93,6 +94,7 @@ public class IssueQuery {
     this.projects = defaultCollection(builder.projects);
     this.directories = defaultCollection(builder.directories);
     this.files = defaultCollection(builder.files);
+    this.views = defaultCollection(builder.views);
     this.rules = defaultCollection(builder.rules);
     this.actionPlans = defaultCollection(builder.actionPlans);
     this.reporters = defaultCollection(builder.reporters);
@@ -153,6 +155,10 @@ public class IssueQuery {
     return files;
   }
 
+  public Collection<String> viewUuids() {
+    return views;
+  }
+
   public Collection<RuleKey> rules() {
     return rules;
   }
@@ -260,6 +266,7 @@ public class IssueQuery {
     private Collection<String> projects;
     private Collection<String> directories;
     private Collection<String> files;
+    private Collection<String> views;
     private Collection<RuleKey> rules;
     private Collection<String> actionPlans;
     private Collection<String> reporters;
@@ -332,6 +339,11 @@ public class IssueQuery {
       return this;
     }
 
+    public Builder viewUuids(@Nullable Collection<String> l) {
+      this.views = l;
+      return this;
+    }
+
     public Builder rules(@Nullable Collection<RuleKey> rules) {
       this.rules = rules;
       return this;
index 11603bf01d60627544d307d8207ec897d7e7a95a..42414efe6dbe669773b1e363b7a385ca6a2b41e3 100644 (file)
@@ -45,6 +45,7 @@ import org.sonar.server.issue.IssueQuery;
 import org.sonar.server.issue.filter.IssueFilterParameters;
 import org.sonar.server.search.*;
 import org.sonar.server.user.UserSession;
+import org.sonar.server.view.index.ViewIndexDefinition;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
@@ -258,7 +259,6 @@ public class IssueIndex extends BaseIndex<Issue, FakeIssueDto, String> {
   }
 
   private void addComponentRelatedFilters(IssueQuery query, Map<String, FilterBuilder> filters) {
-
     FilterBuilder projectFilter = matchFilter(IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID, query.projectUuids());
     FilterBuilder moduleRootFilter = moduleRootFilter(query.moduleRootUuids());
     FilterBuilder moduleFilter = matchFilter(IssueIndexDefinition.FIELD_ISSUE_MODULE_UUID, query.moduleUuids());
@@ -266,6 +266,7 @@ public class IssueIndex extends BaseIndex<Issue, FakeIssueDto, String> {
     FilterBuilder directoryFilter = matchFilter(IssueIndexDefinition.FIELD_ISSUE_DIRECTORY_PATH, query.directories());
     FilterBuilder fileFilter = matchFilter(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, query.fileUuids());
     FilterBuilder componentFilter = matchFilter(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, query.componentUuids());
+    FilterBuilder viewFilter = viewFilter(query.viewUuids());
 
     if (BooleanUtils.isTrue(query.isContextualized())) {
       if (projectFilter != null) {
@@ -292,14 +293,15 @@ public class IssueIndex extends BaseIndex<Issue, FakeIssueDto, String> {
       filters.put(IssueIndexDefinition.FIELD_ISSUE_MODULE_UUID, moduleFilter);
       filters.put(IssueIndexDefinition.FIELD_ISSUE_DIRECTORY_PATH, directoryFilter);
       filters.put(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, fileFilter);
+      filters.put("view", viewFilter);
     }
   }
 
+  @CheckForNull
   private FilterBuilder moduleRootFilter(Collection<String> componentUuids) {
-    if (componentUuids == null || componentUuids.isEmpty()) {
+    if (componentUuids.isEmpty()) {
       return null;
     }
-
     FilterBuilder componentFilter = matchFilter(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, componentUuids);
     FilterBuilder modulePathFilter = matchFilter(IssueIndexDefinition.FIELD_ISSUE_MODULE_PATH, componentUuids);
     FilterBuilder compositeFilter = null;
@@ -315,6 +317,7 @@ public class IssueIndex extends BaseIndex<Issue, FakeIssueDto, String> {
     return compositeFilter;
   }
 
+  @CheckForNull
   private FilterBuilder directoryFilter(Collection<String> moduleUuids, Collection<String> directoryPaths) {
     BoolFilterBuilder directoryTop = null;
     FilterBuilder moduleFilter = matchFilter(IssueIndexDefinition.FIELD_ISSUE_MODULE_UUID, moduleUuids);
@@ -332,6 +335,23 @@ public class IssueIndex extends BaseIndex<Issue, FakeIssueDto, String> {
     return directoryTop;
   }
 
+  @CheckForNull
+  private FilterBuilder viewFilter(Collection<String> viewUuids) {
+    if (viewUuids.isEmpty()) {
+      return null;
+    }
+
+    OrFilterBuilder viewsFilter = FilterBuilders.orFilter();
+    for (String viewUuid : viewUuids) {
+      viewsFilter.add(FilterBuilders.termsLookupFilter(IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID)
+        .lookupIndex(ViewIndexDefinition.INDEX)
+        .lookupType(ViewIndexDefinition.TYPE_VIEW)
+        .lookupId(viewUuid)
+        .lookupPath(ViewIndexDefinition.FIELD_PROJECTS));
+    }
+    return viewsFilter;
+  }
+
   private FilterBuilder getAuthorizationFilter(QueryContext options) {
     String user = options.getUserLogin();
     Set<String> groups = options.getUserGroups();
@@ -513,8 +533,8 @@ public class IssueIndex extends BaseIndex<Issue, FakeIssueDto, String> {
   }
 
   @CheckForNull
-  private FilterBuilder matchFilter(String field, @Nullable Collection<?> values) {
-    if (values != null && !values.isEmpty()) {
+  private FilterBuilder matchFilter(String field, Collection<?> values) {
+    if (!values.isEmpty()) {
       return FilterBuilders.termsFilter(field, values);
     } else {
       return null;
index a5f007bee0592d4e221fa02e9a5f5e5af3291ec5..5d6091370cd95df40cc3dec214edc4d21daf9819 100644 (file)
@@ -34,7 +34,7 @@ public class ComponentTesting {
   }
 
   public static ComponentDto newFileDto(ComponentDto module, String fileUuid) {
-    return newComponent(module, fileUuid)
+    return newComponent(fileUuid, module)
       .setKey("KEY_" + fileUuid)
       .setName("NAME_" + fileUuid)
       .setLongName("LONG_NAME_" + fileUuid)
@@ -46,7 +46,7 @@ public class ComponentTesting {
 
   public static ComponentDto newDirectory(ComponentDto module, String path) {
     String uuid = Uuids.create();
-    return newComponent(module, uuid)
+    return newComponent(uuid, module)
       .setKey(!path.equals("/") ? module.getKey() + ":" + path : module.getKey() + ":/")
       .setName(path)
       .setLongName(path)
@@ -55,12 +55,8 @@ public class ComponentTesting {
       .setQualifier(Qualifiers.DIRECTORY);
   }
 
-  public static ComponentDto newModuleDto(ComponentDto subProjectOrProject) {
-    return newModuleDto(subProjectOrProject, Uuids.create());
-  }
-
-  public static ComponentDto newModuleDto(ComponentDto subProjectOrProject, String uuid) {
-    return newComponent(subProjectOrProject, uuid)
+  public static ComponentDto newModuleDto(String uuid, ComponentDto subProjectOrProject) {
+    return newComponent(uuid, subProjectOrProject)
       .setKey("KEY_" + uuid)
       .setName("NAME_" + uuid)
       .setLongName("LONG_NAME_" + uuid)
@@ -70,6 +66,10 @@ public class ComponentTesting {
       .setLanguage(null);
   }
 
+  public static ComponentDto newModuleDto(ComponentDto subProjectOrProject) {
+    return newModuleDto(Uuids.create(), subProjectOrProject);
+  }
+
   public static ComponentDto newProjectDto() {
     return newProjectDto(Uuids.create());
   }
@@ -90,7 +90,7 @@ public class ComponentTesting {
       .setEnabled(true);
   }
 
-  private static ComponentDto newComponent(ComponentDto module, String uuid) {
+  private static ComponentDto newComponent(String uuid, ComponentDto module) {
     return new ComponentDto()
       .setUuid(uuid)
       .setProjectUuid(module.projectUuid())
index 0f1de03316ed3209387b729e3ba89cb3475a0408..ed96ecb547d81c2f14688397e412fbb4fbfeefb3 100644 (file)
@@ -85,7 +85,7 @@ public class PurgeRemovedViewsStepTest {
       new ViewDoc().setUuid("CDEF").getFields());
 
     ComponentDto view = ComponentTesting.newProjectDto("ABCD").setQualifier(Qualifiers.VIEW);
-    ComponentDto subView = ComponentTesting.newModuleDto(view, "BCDE").setQualifier(Qualifiers.SUBVIEW);
+    ComponentDto subView = ComponentTesting.newModuleDto("BCDE", view).setQualifier(Qualifiers.SUBVIEW);
     dbClient.componentDao().insert(session, view, subView);
     session.commit();
 
index 0fc89a0186d8731cbd5c4494faae8b8a5c2f1be7..1f5f1b6eb00732eaf758851be7399ee74520411f 100644 (file)
@@ -21,6 +21,8 @@ package org.sonar.server.issue.index;
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterators;
+import org.elasticsearch.action.bulk.BulkRequestBuilder;
+import org.elasticsearch.action.index.IndexRequest;
 import org.junit.Before;
 import org.junit.ClassRule;
 import org.junit.Test;
@@ -32,6 +34,7 @@ import org.sonar.api.utils.DateUtils;
 import org.sonar.api.utils.KeyValueFormat;
 import org.sonar.core.component.ComponentDto;
 import org.sonar.server.component.ComponentTesting;
+import org.sonar.server.es.EsClient;
 import org.sonar.server.exceptions.NotFoundException;
 import org.sonar.server.issue.IssueQuery;
 import org.sonar.server.issue.IssueTesting;
@@ -40,6 +43,8 @@ import org.sonar.server.search.QueryContext;
 import org.sonar.server.search.Result;
 import org.sonar.server.tester.ServerTester;
 import org.sonar.server.user.MockUserSession;
+import org.sonar.server.view.index.ViewDoc;
+import org.sonar.server.view.index.ViewIndexDefinition;
 
 import javax.annotation.Nullable;
 
@@ -290,6 +295,32 @@ public class IssueIndexMediumTest {
     assertThat(result.getFacets().get("directories")).containsOnly(new FacetValue("/src/main/xoo", 1), new FacetValue("/", 1));
   }
 
+  @Test
+  public void filter_by_views() throws Exception {
+    ComponentDto project1 = ComponentTesting.newProjectDto();
+    ComponentDto file1 = ComponentTesting.newFileDto(project1);
+    ComponentDto project2 = ComponentTesting.newProjectDto();
+    indexIssues(
+      // Project1 has 2 issues (one on a file and one on the project itself)
+      IssueTesting.newDoc("ISSUE1", project1),
+      IssueTesting.newDoc("ISSUE2", file1),
+      // Project2 has 1 issue
+      IssueTesting.newDoc("ISSUE3", project2));
+
+    // The view1 is containing 2 issues from project1
+    String view1 = "ABCD";
+    indexView(view1, newArrayList(project1.uuid()));
+
+    // The view2 is containing 1 issue from project2
+    String view2 = "CDEF";
+    indexView(view2, newArrayList(project2.uuid()));
+
+    assertThat(index.search(IssueQuery.builder().viewUuids(newArrayList(view1)).build(), new QueryContext()).getHits()).hasSize(2);
+    assertThat(index.search(IssueQuery.builder().viewUuids(newArrayList(view2)).build(), new QueryContext()).getHits()).hasSize(1);
+    assertThat(index.search(IssueQuery.builder().viewUuids(newArrayList(view1, view2)).build(), new QueryContext()).getHits()).hasSize(3);
+    assertThat(index.search(IssueQuery.builder().viewUuids(newArrayList("unknown")).build(), new QueryContext()).getHits()).isEmpty();
+  }
+
   @Test
   public void filter_by_severities() throws Exception {
     ComponentDto project = ComponentTesting.newProjectDto();
@@ -952,4 +983,10 @@ public class IssueIndexMediumTest {
     tester.get(IssueAuthorizationIndexer.class).index(newArrayList(new IssueAuthorizationDao.Dto(projectUuid, 1).addGroup(group).addUser(user)));
   }
 
+  private void indexView(String viewUuid, List<String> projects) {
+    EsClient client = tester.get(EsClient.class);
+    BulkRequestBuilder bulk = client.prepareBulk().setRefresh(true);
+    bulk.add(new IndexRequest(ViewIndexDefinition.INDEX, ViewIndexDefinition.TYPE_VIEW).source(new ViewDoc().setUuid(viewUuid).setProjects(projects).getFields()));
+    bulk.get();
+  }
 }