]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-10356 clean message when searching for more than 1000 projects
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Tue, 20 Feb 2018 12:49:56 +0000 (13:49 +0100)
committerSimon Brandhof <simon.brandhof@sonarsource.com>
Thu, 22 Feb 2018 08:23:27 +0000 (09:23 +0100)
server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchAction.java
server/sonar-server/src/test/java/org/sonar/server/project/ws/SearchActionTest.java

index 03a235e2c61a09ad4d91c713447408f7109b1970..e248082b8aa906b415479a30c816ad4f5c722be2 100644 (file)
@@ -30,6 +30,7 @@ import org.sonar.api.server.ws.WebService;
 import org.sonar.api.server.ws.WebService.Param;
 import org.sonar.api.utils.Paging;
 import org.sonar.core.util.stream.MoreCollectors;
+import org.sonar.db.DatabaseUtils;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.component.ComponentDto;
@@ -43,6 +44,7 @@ import org.sonarqube.ws.WsProjects.SearchWsResponse;
 import org.sonarqube.ws.client.project.SearchWsRequest;
 
 import static com.google.common.base.Preconditions.checkArgument;
+import static java.lang.String.format;
 import static org.sonar.api.resources.Qualifiers.APP;
 import static org.sonar.api.resources.Qualifiers.PROJECT;
 import static org.sonar.api.resources.Qualifiers.VIEW;
