diff options
author | Simon Brandhof <simon.brandhof@sonarsource.com> | 2018-02-13 20:18:49 +0100 |
---|---|---|
committer | Simon Brandhof <simon.brandhof@sonarsource.com> | 2018-02-22 09:44:52 +0100 |
commit | 0b2bd218c88655f24bc541086dfee18d3364a9e1 (patch) | |
tree | 31528458dc23d762d79f2ca6b526982b0db2eae5 | |
parent | e353cd6f2bd37ea143d5806e9ee85323cddd1f97 (diff) | |
download | sonarqube-0b2bd218c88655f24bc541086dfee18d3364a9e1.tar.gz sonarqube-0b2bd218c88655f24bc541086dfee18d3364a9e1.zip |
SONAR-10356 Oracle error on some WS involving more than 1000 projects
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); } |