aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Brandhof <simon.brandhof@sonarsource.com>2018-02-13 20:18:49 +0100
committerSimon Brandhof <simon.brandhof@sonarsource.com>2018-02-22 09:44:52 +0100
commit0b2bd218c88655f24bc541086dfee18d3364a9e1 (patch)
tree31528458dc23d762d79f2ca6b526982b0db2eae5
parente353cd6f2bd37ea143d5806e9ee85323cddd1f97 (diff)
downloadsonarqube-0b2bd218c88655f24bc541086dfee18d3364a9e1.tar.gz
sonarqube-0b2bd218c88655f24bc541086dfee18d3364a9e1.zip
SONAR-10356 Oracle error on some WS involving more than 1000 projects
-rw-r--r--server/sonar-db-core/src/main/java/org/sonar/db/DatabaseUtils.java11
-rw-r--r--server/sonar-db-core/src/test/java/org/sonar/db/DatabaseUtilsTest.java16
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java29
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java86
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/permission/ws/template/BulkApplyTemplateAction.java7
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/project/ws/BulkDeleteAction.java22
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchAction.java12
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchMyProjectsAction.java5
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/permission/ws/template/BulkApplyTemplateActionTest.java32
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/project/ws/BulkDeleteActionTest.java21
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/project/ws/SearchActionTest.java23
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/project/ws/SearchMyProjectsActionTest.java32
12 files changed, 262 insertions, 34 deletions
diff --git a/server/sonar-db-core/src/main/java/org/sonar/db/DatabaseUtils.java b/server/sonar-db-core/src/main/java/org/sonar/db/DatabaseUtils.java
index 243436a84a3..9b850e40f65 100644
--- a/server/sonar-db-core/src/main/java/org/sonar/db/DatabaseUtils.java
+++ b/server/sonar-db-core/src/main/java/org/sonar/db/DatabaseUtils.java
@@ -47,6 +47,7 @@ import javax.annotation.Nullable;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
+import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.Lists.newArrayList;
import static java.lang.String.format;
@@ -338,4 +339,14 @@ public class DatabaseUtils {
}
};
}
+
+ /**
+ * @throws IllegalArgumentException if the collection is not null and has strictly more
+ * than {@link #PARTITION_SIZE_FOR_ORACLE} values.
+ */
+ public static void checkThatNotTooManyConditions(@Nullable Collection<?> values, String message) {
+ if (values != null) {
+ checkArgument(values.size() <= PARTITION_SIZE_FOR_ORACLE, message);
+ }
+ }
}
diff --git a/server/sonar-db-core/src/test/java/org/sonar/db/DatabaseUtilsTest.java b/server/sonar-db-core/src/test/java/org/sonar/db/DatabaseUtilsTest.java
index 98c6d1e4661..b32852e37e6 100644
--- a/server/sonar-db-core/src/test/java/org/sonar/db/DatabaseUtilsTest.java
+++ b/server/sonar-db-core/src/test/java/org/sonar/db/DatabaseUtilsTest.java
@@ -332,4 +332,20 @@ public class DatabaseUtilsTest {
assertThat(DatabaseUtils.tableExists("foo", connection)).isFalse();
}
}
+
+ @Test
+ public void checkThatNotTooManyConditions_does_not_fail_if_less_than_1000_conditions() {
+ DatabaseUtils.checkThatNotTooManyConditions(null, "unused");
+ DatabaseUtils.checkThatNotTooManyConditions(Collections.emptySet(), "unused");
+ DatabaseUtils.checkThatNotTooManyConditions(Collections.nCopies(10, "foo"), "unused");
+ DatabaseUtils.checkThatNotTooManyConditions(Collections.nCopies(1_000, "foo"), "unused");
+ }
+
+ @Test
+ public void checkThatNotTooManyConditions_throws_IAE_if_strictly_more_than_1000_conditions() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("the message");
+
+ DatabaseUtils.checkThatNotTooManyConditions(Collections.nCopies(1_001, "foo"), "the message");
+ }
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java
index 04a297326ba..24eb17266ce 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java
@@ -45,6 +45,7 @@ import static java.util.Objects.requireNonNull;
import static org.apache.commons.lang.StringUtils.isBlank;
import static org.sonar.core.util.stream.MoreCollectors.toList;
import static org.sonar.db.DaoDatabaseUtils.buildLikeValue;
+import static org.sonar.db.DatabaseUtils.checkThatNotTooManyConditions;
import static org.sonar.db.DatabaseUtils.executeLargeInputs;
import static org.sonar.db.DatabaseUtils.executeLargeUpdates;
import static org.sonar.db.WildcardPosition.BEFORE_AND_AFTER;
@@ -56,6 +57,7 @@ public class ComponentDao implements Dao {
if (query.hasEmptySetOfComponents()) {
return emptyList();
}
+ checkThatNotTooManyComponents(query);
return mapper(session).selectByQuery(organizationUuid, query, new RowBounds(offset, limit));
}
@@ -63,7 +65,7 @@ public class ComponentDao implements Dao {
if (query.hasEmptySetOfComponents()) {
return 0;
}
-
+ checkThatNotTooManyComponents(query);
return mapper(session).countByQuery(organizationUuid, query);
}
@@ -103,19 +105,37 @@ public class ComponentDao implements Dao {
return componentDto.get();
}
+ /**
+ * Same as {@link #selectByQuery(DbSession, String, ComponentQuery, int, int)} except
+ * that the filter on organization is disabled.
+ */
public List<ComponentDto> selectByQuery(DbSession session, ComponentQuery query, int offset, int limit) {
return selectByQueryImpl(session, null, query, offset, limit);
}
+ /**
+ * @throws IllegalArgumentException if parameter query#getComponentIds() has more than {@link org.sonar.db.DatabaseUtils#PARTITION_SIZE_FOR_ORACLE} values
+ * @throws IllegalArgumentException if parameter query#getComponentKeys() has more than {@link org.sonar.db.DatabaseUtils#PARTITION_SIZE_FOR_ORACLE} values
+ * @throws IllegalArgumentException if parameter query#getComponentUuids() has more than {@link org.sonar.db.DatabaseUtils#PARTITION_SIZE_FOR_ORACLE} values
+ */
public List<ComponentDto> selectByQuery(DbSession dbSession, String organizationUuid, ComponentQuery query, int offset, int limit) {
requireNonNull(organizationUuid, "organizationUuid can't be null");
return selectByQueryImpl(dbSession, organizationUuid, query, offset, limit);
}
+ /**
+ * Same as {@link #countByQuery(DbSession, String, ComponentQuery)} except
+ * that the filter on organization is disabled.
+ */
public int countByQuery(DbSession session, ComponentQuery query) {
return countByQueryImpl(session, null, query);
}
+ /**
+ * @throws IllegalArgumentException if parameter query#getComponentIds() has more than {@link org.sonar.db.DatabaseUtils#PARTITION_SIZE_FOR_ORACLE} values
+ * @throws IllegalArgumentException if parameter query#getComponentKeys() has more than {@link org.sonar.db.DatabaseUtils#PARTITION_SIZE_FOR_ORACLE} values
+ * @throws IllegalArgumentException if parameter query#getComponentUuids() has more than {@link org.sonar.db.DatabaseUtils#PARTITION_SIZE_FOR_ORACLE} values
+ */
public int countByQuery(DbSession session, String organizationUuid, ComponentQuery query) {
requireNonNull(organizationUuid, "organizationUuid can't be null");
return countByQueryImpl(session, organizationUuid, query);
@@ -346,4 +366,11 @@ public class ComponentDao implements Dao {
public List<KeyWithUuidDto> selectComponentKeysHavingIssuesToMerge(DbSession dbSession, String mergeBranchUuid) {
return mapper(dbSession).selectComponentKeysHavingIssuesToMerge(mergeBranchUuid);
}
+
+ private static void checkThatNotTooManyComponents(ComponentQuery query) {
+ checkThatNotTooManyConditions(query.getComponentIds(), "Too many component ids in query");
+ checkThatNotTooManyConditions(query.getComponentKeys(), "Too many component keys in query");
+ checkThatNotTooManyConditions(query.getComponentUuids(), "Too many component UUIDs in query");
+ }
+
}
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java
index a47703e7b60..9dda85ffaff 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java
@@ -27,6 +27,8 @@ import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.LongStream;
import javax.annotation.Nullable;
import org.assertj.core.api.ListAssert;
import org.junit.Rule;
@@ -720,7 +722,7 @@ public class ComponentDaoTest {
}
@Test
- public void select_provisioned() {
+ public void selectByQuery_provisioned() {
OrganizationDto organization = db.organizations().insert();
ComponentDto provisionedProject = db.components()
.insertComponent(newPrivateProjectDto(organization).setDbKey("provisioned.project").setName("Provisioned Project"));
@@ -778,6 +780,51 @@ public class ComponentDaoTest {
}
@Test
+ public void countByQuery_with_organization_throws_NPE_of_organizationUuid_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("organizationUuid can't be null");
+
+ underTest.countByQuery(dbSession, null, ALL_PROJECTS_COMPONENT_QUERY);
+ }
+
+ @Test
+ public void countByQuery_throws_IAE_if_too_many_component_ids() {
+ Set<Long> ids = LongStream.range(0L, 1_010L).boxed().collect(Collectors.toSet());
+ ComponentQuery.Builder query = ComponentQuery.builder()
+ .setQualifiers(Qualifiers.PROJECT)
+ .setComponentIds(ids);
+
+ assertThatCountByQueryThrowsIAE(query, "Too many component ids in query");
+ }
+
+ @Test
+ public void countByQuery_throws_IAE_if_too_many_component_keys() {
+ Set<String> keys = IntStream.range(0, 1_010).mapToObj(String::valueOf).collect(Collectors.toSet());
+ ComponentQuery.Builder query = ComponentQuery.builder()
+ .setQualifiers(Qualifiers.PROJECT)
+ .setComponentKeys(keys);
+
+ assertThatCountByQueryThrowsIAE(query, "Too many component keys in query");
+ }
+
+ @Test
+ public void countByQuery_throws_IAE_if_too_many_component_uuids() {
+ Set<String> uuids = IntStream.range(0, 1_010).mapToObj(String::valueOf).collect(Collectors.toSet());
+ ComponentQuery.Builder query = ComponentQuery.builder()
+ .setQualifiers(Qualifiers.PROJECT)
+ .setComponentUuids(uuids);
+
+ assertThatCountByQueryThrowsIAE(query, "Too many component UUIDs in query");
+ }
+
+ private void assertThatCountByQueryThrowsIAE(ComponentQuery.Builder query, String expectedMessage) {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage(expectedMessage);
+
+ underTest.countByQuery(dbSession, query.build());
+ }
+
+ @Test
public void select_ghost_projects() {
OrganizationDto organization = db.organizations().insert();
@@ -1007,11 +1054,40 @@ public class ComponentDaoTest {
}
@Test
- public void countByQuery_with_organization_throws_NPE_of_organizationUuid_is_null() {
- expectedException.expect(NullPointerException.class);
- expectedException.expectMessage("organizationUuid can't be null");
+ public void selectByQuery_throws_IAE_if_too_many_component_ids() {
+ Set<Long> ids = LongStream.range(0L, 1_010L).boxed().collect(Collectors.toSet());
+ ComponentQuery.Builder query = ComponentQuery.builder()
+ .setQualifiers(Qualifiers.PROJECT)
+ .setComponentIds(ids);
- underTest.countByQuery(dbSession, null, ALL_PROJECTS_COMPONENT_QUERY);
+ assertThatSelectByQueryThrowsIAE(query, "Too many component ids in query");
+ }
+
+ @Test
+ public void selectByQuery_throws_IAE_if_too_many_component_keys() {
+ Set<String> keys = IntStream.range(0, 1_010).mapToObj(String::valueOf).collect(Collectors.toSet());
+ ComponentQuery.Builder query = ComponentQuery.builder()
+ .setQualifiers(Qualifiers.PROJECT)
+ .setComponentKeys(keys);
+
+ assertThatSelectByQueryThrowsIAE(query, "Too many component keys in query");
+ }
+
+ @Test
+ public void selectByQuery_throws_IAE_if_too_many_component_uuids() {
+ Set<String> uuids = IntStream.range(0, 1_010).mapToObj(String::valueOf).collect(Collectors.toSet());
+ ComponentQuery.Builder query = ComponentQuery.builder()
+ .setQualifiers(Qualifiers.PROJECT)
+ .setComponentUuids(uuids);
+
+ assertThatSelectByQueryThrowsIAE(query, "Too many component UUIDs in query");
+ }
+
+ private void assertThatSelectByQueryThrowsIAE(ComponentQuery.Builder query, String expectedMessage) {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage(expectedMessage);
+
+ underTest.selectByQuery(dbSession, query.build(), 0, Integer.MAX_VALUE);
}
@Test
diff --git a/server/sonar-server/src/main/java/org/sonar/server/permission/ws/template/BulkApplyTemplateAction.java b/server/sonar-server/src/main/java/org/sonar/server/permission/ws/template/BulkApplyTemplateAction.java
index df2dada5a2a..a86482a8a9a 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/permission/ws/template/BulkApplyTemplateAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/permission/ws/template/BulkApplyTemplateAction.java
@@ -25,10 +25,12 @@ import java.util.List;
import org.sonar.api.i18n.I18n;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.resources.ResourceTypes;
+import org.sonar.api.server.ws.Change;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.server.ws.WebService.Param;
+import org.sonar.db.DatabaseUtils;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
@@ -45,6 +47,7 @@ import javax.annotation.Nullable;
import static java.util.Collections.singleton;
import static java.util.Objects.requireNonNull;
+import static java.lang.String.format;
import static org.sonar.api.utils.DateUtils.parseDateOrDateTime;
import static org.sonar.core.util.Protobuf.setNullable;
import static org.sonar.server.permission.PermissionPrivilegeChecker.checkGlobalAdmin;
@@ -91,6 +94,7 @@ public class BulkApplyTemplateAction implements PermissionsWsAction {
"Requires the following permission: 'Administer System'.")
.setPost(true)
.setSince("5.5")
+ .setChangelog(new Change("6.7.2", format("Parameter %s accepts maximum %d values", PARAM_PROJECTS, DatabaseUtils.PARTITION_SIZE_FOR_ORACLE)))
.setHandler(this);
action.createParam(Param.TEXT_QUERY)
@@ -110,6 +114,9 @@ public class BulkApplyTemplateAction implements PermissionsWsAction {
.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.createParam(PARAM_VISIBILITY)
diff --git a/server/sonar-server/src/main/java/org/sonar/server/project/ws/BulkDeleteAction.java b/server/sonar-server/src/main/java/org/sonar/server/project/ws/BulkDeleteAction.java
index 95b23b2c663..dbd400435d7 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/project/ws/BulkDeleteAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/project/ws/BulkDeleteAction.java
@@ -19,6 +19,10 @@
*/
package org.sonar.server.project.ws;
+import java.util.List;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.sonar.api.server.ws.Change;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
@@ -32,6 +36,7 @@ import org.sonar.server.component.ComponentCleanerService;
import org.sonar.server.project.Visibility;
import org.sonar.server.user.UserSession;
+import static java.lang.Math.min;
import static org.sonar.api.resources.Qualifiers.APP;
import static org.sonar.api.resources.Qualifiers.PROJECT;
import static org.sonar.api.resources.Qualifiers.VIEW;
@@ -73,7 +78,8 @@ public class BulkDeleteAction implements ProjectsWsAction {
.setDescription("Delete one or several projects.<br />" +
"Requires 'Administer System' permission.")
.setSince("5.2")
- .setHandler(this);
+ .setHandler(this)
+ .setChangelog(new Change("6.7.2", "Only the 1'000 first items in project filters are taken into account"));
support.addOrganizationParam(action);
@@ -85,7 +91,7 @@ public class BulkDeleteAction implements ProjectsWsAction {
action
.createParam(PARAM_PROJECT_IDS)
- .setDescription("Comma-separated list of project ids")
+ .setDescription("Comma-separated list of project ids. Only the 1'000 first ids are used. Others are silently ignored.")
.setDeprecatedKey("ids", "6.4")
.setDeprecatedSince("6.4")
.setExampleValue(String.join(",", UUID_EXAMPLE_01, UUID_EXAMPLE_02));
@@ -147,8 +153,16 @@ public class BulkDeleteAction implements ProjectsWsAction {
.setVisibility(request.param(PARAM_VISIBILITY))
.setAnalyzedBefore(request.param(PARAM_ANALYZED_BEFORE))
.setOnProvisionedOnly(request.mandatoryParamAsBoolean(PARAM_ON_PROVISIONED_ONLY))
- .setProjects(request.paramAsStrings(PARAM_PROJECTS))
- .setProjectIds(request.paramAsStrings(PARAM_PROJECT_IDS))
+ .setProjects(restrictTo1000Values(request.paramAsStrings(PARAM_PROJECTS)))
+ .setProjectIds(restrictTo1000Values(request.paramAsStrings(PARAM_PROJECT_IDS)))
.build();
}
+
+ @CheckForNull
+ private static List<String> restrictTo1000Values(@Nullable List<String> values) {
+ if (values == null) {
+ return null;
+ }
+ return values.subList(0, min(values.size(), 1_000));
+ }
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchAction.java
index e4b97ba9eb0..1e4ba6fe8ba 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchAction.java
@@ -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;
@@ -42,6 +43,7 @@ import org.sonar.server.user.UserSession;
import org.sonarqube.ws.Projects.SearchWsResponse;
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;
@@ -89,7 +91,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>" +
@@ -129,6 +133,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
@@ -137,6 +144,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));
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchMyProjectsAction.java b/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchMyProjectsAction.java
index 1ec0e29c2a1..9752e19127f 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchMyProjectsAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchMyProjectsAction.java
@@ -34,6 +34,7 @@ import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.server.ws.WebService.Param;
import org.sonar.api.web.UserRole;
+import org.sonar.db.DatabaseUtils;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
@@ -69,7 +70,7 @@ public class SearchMyProjectsAction implements ProjectsWsAction {
@Override
public void define(WebService.NewController context) {
WebService.NewAction action = context.createAction("search_my_projects")
- .setDescription("Return list of projects for which the current user has 'Administer' permission.")
+ .setDescription("Return list of projects for which the current user has 'Administer' permission. Maximum 1'000 projects are returned.")
.setResponseExample(getClass().getResource("search_my_projects-example.json"))
.addPagingParams(100, MAX_SIZE)
.setSince("6.0")
@@ -193,7 +194,7 @@ public class SearchMyProjectsAction implements ProjectsWsAction {
List<Long> componentIds = dbClient.roleDao().selectComponentIdsByPermissionAndUserId(dbSession, UserRole.ADMIN, userId);
ComponentQuery dbQuery = ComponentQuery.builder()
.setQualifiers(Qualifiers.PROJECT)
- .setComponentIds(ImmutableSet.copyOf(componentIds))
+ .setComponentIds(ImmutableSet.copyOf(componentIds.subList(0, Math.min(componentIds.size(), DatabaseUtils.PARTITION_SIZE_FOR_ORACLE))))
.build();
return new ProjectsResult(
diff --git a/server/sonar-server/src/test/java/org/sonar/server/permission/ws/template/BulkApplyTemplateActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/permission/ws/template/BulkApplyTemplateActionTest.java
index 978e3732f40..e0b25a0d667 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/permission/ws/template/BulkApplyTemplateActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/permission/ws/template/BulkApplyTemplateActionTest.java
@@ -19,7 +19,9 @@
*/
package org.sonar.server.permission.ws.template;
+import java.util.Collections;
import java.util.List;
+import org.apache.commons.lang.StringUtils;
import org.junit.Before;
import org.junit.Test;
import org.sonar.api.resources.Qualifiers;
@@ -102,7 +104,7 @@ public class BulkApplyTemplateActionTest extends BasePermissionWsTest<BulkApplyT
}
@Test
- public void bulk_apply_template_by_template_uuid() throws Exception {
+ public void bulk_apply_template_by_template_uuid() {
// this project should not be applied the template
OrganizationDto otherOrganization = db.organizations().insert();
db.components().insertPrivateProject(otherOrganization);
@@ -135,7 +137,19 @@ public class BulkApplyTemplateActionTest extends BasePermissionWsTest<BulkApplyT
}
@Test
- public void bulk_apply_template_by_template_name() 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");
+
+ newRequest()
+ .setParam(PARAM_ORGANIZATION, organization.getKey())
+ .setParam(PARAM_TEMPLATE_NAME, template1.getName())
+ .setParam(PARAM_PROJECTS, StringUtils.join(Collections.nCopies(1_001, "foo"), ","))
+ .execute();
+ }
+
+ @Test
+ public void bulk_apply_template_by_template_name() {
ComponentDto privateProject = db.components().insertPrivateProject(organization);
ComponentDto publicProject = db.components().insertPublicProject(organization);
loginAsAdmin(organization);
@@ -150,7 +164,7 @@ public class BulkApplyTemplateActionTest extends BasePermissionWsTest<BulkApplyT
}
@Test
- public void apply_template_by_qualifiers() throws Exception {
+ public void apply_template_by_qualifiers() {
ComponentDto publicProject = db.components().insertPublicProject(organization);
ComponentDto privateProject = db.components().insertPrivateProject(organization);
ComponentDto view = db.components().insertComponent(newView(organization));
@@ -169,7 +183,7 @@ public class BulkApplyTemplateActionTest extends BasePermissionWsTest<BulkApplyT
}
@Test
- public void apply_template_by_query_on_name_and_key_public_project() throws Exception {
+ public void apply_template_by_query_on_name_and_key_public_project() {
ComponentDto publicProjectFoundByKey = ComponentTesting.newPublicProjectDto(organization).setDbKey("sonar");
db.components().insertProjectAndSnapshot(publicProjectFoundByKey);
ComponentDto publicProjectFoundByName = ComponentTesting.newPublicProjectDto(organization).setName("name-sonar-name");
@@ -189,7 +203,7 @@ public class BulkApplyTemplateActionTest extends BasePermissionWsTest<BulkApplyT
}
@Test
- public void apply_template_by_query_on_name_and_key() throws Exception {
+ public void apply_template_by_query_on_name_and_key() {
// partial match on key
ComponentDto privateProjectFoundByKey = ComponentTesting.newPrivateProjectDto(organization).setDbKey("sonarqube");
db.components().insertProjectAndSnapshot(privateProjectFoundByKey);
@@ -210,7 +224,7 @@ public class BulkApplyTemplateActionTest extends BasePermissionWsTest<BulkApplyT
}
@Test
- public void apply_template_by_project_keys() throws Exception {
+ public void apply_template_by_project_keys() {
ComponentDto project1 = db.components().insertPrivateProject(organization);
ComponentDto project2 = db.components().insertPrivateProject(organization);
ComponentDto untouchedProject = db.components().insertPrivateProject(organization);
@@ -227,7 +241,7 @@ public class BulkApplyTemplateActionTest extends BasePermissionWsTest<BulkApplyT
}
@Test
- public void apply_template_by_provisioned_only() throws Exception {
+ public void apply_template_by_provisioned_only() {
ComponentDto provisionedProject1 = db.components().insertPrivateProject(organization);
ComponentDto provisionedProject2 = db.components().insertPrivateProject(organization);
ComponentDto analyzedProject = db.components().insertPrivateProject(organization);
@@ -245,7 +259,7 @@ public class BulkApplyTemplateActionTest extends BasePermissionWsTest<BulkApplyT
}
@Test
- public void apply_template_by_analyzed_before() throws Exception {
+ public void apply_template_by_analyzed_before() {
ComponentDto oldProject1 = db.components().insertPrivateProject(organization);
ComponentDto oldProject2 = db.components().insertPrivateProject(organization);
ComponentDto recentProject = db.components().insertPrivateProject(organization);
@@ -265,7 +279,7 @@ public class BulkApplyTemplateActionTest extends BasePermissionWsTest<BulkApplyT
}
@Test
- public void apply_template_by_visibility() throws Exception {
+ public void apply_template_by_visibility() {
ComponentDto privateProject1 = db.components().insertPrivateProject(organization);
ComponentDto privateProject2 = db.components().insertPrivateProject(organization);
ComponentDto publicProject = db.components().insertPublicProject(organization);
diff --git a/server/sonar-server/src/test/java/org/sonar/server/project/ws/BulkDeleteActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/project/ws/BulkDeleteActionTest.java
index 1709788a7c9..617e0f07290 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/project/ws/BulkDeleteActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/project/ws/BulkDeleteActionTest.java
@@ -19,10 +19,11 @@
*/
package org.sonar.server.project.ws;
-import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.Date;
+import java.util.List;
import java.util.stream.IntStream;
+import org.apache.commons.lang.StringUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -31,6 +32,7 @@ import org.mockito.ArgumentCaptor;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.server.ws.WebService.Param;
import org.sonar.api.utils.System2;
+import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
@@ -209,6 +211,23 @@ public class BulkDeleteActionTest {
verifyNoDeletions();
}
+ /**
+ * SONAR-10356
+ */
+ @Test
+ public void delete_only_the_1000_first_projects() {
+ userSession.logIn().addPermission(ADMINISTER, org1);
+ List<String> keys = IntStream.range(0, 1_010).mapToObj(i -> "key" + i).collect(MoreCollectors.toArrayList());
+ keys.forEach(key -> db.components().insertPrivateProject(org1, p -> p.setDbKey(key)));
+
+ ws.newRequest()
+ .setParam("organization", org1.getKey())
+ .setParam("projects", StringUtils.join(keys, ","))
+ .execute();
+
+ verify(componentCleanerService, times(1_000)).delete(any(DbSession.class), any(ComponentDto.class));
+ }
+
@Test
public void organization_administrator_deletes_projects_by_keys_in_his_organization() {
userSession.logIn().addPermission(ADMINISTER, org1);
diff --git a/server/sonar-server/src/test/java/org/sonar/server/project/ws/SearchActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/project/ws/SearchActionTest.java
index e071564fe3f..04d6aaf3fd7 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/project/ws/SearchActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/project/ws/SearchActionTest.java
@@ -20,10 +20,9 @@
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;
@@ -304,6 +303,26 @@ public class SearchActionTest {
}
@Test
+ 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(SearchRequest.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(SearchRequest.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);
diff --git a/server/sonar-server/src/test/java/org/sonar/server/project/ws/SearchMyProjectsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/project/ws/SearchMyProjectsActionTest.java
index bcb8a89785b..28f39588fa5 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/project/ws/SearchMyProjectsActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/project/ws/SearchMyProjectsActionTest.java
@@ -19,6 +19,7 @@
*/
package org.sonar.server.project.ws;
+import java.util.stream.IntStream;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -114,13 +115,26 @@ public class SearchMyProjectsActionTest {
db.users().insertProjectPermissionOnUser(user, UserRole.ADMIN, jdk7);
db.users().insertProjectPermissionOnUser(anotherUser, UserRole.ADMIN, cLang);
- SearchMyProjectsWsResponse result = call_ws();
+ SearchMyProjectsWsResponse result = callWs();
assertThat(result.getProjectsCount()).isEqualTo(1);
assertThat(result.getProjects(0).getId()).isEqualTo(jdk7.uuid());
}
@Test
+ public void return_only_first_1000_projects() {
+ OrganizationDto organization = db.organizations().insert();
+ IntStream.range(0, 1_010).forEach(i -> {
+ ComponentDto project = db.components().insertComponent(newPrivateProjectDto(organization));
+ db.users().insertProjectPermissionOnUser(user, UserRole.ADMIN, project);
+ });
+
+ SearchMyProjectsWsResponse result = callWs();
+
+ assertThat(result.getPaging().getTotal()).isEqualTo(1_000);
+ }
+
+ @Test
public void sort_projects_by_name() {
OrganizationDto organizationDto = db.organizations().insert();
ComponentDto b_project = db.components().insertComponent(ComponentTesting.newPrivateProjectDto(organizationDto).setName("B_project_name"));
@@ -131,7 +145,7 @@ public class SearchMyProjectsActionTest {
db.users().insertProjectPermissionOnUser(user, UserRole.ADMIN, a_project);
db.users().insertProjectPermissionOnUser(user, UserRole.ADMIN, c_project);
- SearchMyProjectsWsResponse result = call_ws();
+ SearchMyProjectsWsResponse result = callWs();
assertThat(result.getProjectsCount()).isEqualTo(3);
assertThat(result.getProjectsList()).extracting(Project::getId)
@@ -164,7 +178,7 @@ public class SearchMyProjectsActionTest {
db.users().insertProjectPermissionOnUser(user, UserRole.ADMIN, jdk7);
db.users().insertProjectPermissionOnUser(user, UserRole.ISSUE_ADMIN, clang);
- SearchMyProjectsWsResponse result = call_ws();
+ SearchMyProjectsWsResponse result = callWs();
assertThat(result.getProjectsCount()).isEqualTo(1);
assertThat(result.getProjects(0).getId()).isEqualTo(jdk7.uuid());
@@ -179,7 +193,7 @@ public class SearchMyProjectsActionTest {
db.users().insertProjectPermissionOnUser(user, UserRole.ADMIN, jdk7);
db.users().insertProjectPermissionOnUser(user, UserRole.ADMIN, view);
- SearchMyProjectsWsResponse result = call_ws();
+ SearchMyProjectsWsResponse result = callWs();
assertThat(result.getProjectsCount()).isEqualTo(1);
assertThat(result.getProjects(0).getId()).isEqualTo(jdk7.uuid());
@@ -191,7 +205,7 @@ public class SearchMyProjectsActionTest {
ComponentDto branch = db.components().insertProjectBranch(project);
db.users().insertProjectPermissionOnUser(user, UserRole.ADMIN, project);
- SearchMyProjectsWsResponse result = call_ws();
+ SearchMyProjectsWsResponse result = callWs();
assertThat(result.getProjectsList())
.extracting(Project::getKey)
@@ -210,7 +224,7 @@ public class SearchMyProjectsActionTest {
db.users().insertProjectPermissionOnGroup(group, UserRole.ADMIN, jdk7);
db.users().insertProjectPermissionOnGroup(group, UserRole.USER, cLang);
- SearchMyProjectsWsResponse result = call_ws();
+ SearchMyProjectsWsResponse result = callWs();
assertThat(result.getProjectsCount()).isEqualTo(1);
assertThat(result.getProjects(0).getId()).isEqualTo(jdk7.uuid());
@@ -232,7 +246,7 @@ public class SearchMyProjectsActionTest {
db.users().insertProjectPermissionOnUser(user, UserRole.ADMIN, sonarqube);
db.users().insertProjectPermissionOnGroup(group, UserRole.ADMIN, sonarqube);
- SearchMyProjectsWsResponse result = call_ws();
+ SearchMyProjectsWsResponse result = callWs();
assertThat(result.getProjectsCount()).isEqualTo(3);
assertThat(result.getProjectsList()).extracting(Project::getId).containsOnly(jdk7.uuid(), cLang.uuid(), sonarqube.uuid());
@@ -249,7 +263,7 @@ public class SearchMyProjectsActionTest {
userSession.anonymous();
expectedException.expect(UnauthorizedException.class);
- call_ws();
+ callWs();
}
private ComponentDto insertClang(OrganizationDto organizationDto) {
@@ -271,7 +285,7 @@ public class SearchMyProjectsActionTest {
.setDbKey("Java"));
}
- private SearchMyProjectsWsResponse call_ws() {
+ private SearchMyProjectsWsResponse callWs() {
return ws.newRequest()
.executeProtobuf(SearchMyProjectsWsResponse.class);
}