public static final int DEFAULT_OFFSET = 0;
public static final int DEFAULT_LIMIT = 10;
- public static final int MAX_LIMIT = 500;
+ public static final int MAX_PAGE_SIZE = 500;
private static final int MAX_RETURNABLE_RESULTS = 10_000;
private int offset = DEFAULT_OFFSET;
}
/**
- * Set offset and limit according to page approach. If pageSize is negative, then
- * {@link #MAX_LIMIT} is used.
+ * Set offset and limit according to page approach
*/
public SearchOptions setPage(int page, int pageSize) {
checkArgument(page >= 1, "Page must be greater or equal to 1 (got " + page + ")");
+ setLimit(pageSize);
int lastResultIndex = page * pageSize;
checkArgument(lastResultIndex <= MAX_RETURNABLE_RESULTS, "Can return only the first %s results. %sth result asked.", MAX_RETURNABLE_RESULTS, lastResultIndex);
- setLimit(pageSize);
- setOffset((page * this.limit) - this.limit);
+ setOffset(lastResultIndex - pageSize);
return this;
}
* Sets the limit on the number of results to return.
*/
public SearchOptions setLimit(int limit) {
- if (limit <= 0) {
- this.limit = MAX_LIMIT;
- } else {
- this.limit = Math.min(limit, MAX_LIMIT);
- }
+ checkArgument(limit > 0 && limit <= MAX_PAGE_SIZE, "Page size must be between 1 and " + MAX_PAGE_SIZE + " (got " + limit + ")");
+ this.limit = limit;
return this;
}
import org.sonar.test.JsonAssert;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.Assert.fail;
public class SearchOptionsTest {
}
@Test
- public void with_zero_page_size() {
- SearchOptions options = new SearchOptions().setPage(1, 0);
- assertThat(options.getLimit()).isEqualTo(SearchOptions.MAX_LIMIT);
- assertThat(options.getOffset()).isEqualTo(0);
- assertThat(options.getPage()).isEqualTo(1);
+ public void fail_if_page_is_not_strictly_positive() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Page must be greater or equal to 1 (got 0)");
+ new SearchOptions().setPage(0, 10);
}
@Test
- public void page_must_be_strictly_positive() {
- try {
- new SearchOptions().setPage(0, 10);
- fail();
- } catch (IllegalArgumentException e) {
- assertThat(e).hasMessage("Page must be greater or equal to 1 (got 0)");
- }
+ public void fail_if_ps_is_zero() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Page size must be between 1 and 500 (got 0)");
+ new SearchOptions().setPage(1, 0);
}
@Test
- public void use_max_limit_if_negative() {
- SearchOptions options = new SearchOptions().setPage(2, -1);
- assertThat(options.getLimit()).isEqualTo(SearchOptions.MAX_LIMIT);
+ public void fail_if_ps_is_negative() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Page size must be between 1 and 500 (got -1)");
+ new SearchOptions().setPage(2, -1);
}
@Test
- public void max_limit() {
- SearchOptions options = new SearchOptions().setLimit(42);
- assertThat(options.getLimit()).isEqualTo(42);
+ public void fail_if_ps_is_over_limit() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Page size must be between 1 and 500 (got 510)");
+ new SearchOptions().setPage(3, SearchOptions.MAX_PAGE_SIZE + 10);
+ }
+
+ @Test
+ public void fail_if_result_after_first_10_000() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Can return only the first 10000 results. 10500th result asked.");
- options.setLimit(SearchOptions.MAX_LIMIT + 10);
- assertThat(options.getLimit()).isEqualTo(SearchOptions.MAX_LIMIT);
+ underTest.setPage(21, 500);
}
@Test
- public void max_page_size() {
- SearchOptions options = new SearchOptions().setPage(3, SearchOptions.MAX_LIMIT + 10);
- assertThat(options.getOffset()).isEqualTo(SearchOptions.MAX_LIMIT * 2);
- assertThat(options.getLimit()).isEqualTo(SearchOptions.MAX_LIMIT);
+ public void max_limit() {
+ SearchOptions options = new SearchOptions().setLimit(42);
+ assertThat(options.getLimit()).isEqualTo(42);
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Page size must be between 1 and 500 (got 510)");
+ options.setLimit(SearchOptions.MAX_PAGE_SIZE + 10);
}
@Test
JsonAssert.assertJson(json.toString()).isSimilarTo("{\"total\": 42, \"p\": 3, \"ps\": 10}");
}
-
- @Test
- public void fail_if_result_after_first_10_000() {
- expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("Can return only the first 10000 results. 10500th result asked.");
-
- underTest.setPage(21, 500);
- }
}
results = underTest.search(new RuleQuery(), options);
assertThat(results.getTotal()).isEqualTo(3);
assertThat(results.getUuids()).hasSize(1);
-
- // from 2 to 11 included
- options.setOffset(2).setLimit(0);
- results = underTest.search(new RuleQuery(), options);
- assertThat(results.getTotal()).isEqualTo(3);
- assertThat(results.getUuids()).hasSize(1);
}
@Test
import org.sonar.db.rule.RuleDefinitionDto;
import static com.google.common.base.Preconditions.checkArgument;
-import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
+import static org.sonar.server.es.SearchOptions.MAX_PAGE_SIZE;
/**
* @since 3.6
public IssueQuery build() {
if (issueKeys != null) {
- checkArgument(issueKeys.size() <= MAX_LIMIT, "Number of issue keys must be less than " + MAX_LIMIT + " (got " + issueKeys.size() + ")");
+ checkArgument(issueKeys.size() <= MAX_PAGE_SIZE, "Number of issue keys must be less than " + MAX_PAGE_SIZE + " (got " + issueKeys.size() + ")");
}
return new IssueQuery(this);
}
assertThat(result.getHits().getHits()).hasSize(5);
assertThat(result.getHits().getTotalHits()).isEqualTo(12);
- result = underTest.search(IssueQuery.builder().build(), new SearchOptions().setOffset(2).setLimit(0));
+ result = underTest.search(IssueQuery.builder().build(), new SearchOptions().setOffset(2).setLimit(10));
assertThat(result.getHits().getHits()).hasSize(10);
assertThat(result.getHits().getTotalHits()).isEqualTo(12);
}
indexIssues(issues.toArray(new IssueDoc[] {}));
IssueQuery.Builder query = IssueQuery.builder();
- SearchResponse result = underTest.search(query.build(), new SearchOptions().setLimit(Integer.MAX_VALUE));
- assertThat(result.getHits().getHits()).hasSize(SearchOptions.MAX_LIMIT);
+ SearchResponse result = underTest.search(query.build(), new SearchOptions().setLimit(500));
+ assertThat(result.getHits().getHits()).hasSize(SearchOptions.MAX_PAGE_SIZE);
}
@Test
import static org.sonar.api.resources.Qualifiers.SUBVIEW;
import static org.sonar.api.resources.Qualifiers.VIEW;
import static org.sonar.core.util.stream.MoreCollectors.toHashSet;
-import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
+import static org.sonar.server.es.SearchOptions.MAX_PAGE_SIZE;
import static org.sonar.server.ws.WsParameterBuilder.createQualifiersParameter;
import static org.sonar.server.ws.WsParameterBuilder.QualifierParameterContext.newQualifierParameterContext;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
WebService.NewAction action = context.createAction(ACTION_SEARCH)
.setSince("6.3")
.setDescription("Search for components")
- .addPagingParams(100, MAX_LIMIT)
+ .addPagingParams(100, MAX_PAGE_SIZE)
.setChangelog(
new Change("8.4", "Param 'language' has been removed"),
new Change("8.4", String.format("The use of 'DIR','FIL','UTS' as values for parameter '%s' is no longer supported", PARAM_QUALIFIERS)),
import static org.sonar.core.util.Uuids.UUID_EXAMPLE_02;
import static org.sonar.core.util.stream.MoreCollectors.toSet;
import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
-import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
+import static org.sonar.server.es.SearchOptions.MAX_PAGE_SIZE;
import static org.sonar.server.issue.AbstractChangeTagsAction.TAGS_PARAMETER;
import static org.sonar.server.issue.AssignAction.ASSIGNEE_PARAMETER;
import static org.sonar.server.issue.CommentAction.COMMENT_KEY;
this.propertiesByActions = toPropertiesByActions(request);
List<String> issueKeys = request.mandatoryParamAsStrings(PARAM_ISSUES);
- checkArgument(issueKeys.size() <= MAX_LIMIT, "Number of issues is limited to %s", MAX_LIMIT);
+ checkArgument(issueKeys.size() <= MAX_PAGE_SIZE, "Number of issues is limited to %s", MAX_PAGE_SIZE);
List<IssueDto> allIssues = dbClient.issueDao().selectByKeys(dbSession, issueKeys)
.stream()
.filter(issueDto -> SECURITY_HOTSPOT.getDbConstant() != issueDto.getType())
import static org.sonar.api.server.ws.WebService.Param.FACETS;
import static org.sonar.api.utils.Paging.forPageIndex;
import static org.sonar.core.util.stream.MoreCollectors.toSet;
-import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
+import static org.sonar.server.es.SearchOptions.MAX_PAGE_SIZE;
import static org.sonar.server.issue.index.IssueIndex.FACET_ASSIGNED_TO_ME;
import static org.sonar.server.issue.index.IssueIndex.FACET_PROJECTS;
import static org.sonar.server.issue.index.IssueQuery.SORT_BY_ASSIGNEE;
new Change("5.5", "response field 'debt' is renamed 'effort'"))
.setResponseExample(getClass().getResource("search-example.json"));
- action.addPagingParams(100, MAX_LIMIT);
+ action.addPagingParams(100, MAX_PAGE_SIZE);
action.createParam(FACETS)
.setDescription("Comma-separated list of the facets to be computed. No facet is computed by default.")
.setPossibleValues(SUPPORTED_FACETS);
import org.sonar.server.user.UserSession;
import static org.sonar.server.component.ComponentFinder.ParamNames.PROJECT_ID_AND_KEY;
-import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
+import static org.sonar.server.es.SearchOptions.MAX_PAGE_SIZE;
import static org.sonar.server.measure.custom.ws.CustomMeasureValidator.checkPermissions;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
.setSince("5.2")
.setDeprecatedSince("7.4")
.addFieldsParam(CustomMeasureJsonWriter.OPTIONAL_FIELDS)
- .addPagingParams(100, MAX_LIMIT)
+ .addPagingParams(100, MAX_PAGE_SIZE)
.setResponseExample(getClass().getResource("example-search.json"))
.setHandler(this);
import org.sonar.server.es.SearchOptions;
import static com.google.common.collect.Sets.newHashSet;
-import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
+import static org.sonar.server.es.SearchOptions.MAX_PAGE_SIZE;
import static org.sonar.server.metric.ws.MetricJsonWriter.FIELD_ID;
import static org.sonar.server.metric.ws.MetricJsonWriter.FIELD_KEY;
.setSince("5.2")
.setDescription("Search for metrics")
.setResponseExample(getClass().getResource("example-search.json"))
- .addPagingParams(100, MAX_LIMIT)
+ .addPagingParams(100, MAX_PAGE_SIZE)
.setChangelog(
new Change("8.4", "Field 'id' in the response is deprecated"),
new Change("7.7", "Field 'custom' in the response is deprecated"))
import static java.util.Optional.ofNullable;
import static org.sonar.api.server.ws.WebService.SelectionMode.SELECTED;
import static org.sonar.db.permission.OrganizationPermission.ADMINISTER;
-import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
+import static org.sonar.server.es.SearchOptions.MAX_PAGE_SIZE;
import static org.sonar.server.organization.ws.OrganizationsWsSupport.PARAM_ORGANIZATION;
import static org.sonar.server.exceptions.NotFoundException.checkFoundWithOptional;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
action.createSearchQuery("orwe", "names", "logins")
.setMinimumLength(2);
- action.addPagingParams(50, MAX_LIMIT);
+ action.addPagingParams(50, MAX_PAGE_SIZE);
action.createParam(Param.SELECTED)
.setDescription("Depending on the value, show only selected items (selected=selected) or deselected items (selected=deselected).")
import static org.sonar.api.utils.DateUtils.parseStartingDateOrDateTime;
import static org.sonar.core.util.stream.MoreCollectors.toSet;
import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
-import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
+import static org.sonar.server.es.SearchOptions.MAX_PAGE_SIZE;
import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_SINCE;
import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_TO;
QProfileReference.defineParams(wsAction, languages);
- wsAction.addPagingParams(50, MAX_LIMIT);
+ wsAction.addPagingParams(50, MAX_PAGE_SIZE);
wsAction.createParam(PARAM_SINCE)
.setDescription("Start date for the changelog. <br>" +
import static org.sonar.api.server.ws.WebService.Param.FIELDS;
import static org.sonar.api.server.ws.WebService.Param.PAGE;
import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE;
-import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
+import static org.sonar.server.es.SearchOptions.MAX_PAGE_SIZE;
import static org.sonar.server.rule.index.RuleIndex.ALL_STATUSES_EXCEPT_REMOVED;
import static org.sonar.server.rule.index.RuleIndex.FACET_ACTIVE_SEVERITIES;
import static org.sonar.server.rule.index.RuleIndex.FACET_CWE;
@Override
public void define(WebService.NewController controller) {
WebService.NewAction action = controller.createAction(ACTION)
- .addPagingParams(100, MAX_LIMIT)
+ .addPagingParams(100, MAX_PAGE_SIZE)
.setHandler(this)
.setChangelog(new Change("7.1", "The field 'scope' has been added to the response"))
.setChangelog(new Change("7.1", "The field 'scope' has been added to the 'f' parameter"))
context.addFacets(request.getFacets());
}
if (pageSize < 1) {
- context.setPage(Integer.parseInt(request.getP()), 0).setLimit(MAX_LIMIT);
+ context.setPage(Integer.parseInt(request.getP()), 0).setLimit(MAX_PAGE_SIZE);
} else {
context.setPage(Integer.parseInt(request.getP()), pageSize);
}
import static org.sonar.api.utils.DateUtils.formatDateTime;
import static org.sonar.api.utils.Paging.forPageIndex;
import static org.sonar.core.util.stream.MoreCollectors.toList;
-import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
import static org.sonarqube.ws.Users.SearchWsResponse.Groups;
import static org.sonarqube.ws.Users.SearchWsResponse.ScmAccounts;
.setHandler(this)
.setResponseExample(getClass().getResource("search-example.json"));
- action.addPagingParams(50, MAX_LIMIT);
+ action.addPagingParams(50, SearchOptions.MAX_PAGE_SIZE);
action.createParam(TEXT_QUERY)
.setMinimumLength(2)
import static org.apache.commons.lang.StringUtils.defaultIfBlank;
import static org.sonar.api.utils.Paging.forPageIndex;
import static org.sonar.db.permission.OrganizationPermission.ADMINISTER;
-import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
+import static org.sonar.server.es.SearchOptions.MAX_PAGE_SIZE;
import static org.sonar.server.usergroups.ws.GroupWsSupport.PARAM_ORGANIZATION_KEY;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
import static org.sonarqube.ws.UserGroups.Group;
.setResponseExample(getClass().getResource("search-example.json"))
.setSince("5.2")
.addFieldsParam(ALL_FIELDS)
- .addPagingParams(100, MAX_LIMIT)
+ .addPagingParams(100, MAX_PAGE_SIZE)
.addSearchQuery("sonar-users", "names")
.setChangelog(
new Change("8.4", "Field 'id' in the response is deprecated. Format changes from integer to string."),
import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE;
import static org.sonar.api.utils.Paging.offset;
import static org.sonar.core.util.Uuids.UUID_EXAMPLE_02;
-import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
+import static org.sonar.server.es.SearchOptions.MAX_PAGE_SIZE;
import static org.sonar.server.webhook.ws.WebhookWsSupport.copyDtoToProtobuf;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
"auto-generated value that can be obtained through api/webhooks/create or api/webhooks/list")
.setExampleValue(UUID_EXAMPLE_02);
- action.addPagingParamsSince(10, MAX_LIMIT, "7.1");
+ action.addPagingParamsSince(10, MAX_PAGE_SIZE, "7.1");
}
@Override
ComponentDto project = db.components().insertComponent(ComponentTesting.newPublicProjectDto(organization, "PROJECT_ID").setDbKey("PROJECT_KEY"));
indexPermissions();
ComponentDto file = db.components().insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY"));
- for (int i = 0; i < SearchOptions.MAX_LIMIT + 1; i++) {
+ for (int i = 0; i < SearchOptions.MAX_PAGE_SIZE + 1; i++) {
IssueDto issue = newDto(rule, file, project).setAssigneeUuid(null);
dbClient.issueDao().insert(session, issue);
}
@Test
public void paging_with_page_size_to_minus_one() {
- RuleDto rule = newIssueRule();
- OrganizationDto organization = db.organizations().insert(o -> o.setKey("my-org-1"));
- ComponentDto project = db.components().insertComponent(ComponentTesting.newPublicProjectDto(organization, "PROJECT_ID").setDbKey("PROJECT_KEY"));
- indexPermissions();
- ComponentDto file = db.components().insertComponent(newFileDto(project, null, "FILE_ID").setDbKey("FILE_KEY"));
- for (int i = 0; i < 12; i++) {
- IssueDto issue = newDto(rule, file, project);
- dbClient.issueDao().insert(session, issue);
- }
- session.commit();
- indexIssues();
-
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Page size must be between 1 and 500 (got -1)");
ws.newRequest()
.setParam(WebService.Param.PAGE, "1")
.setParam(WebService.Param.PAGE_SIZE, "-1")
- .execute()
- .assertJson(this.getClass(), "paging_with_page_size_to_minus_one.json");
+ .execute();
}
@Test
@Test
public void bulk_activation() {
- int bulkSize = SearchOptions.MAX_LIMIT + 10 + new Random().nextInt(100);
+ int bulkSize = SearchOptions.MAX_PAGE_SIZE + 10 + new Random().nextInt(100);
String language = randomAlphanumeric(10);
String repositoryKey = randomAlphanumeric(10);
QProfileDto profile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(language));
@Test
public void bulk_deactivation() {
- int bulkSize = SearchOptions.MAX_LIMIT + 10 + new Random().nextInt(100);
+ int bulkSize = SearchOptions.MAX_PAGE_SIZE + 10 + new Random().nextInt(100);
String language = randomAlphanumeric(10);
String repositoryKey = randomAlphanumeric(10);
QProfileDto profile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(language));
+++ /dev/null
-{
- "total": 12,
- "p": 1,
- "ps": 500
-}