@@ -90,7 +92,9 @@ public class SearchAction implements ProjectsWsAction {
       .setResponseExample(getClass().getResource("search-example.json"))
       .setHandler(this);
 
-    action.setChangelog(new Change("6.4", "The 'uuid' field is deprecated in the response"));
+    action.setChangelog(
+      new Change("6.4", "The 'uuid' field is deprecated in the response"),
+      new Change("6.7.2", format("Parameters %s and %s accept maximum %d values", PARAM_PROJECTS, PARAM_PROJECT_IDS, DatabaseUtils.PARTITION_SIZE_FOR_ORACLE)));
 
     action.createParam(Param.TEXT_QUERY)
       .setDescription("Limit search to: <ul>" +
@@ -130,6 +134,9 @@ public class SearchAction implements ProjectsWsAction {
       .createParam(PARAM_PROJECTS)
       .setDescription("Comma-separated list of project keys")
       .setSince("6.6")
+      // Limitation of ComponentDao#selectByQuery(), max 1000 values are accepted.
+      // Restricting size of HTTP parameter allows to not fail with SQL error
+      .setMaxValuesAllowed(DatabaseUtils.PARTITION_SIZE_FOR_ORACLE)
       .setExampleValue(String.join(",", KEY_PROJECT_EXAMPLE_001, KEY_PROJECT_EXAMPLE_002));
 
     action
@@ -138,6 +145,9 @@ public class SearchAction implements ProjectsWsAction {
       .setSince("6.6")
       // parameter added to match api/projects/bulk_delete parameters
       .setDeprecatedSince("6.6")
+      // Limitation of ComponentDao#selectByQuery(), max 1000 values are accepted.
+      // Restricting size of HTTP parameter allows to not fail with SQL error
+      .setMaxValuesAllowed(DatabaseUtils.PARTITION_SIZE_FOR_ORACLE)
       .setExampleValue(String.join(",", UUID_EXAMPLE_01, UUID_EXAMPLE_02));
   }
 
index 9f122fe9febfe08385497f41de7f5754df1ef8b6..f301ae1b7722f3ba3dc28f2a9a4969a219c653dc 100644 (file)
 package org.sonar.server.project.ws;
 
 import com.google.common.base.Joiner;
-import java.io.IOException;
-import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Date;
 import java.util.List;
 import org.assertj.core.api.Assertions;
@@ -97,7 +96,7 @@ public class SearchActionTest {
     new SearchAction(db.getDbClient(), userSession, new ProjectsWsSupport(db.getDbClient(), defaultOrganizationProvider, mock(BillingValidationsProxy.class))));
 
   @Test
-  public void search_by_key_query_with_partial_match_case_insensitive() throws IOException {
+  public void search_by_key_query_with_partial_match_case_insensitive() {
     userSession.addPermission(ADMINISTER, db.getDefaultOrganization());
     db.components().insertComponents(
       ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization()).setDbKey("project-_%-key"),
@@ -134,7 +133,7 @@ public class SearchActionTest {
   }
 
   @Test
-  public void search_projects_when_no_qualifier_set() throws IOException {
+  public void search_projects_when_no_qualifier_set() {
     userSession.addPermission(ADMINISTER, db.getDefaultOrganization());
     db.components().insertComponents(
       ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization()).setDbKey(PROJECT_KEY_1),
@@ -146,7 +145,7 @@ public class SearchActionTest {
   }
 
   @Test
-  public void search_projects() throws IOException {
+  public void search_projects() {
     userSession.addPermission(ADMINISTER, db.getDefaultOrganization());
     ComponentDto project = ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization()).setDbKey(PROJECT_KEY_1);
     ComponentDto module = newModuleDto(project);
@@ -163,7 +162,7 @@ public class SearchActionTest {
   }
 
   @Test
-  public void search_views() throws IOException {
+  public void search_views() {
     userSession.addPermission(ADMINISTER, db.getDefaultOrganization());
     db.components().insertComponents(
       ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization()).setDbKey(PROJECT_KEY_1),
@@ -175,7 +174,7 @@ public class SearchActionTest {
   }
 
   @Test
-  public void search_projects_and_views() throws IOException {
+  public void search_projects_and_views() {
     userSession.addPermission(ADMINISTER, db.getDefaultOrganization());
     db.components().insertComponents(
       ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization()).setDbKey(PROJECT_KEY_1),
@@ -187,7 +186,7 @@ public class SearchActionTest {
   }
 
   @Test
-  public void search_on_default_organization_when_no_organization_set() throws IOException {
+  public void search_on_default_organization_when_no_organization_set() {
     userSession.addPermission(ADMINISTER, db.getDefaultOrganization());
     OrganizationDto otherOrganization = db.organizations().insert();
     db.components().insertComponents(
@@ -201,7 +200,7 @@ public class SearchActionTest {
   }
 
   @Test
-  public void search_for_projects_on_given_organization() throws IOException {
+  public void search_for_projects_on_given_organization() {
     OrganizationDto organization1 = db.organizations().insert();
     OrganizationDto organization2 = db.organizations().insert();
     userSession.addPermission(ADMINISTER, organization1);
@@ -245,7 +244,7 @@ public class SearchActionTest {
   }
 
   @Test
-  public void result_is_paginated() throws IOException {
+  public void result_is_paginated() {
     userSession.addPermission(ADMINISTER, db.getDefaultOrganization());
     List<ComponentDto> componentDtoList = new ArrayList<>();
     for (int i = 1; i <= 9; i++) {
@@ -305,7 +304,27 @@ public class SearchActionTest {
   }
 
   @Test
-  public void fail_when_not_system_admin() throws Exception {
+  public void request_throws_IAE_if_more_than_1000_projects() {
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("'projects' can contains only 1000 values, got 1001");
+
+    call(SearchWsRequest.builder()
+      .setProjects(Collections.nCopies(1_001, "foo"))
+      .build());
+  }
+
+  @Test
+  public void request_throws_IAE_if_more_than_1000_project_ids() {
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("'projectIds' can contains only 1000 values, got 1001");
+
+    call(SearchWsRequest.builder()
+      .setProjectIds(Collections.nCopies(1_001, "foo"))
+      .build());
+  }
+
+  @Test
+  public void fail_when_not_system_admin() {
     userSession.addPermission(ADMINISTER_QUALITY_PROFILES, db.getDefaultOrganization());
     expectedException.expect(ForbiddenException.class);
 
@@ -313,14 +332,14 @@ public class SearchActionTest {
   }
 
   @Test
-  public void fail_on_unknown_organization() throws Exception {
+  public void fail_on_unknown_organization() {
     expectedException.expect(NotFoundException.class);
 
     call(SearchWsRequest.builder().setOrganization("unknown").build());
   }
 
   @Test
-  public void fail_on_invalid_qualifier() throws Exception {
+  public void fail_on_invalid_qualifier() {
     userSession.addPermission(ADMINISTER_QUALITY_PROFILES, db.getDefaultOrganization());
     expectedException.expect(IllegalArgumentException.class);
     expectedException.expectMessage("Value of parameter 'qualifiers' (BRC) must be one of: [TRK, VW, APP]");
@@ -387,7 +406,7 @@ public class SearchActionTest {
   }
 
   @Test
-  public void json_example() throws URISyntaxException, IOException {
+  public void json_example() {
     OrganizationDto organization = db.organizations().insertForKey("my-org-1");
     userSession.addPermission(ADMINISTER, organization);
     ComponentDto publicProject = newPrivateProjectDto(organization, "project-uuid-1").setName("Project Name 1").setDbKey("project-key-1").setPrivate(false);