From 0b2bd218c88655f24bc541086dfee18d3364a9e1 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Tue, 13 Feb 2018 20:18:49 +0100 Subject: [PATCH] SONAR-10356 Oracle error on some WS involving more than 1000 projects --- .../main/java/org/sonar/db/DatabaseUtils.java | 11 +++ .../java/org/sonar/db/DatabaseUtilsTest.java | 16 ++++ .../org/sonar/db/component/ComponentDao.java | 29 ++++++- .../sonar/db/component/ComponentDaoTest.java | 86 +++++++++++++++++-- .../ws/template/BulkApplyTemplateAction.java | 7 ++ .../server/project/ws/BulkDeleteAction.java | 22 ++++- .../sonar/server/project/ws/SearchAction.java | 12 ++- .../project/ws/SearchMyProjectsAction.java | 5 +- .../template/BulkApplyTemplateActionTest.java | 32 +++++-- .../project/ws/BulkDeleteActionTest.java | 21 ++++- .../server/project/ws/SearchActionTest.java | 23 ++++- .../ws/SearchMyProjectsActionTest.java | 32 +++++-- 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 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 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 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")); @@ -777,6 +779,51 @@ public class ComponentDaoTest { assertThat(underTest.countByQuery(dbSession, organization.getUuid(), query.get().setQualifiers(Qualifiers.PROJECT, Qualifiers.VIEW).build())).isEqualTo(1); } + @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 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 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 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 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 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 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.
" + "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 restrictTo1000Values(@Nullable List 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:
    " + @@ -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 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 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; @@ -303,6 +302,26 @@ public class SearchActionTest { .doesNotContain(sonarlint.getKey()); } + @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()); 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,12 +115,25 @@ 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(); @@ -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); } -- 2.39.5