]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9332 api/issues/tags searches across organizations
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Fri, 1 Dec 2017 15:34:41 +0000 (16:34 +0100)
committerTeryk Bellahsene <teryk@users.noreply.github.com>
Mon, 4 Dec 2017 15:23:51 +0000 (16:23 +0100)
server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java
server/sonar-server/src/main/java/org/sonar/server/issue/ws/TagsAction.java
server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex.java
server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/ws/TagsActionTest.java
tests/src/test/java/org/sonarqube/tests/issue/IssueTagsTest.java

index ba7cd670e53ef29ac94ac05061086bf245be3c60..1affc61ee1a4dc70f92c2154c54a0957e8b545f5 100644 (file)
@@ -595,18 +595,22 @@ public class IssueIndex {
         .subAggregation(facetTopAggregation));
   }
 
-  public List<String> listTags(OrganizationDto organization, @Nullable String textQuery, int size) {
+  public List<String> listTags(@Nullable OrganizationDto organization, @Nullable String textQuery, int size) {
     int maxPageSize = 500;
     checkArgument(size <= maxPageSize, "Page size must be lower than or equals to " + maxPageSize);
     if (size <= 0) {
       return emptyList();
     }
 
+    BoolQueryBuilder esQuery = boolQuery()
+      .filter(createAuthorizationFilter(true));
+    if (organization != null) {
+      esQuery.filter(termQuery(FIELD_ISSUE_ORGANIZATION_UUID, organization.getUuid()));
+    }
+
     SearchRequestBuilder requestBuilder = client
       .prepareSearch(INDEX_TYPE_ISSUE)
-      .setQuery(boolQuery()
-        .filter(createAuthorizationFilter(true))
-        .filter(termQuery(FIELD_ISSUE_ORGANIZATION_UUID, organization.getUuid())))
+      .setQuery(esQuery)
       .setSize(0);
 
     TermsAggregationBuilder termsAggregation = AggregationBuilders.terms(AGGREGATION_NAME_FOR_TAGS)
index 0359656f0ed0476332a95c3376a58da633e4bff5..f208ca97812753f94934ec4b82ab548de990d4a9 100644 (file)
@@ -23,9 +23,9 @@ import com.google.common.io.Resources;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
-import java.util.Optional;
 import java.util.SortedSet;
 import java.util.TreeSet;
+import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
@@ -37,10 +37,9 @@ import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.organization.OrganizationDto;
 import org.sonar.server.issue.index.IssueIndex;
-import org.sonar.server.organization.DefaultOrganizationProvider;
 import org.sonar.server.rule.index.RuleIndex;
-import org.sonar.server.ws.WsUtils;
 
+import static org.sonar.server.ws.WsUtils.checkFoundWithOptional;
 import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_ORGANIZATION;
 
 /**
@@ -52,13 +51,11 @@ public class TagsAction implements IssuesWsAction {
   private final IssueIndex issueIndex;
   private final RuleIndex ruleIndex;
   private final DbClient dbClient;
-  private final DefaultOrganizationProvider defaultOrganizationProvider;
 
-  public TagsAction(IssueIndex issueIndex, RuleIndex ruleIndex, DbClient dbClient, DefaultOrganizationProvider defaultOrganizationProvider) {
+  public TagsAction(IssueIndex issueIndex, RuleIndex ruleIndex, DbClient dbClient) {
     this.issueIndex = issueIndex;
     this.ruleIndex = ruleIndex;
     this.dbClient = dbClient;
-    this.defaultOrganizationProvider = defaultOrganizationProvider;
   }
 
   private static void writeResponse(Response response, List<String> tags) {
@@ -95,7 +92,7 @@ public class TagsAction implements IssuesWsAction {
     writeResponse(response, tags);
   }
 
-  private List<String> listTags(OrganizationDto organization, @Nullable String textQuery, int pageSize) {
+  private List<String> listTags(@Nullable OrganizationDto organization, @Nullable String textQuery, int pageSize) {
     Collection<String> issueTags = issueIndex.listTags(organization, textQuery, pageSize);
     Collection<String> ruleTags = ruleIndex.listTags(organization, textQuery, pageSize);
 
@@ -106,13 +103,11 @@ public class TagsAction implements IssuesWsAction {
     return resultAsList.size() > pageSize && pageSize > 0 ? resultAsList.subList(0, pageSize) : resultAsList;
   }
 
+  @CheckForNull
   private OrganizationDto getOrganization(@Nullable String organizationKey) {
     try (DbSession dbSession = dbClient.openSession(false)) {
-      String organizationOrDefaultKey = Optional.ofNullable(organizationKey)
-        .orElseGet(defaultOrganizationProvider.get()::getKey);
-      return WsUtils.checkFoundWithOptional(
-        dbClient.organizationDao().selectByKey(dbSession, organizationOrDefaultKey),
-        "No organization with key '%s'", organizationOrDefaultKey);
+      return organizationKey == null ? null
+        : checkFoundWithOptional(dbClient.organizationDao().selectByKey(dbSession, organizationKey), "No organization with key '%s'", organizationKey);
     }
   }
 
index 87e6454194aa1bd42b4db2112d58555e8c140bcd..a7a76dd3c9e6fcf9ba93c94cc5f7b1ea165fe0dd 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.server.rule.index;
 
 import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -547,17 +548,21 @@ public class RuleIndex {
     return EsUtils.termsKeys(esResponse.getAggregations().get(AGGREGATION_NAME));
   }
 
-  public List<String> listTags(OrganizationDto organization, @Nullable String query, int size) {
+  public List<String> listTags(@Nullable OrganizationDto organization, @Nullable String query, int size) {
     int maxPageSize = 500;
     checkArgument(size <= maxPageSize, "Page size must be lower than or equals to " + maxPageSize);
     if (size <= 0) {
       return emptyList();
     }
 
+    ImmutableList.Builder<String> scopes = ImmutableList.<String>builder()
+      .add(RuleExtensionScope.system().getScope());
+    if (organization != null) {
+      scopes.add(RuleExtensionScope.organization(organization).getScope());
+    }
     TermsQueryBuilder scopeFilter = QueryBuilders.termsQuery(
       FIELD_RULE_EXTENSION_SCOPE,
-      RuleExtensionScope.system().getScope(),
-      RuleExtensionScope.organization(organization).getScope());
+      scopes.build().toArray(new String[0]));
 
     TermsAggregationBuilder termsAggregation = AggregationBuilders.terms(AGGREGATION_NAME_FOR_TAGS)
       .field(FIELD_RULE_EXTENSION_TAGS)
index 25cf465791579928d6954500c4058e5c23f3b94d..7cc4dce101df64fbd6284c747a39a4e4703a4958 100644 (file)
@@ -1200,9 +1200,11 @@ public class IssueIndexTest {
     ruleIndexer.commitAndIndex(db.getSession(), asList(r1.getKey(), r2.getKey()));
 
     OrganizationDto org = db.organizations().insert();
+    OrganizationDto anotherOrg = db.organizations().insert();
     ComponentDto project = ComponentTesting.newPrivateProjectDto(newOrganizationDto());
     ComponentDto file = newFileDto(project, null);
     indexIssues(
+      newDoc("I42", file).setOrganizationUuid(anotherOrg.getUuid()).setRuleKey(r1.getKey().toString()).setTags(of("another")),
       newDoc("I1", file).setOrganizationUuid(org.getUuid()).setRuleKey(r1.getKey().toString()).setTags(of("convention", "java8", "bug")),
       newDoc("I2", file).setOrganizationUuid(org.getUuid()).setRuleKey(r1.getKey().toString()).setTags(of("convention", "bug")),
       newDoc("I3", file).setOrganizationUuid(org.getUuid()).setRuleKey(r2.getKey().toString()),
@@ -1214,6 +1216,7 @@ public class IssueIndexTest {
     assertThat(underTest.listTags(org, null, 1)).containsOnly("bug");
     assertThat(underTest.listTags(org, null, 100)).containsOnly("convention", "java8", "bug");
     assertThat(underTest.listTags(org, "invalidRegexp[", 100)).isEmpty();
+    assertThat(underTest.listTags(null, null, 100)).containsExactlyInAnyOrder("another", "convention", "java8", "bug");
   }
 
   @Test
index 3241e73902d0d37bf444645d0d97e082d6689886..f2c36f9c23ee444a888e7052826b5baafa1c2732 100644 (file)
@@ -37,7 +37,6 @@ 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.IssueIteratorFactory;
-import org.sonar.server.organization.TestDefaultOrganizationProvider;
 import org.sonar.server.permission.index.AuthorizationTypeSupport;
 import org.sonar.server.permission.index.PermissionIndexerDao;
 import org.sonar.server.permission.index.PermissionIndexerTester;
@@ -70,7 +69,7 @@ public class TagsActionTest {
   private IssueIndex issueIndex = new IssueIndex(esTester.client(), System2.INSTANCE, userSession, new AuthorizationTypeSupport(userSession));
   private RuleIndex ruleIndex = new RuleIndex(esTester.client(), System2.INSTANCE);
 
-  private WsActionTester ws = new WsActionTester(new TagsAction(issueIndex, ruleIndex, dbTester.getDbClient(), TestDefaultOrganizationProvider.from(dbTester)));
+  private WsActionTester ws = new WsActionTester(new TagsAction(issueIndex, ruleIndex, dbTester.getDbClient()));
   private OrganizationDto organization;
 
   @Before
@@ -172,6 +171,19 @@ public class TagsActionTest {
     assertJson(result).isSimilarTo("{\"tags\":[]}");
   }
 
+  @Test
+  public void without_organization_parameter_is_cross_organization() {
+    userSession.logIn();
+    OrganizationDto organization = dbTester.organizations().insert();
+    OrganizationDto anotherOrganization = dbTester.organizations().insert();
+    insertIssueWithBrowsePermission(organization, insertRuleWithoutTags(), "tag1");
+    insertIssueWithBrowsePermission(anotherOrganization, insertRuleWithoutTags(), "tag2");
+
+    String result = ws.newRequest().execute().getInput();
+
+    assertJson(result).isSimilarTo("{\"tags\":[\"tag1\", \"tag2\"]}");
+  }
+
   @Test
   public void json_example() throws Exception {
     userSession.logIn();
@@ -223,12 +235,21 @@ public class TagsActionTest {
     return dbTester.rules().insert(setSystemTags());
   }
 
+  private void insertIssueWithBrowsePermission(OrganizationDto organization, RuleDefinitionDto rule, String... tags) {
+    IssueDto issue = insertIssueWithoutBrowsePermission(organization, rule, tags);
+    grantAccess(issue);
+  }
+
   private void insertIssueWithBrowsePermission(RuleDefinitionDto rule, String... tags) {
     IssueDto issue = insertIssueWithoutBrowsePermission(rule, tags);
     grantAccess(issue);
   }
 
   private IssueDto insertIssueWithoutBrowsePermission(RuleDefinitionDto rule, String... tags) {
+    return insertIssueWithoutBrowsePermission(organization, rule, tags);
+  }
+
+  private IssueDto insertIssueWithoutBrowsePermission(OrganizationDto organization, RuleDefinitionDto rule, String... tags) {
     IssueDto issue = dbTester.issues().insertIssue(organization, i -> i.setRule(rule).setTags(asList(tags)));
     ComponentDto project = dbTester.getDbClient().componentDao().selectByUuid(dbTester.getSession(), issue.getProjectUuid()).get();
     userSession.addProjectPermission(USER, project);
index 2e4f00e1885aef5295de727f2d7db588e9bff5ca..caf1401cffc9534f1a75659ae40dc837d2aeefcd 100644 (file)
@@ -21,7 +21,6 @@ package org.sonarqube.tests.issue;
 
 import com.sonar.orchestrator.Orchestrator;
 import com.sonar.orchestrator.build.SonarScanner;
-import org.sonarqube.tests.Category6Suite;
 import java.util.ArrayList;
 import java.util.List;
 import javax.annotation.Nullable;
@@ -30,7 +29,9 @@ import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
 import org.sonarqube.qa.util.Tester;
+import org.sonarqube.tests.Category6Suite;
 import org.sonarqube.ws.Organizations.Organization;
+import org.sonarqube.ws.Projects.CreateWsResponse;
 import org.sonarqube.ws.Users.CreateWsResponse.User;
 import org.sonarqube.ws.client.issue.SearchRequest;
 import org.sonarqube.ws.client.permission.AddUserRequest;
@@ -38,6 +39,7 @@ import org.sonarqube.ws.client.project.CreateRequest;
 import util.ItUtils;
 
 import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
 import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
 import static org.assertj.core.api.Assertions.assertThat;
 import static util.ItUtils.newProjectKey;
@@ -73,14 +75,14 @@ public class IssueTagsTest {
         .setName(randomAlphabetic(10))
         .setVisibility("private")
         .build());
-    analyzeProject(projectKey);
+    analyzeProject(organization.getKey(), projectKey);
 
     String issue = tester.wsClient().issues().search(new SearchRequest()).getIssues(0).getKey();
     tester.wsClient().issues().setTags(issue, "bla", "blubb");
 
     String[] publicTags = {"bad-practice", "convention", "pitfall"};
     String[] privateTags = {"bad-practice", "bla", "blubb", "convention", "pitfall"};
-    String defaultOrganization = null;
+    String defaultOrganization = tester.organizations().getDefaultOrganization().getKey();
 
     // anonymous must not see custom tags of private project
     {
@@ -106,6 +108,24 @@ public class IssueTagsTest {
     }
   }
 
+  @Test
+  public void tags_across_organizations() {
+    Organization organization = tester.organizations().generate();
+    Organization anotherOrganization = tester.organizations().generate();
+    restoreProfile(orchestrator, IssueTagsTest.class.getResource("/issue/one-issue-per-line-profile.xml"), organization.getKey());
+    restoreProfile(orchestrator, IssueTagsTest.class.getResource("/issue/one-issue-per-line-profile.xml"), anotherOrganization.getKey());
+    CreateWsResponse.Project project = tester.projects().provision(organization);
+    CreateWsResponse.Project anotherProject = tester.projects().provision(anotherOrganization);
+    analyzeProject(organization.getKey(), project.getKey());
+    analyzeProject(anotherOrganization.getKey(), anotherProject.getKey());
+    String issue = tester.wsClient().issues().search(new SearchRequest().setProjectKeys(singletonList(project.getKey()))).getIssues(0).getKey();
+    String anotherIssue = tester.wsClient().issues().search(new SearchRequest().setProjectKeys(singletonList(anotherProject.getKey()))).getIssues(0).getKey();
+    tester.wsClient().issues().setTags(issue, "first-tag");
+    tester.wsClient().issues().setTags(anotherIssue, "another-tag");
+
+    assertThat(tester.wsClient().issues().getTags(null).content()).contains("first-tag", "another-tag");
+  }
+
   private void addMemberToOrganization(User member) {
     tester.organizations().service().addMember(organization.getKey(), member.getLogin());
   }
@@ -130,10 +150,10 @@ public class IssueTagsTest {
           expectedTags);
   }
 
-  private void analyzeProject(String projectKey) {
+  private void analyzeProject(String organizationKey, String projectKey) {
     List<String> keyValueProperties = new ArrayList<>(asList(
       "sonar.projectKey", projectKey,
-      "sonar.organization", organization.getKey(),
+      "sonar.organization", organizationKey,
       "sonar.profile", "one-issue-per-line-profile",
       "sonar.login", "admin", "sonar.password", "admin",
       "sonar.scm.disabled", "false"));