mirror of
https://github.com/SonarSource/sonarqube.git
synced 2024-08-11 02:11:19 +02:00
SONAR-6820 WS using SearchOptions has a page size limit of 500
This commit is contained in:
parent
5d9f474688
commit
c6374cf5b6
@ -33,6 +33,8 @@ import org.sonar.server.es.SearchResult;
|
||||
import org.sonar.server.search.QueryContext;
|
||||
import org.sonar.server.user.UserSession;
|
||||
|
||||
import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
|
||||
|
||||
public class SearchAction implements RequestHandler {
|
||||
|
||||
public static final String PARAM_TYPE = "type";
|
||||
@ -61,7 +63,7 @@ public class SearchAction implements RequestHandler {
|
||||
.setDescription("Activity type")
|
||||
.setPossibleValues(Activity.Type.values());
|
||||
|
||||
action.addPagingParams(10);
|
||||
action.addPagingParams(10, MAX_LIMIT);
|
||||
action.addFieldsParam(docToJsonMapping.supportedFields());
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,7 @@ import org.sonar.server.user.UserSession;
|
||||
import static com.google.common.collect.Sets.newLinkedHashSet;
|
||||
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;
|
||||
|
||||
public class SearchViewComponentsAction implements RequestHandler {
|
||||
|
||||
@ -80,7 +81,7 @@ public class SearchViewComponentsAction implements RequestHandler {
|
||||
.setDescription("UTF-8 search query")
|
||||
.setExampleValue("sonar");
|
||||
|
||||
action.addPagingParams(10);
|
||||
action.addPagingParams(10, MAX_LIMIT);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -19,19 +19,18 @@
|
||||
*/
|
||||
package org.sonar.server.es;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.sonar.api.server.ws.WebService;
|
||||
import org.sonar.api.utils.text.JsonWriter;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.sonar.api.server.ws.WebService;
|
||||
import org.sonar.api.utils.text.JsonWriter;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
/**
|
||||
* Various Elasticsearch request options: paging, fields and facets
|
||||
@ -41,6 +40,7 @@ public class SearchOptions {
|
||||
public static final int DEFAULT_OFFSET = 0;
|
||||
public static final int DEFAULT_LIMIT = 10;
|
||||
public static final int MAX_LIMIT = 500;
|
||||
private static final String MSG_MAX_LIMIT_ERROR = "Page size must be less than " + MAX_LIMIT;
|
||||
|
||||
private int offset = DEFAULT_OFFSET;
|
||||
private int limit = DEFAULT_LIMIT;
|
||||
@ -58,7 +58,7 @@ public class SearchOptions {
|
||||
* Sets the offset of the first result to return (zero-based).
|
||||
*/
|
||||
public SearchOptions setOffset(int offset) {
|
||||
Preconditions.checkArgument(offset >= 0, "Offset must be positive");
|
||||
checkArgument(offset >= 0, "Offset must be positive");
|
||||
this.offset = offset;
|
||||
return this;
|
||||
}
|
||||
@ -68,7 +68,7 @@ public class SearchOptions {
|
||||
* {@link #MAX_LIMIT} is used.
|
||||
*/
|
||||
public SearchOptions setPage(int page, int pageSize) {
|
||||
Preconditions.checkArgument(page >= 1, "Page must be greater or equal to 1 (got " + page + ")");
|
||||
checkArgument(page >= 1, "Page must be greater or equal to 1 (got " + page + ")");
|
||||
setLimit(pageSize);
|
||||
setOffset((page * this.limit) - this.limit);
|
||||
return this;
|
||||
@ -89,10 +89,11 @@ public class SearchOptions {
|
||||
* Sets the limit on the number of results to return.
|
||||
*/
|
||||
public SearchOptions setLimit(int limit) {
|
||||
checkArgument(limit <= MAX_LIMIT, MSG_MAX_LIMIT_ERROR);
|
||||
if (limit <= 0) {
|
||||
this.limit = MAX_LIMIT;
|
||||
} else {
|
||||
this.limit = Math.min(limit, MAX_LIMIT);
|
||||
this.limit = limit;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
@ -54,6 +54,7 @@ import static com.google.common.collect.FluentIterable.from;
|
||||
import static com.google.common.collect.Iterables.concat;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.sonar.api.utils.Paging.forPageIndex;
|
||||
import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
|
||||
import static org.sonar.server.ws.WsUtils.writeProtobuf;
|
||||
import static org.sonarqube.ws.client.issue.IssueFilterParameters.ACTION_PLANS;
|
||||
import static org.sonarqube.ws.client.issue.IssueFilterParameters.ADDITIONAL_FIELDS;
|
||||
@ -119,7 +120,7 @@ public class SearchAction implements IssuesWsAction {
|
||||
.setSince("3.6")
|
||||
.setResponseExample(Resources.getResource(this.getClass(), "example-search.json"));
|
||||
|
||||
action.addPagingParams(100);
|
||||
action.addPagingParams(100, MAX_LIMIT);
|
||||
action.createParam(Param.FACETS)
|
||||
.setDescription("Comma-separated list of the facets to be computed. No facet is computed by default.")
|
||||
.setPossibleValues(IssueIndex.SUPPORTED_FACETS);
|
||||
|
@ -47,6 +47,7 @@ import org.sonar.server.user.UserSession;
|
||||
import org.sonar.server.user.index.UserIndex;
|
||||
|
||||
import static com.google.common.collect.Sets.newHashSet;
|
||||
import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
|
||||
import static org.sonar.server.measure.custom.ws.CustomMeasureValidator.checkPermissions;
|
||||
|
||||
public class SearchAction implements CustomMeasuresWsAction {
|
||||
@ -76,7 +77,7 @@ public class SearchAction implements CustomMeasuresWsAction {
|
||||
"Requires 'Administer System' permission or 'Administer' permission on the project.")
|
||||
.setSince("5.2")
|
||||
.addFieldsParam(CustomMeasureJsonWriter.OPTIONAL_FIELDS)
|
||||
.addPagingParams(100)
|
||||
.addPagingParams(100, MAX_LIMIT)
|
||||
.setResponseExample(getClass().getResource("example-search.json"))
|
||||
.setHandler(this);
|
||||
|
||||
|
@ -69,7 +69,7 @@ public class SearchAction implements MetricsWsAction {
|
||||
.setSince("5.2")
|
||||
.setDescription("Search for metrics")
|
||||
.setResponseExample(getClass().getResource("example-search.json"))
|
||||
.addPagingParams(100)
|
||||
.addPagingParams(100,500)
|
||||
.addFieldsParam(MetricJsonWriter.OPTIONAL_FIELDS)
|
||||
.setHandler(this);
|
||||
|
||||
|
@ -39,6 +39,7 @@ import org.sonar.server.es.SearchOptions;
|
||||
import org.sonar.server.user.UserSession;
|
||||
|
||||
import static com.google.common.collect.Sets.newHashSet;
|
||||
import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
|
||||
|
||||
public class GhostsAction implements ProjectsWsAction {
|
||||
public static final String ACTION = "ghosts";
|
||||
@ -59,7 +60,7 @@ public class GhostsAction implements ProjectsWsAction {
|
||||
.setDescription("List ghost projects.<br /> Requires 'Administer System' permission.")
|
||||
.setResponseExample(Resources.getResource(getClass(), "projects-example-ghosts.json"))
|
||||
.setSince("5.2")
|
||||
.addPagingParams(100)
|
||||
.addPagingParams(100, MAX_LIMIT)
|
||||
.addFieldsParam(POSSIBLE_FIELDS)
|
||||
.addSearchQuery("sonar", "names", "keys")
|
||||
.setHandler(this);
|
||||
|
@ -38,6 +38,7 @@ import org.sonar.server.es.SearchOptions;
|
||||
import org.sonar.server.user.UserSession;
|
||||
|
||||
import static com.google.common.collect.Sets.newHashSet;
|
||||
import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
|
||||
|
||||
public class ProvisionedAction implements ProjectsWsAction {
|
||||
private static final Set<String> POSSIBLE_FIELDS = newHashSet("uuid", "key", "name", "creationDate");
|
||||
@ -60,7 +61,7 @@ public class ProvisionedAction implements ProjectsWsAction {
|
||||
.setSince("5.2")
|
||||
.setResponseExample(Resources.getResource(getClass(), "projects-example-provisioned.json"))
|
||||
.setHandler(this)
|
||||
.addPagingParams(100)
|
||||
.addPagingParams(100, MAX_LIMIT)
|
||||
.addSearchQuery("sonar", "names", "keys")
|
||||
.addFieldsParam(POSSIBLE_FIELDS);
|
||||
}
|
||||
|
@ -45,6 +45,7 @@ import org.sonar.server.qualityprofile.QProfileFactory;
|
||||
import org.sonar.server.search.Result;
|
||||
|
||||
import static org.sonar.api.utils.Paging.forPageIndex;
|
||||
import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
|
||||
|
||||
public class ChangelogAction implements QProfileWsAction {
|
||||
|
||||
@ -74,7 +75,7 @@ public class ChangelogAction implements QProfileWsAction {
|
||||
|
||||
QProfileIdentificationParamUtils.defineProfileParams(changelog, languages);
|
||||
|
||||
changelog.addPagingParams(50);
|
||||
changelog.addPagingParams(50, MAX_LIMIT);
|
||||
|
||||
changelog.createParam(PARAM_SINCE)
|
||||
.setDescription("Start date for the changelog.")
|
||||
|
@ -50,6 +50,8 @@ import org.sonar.server.ws.WsUtils;
|
||||
import org.sonarqube.ws.Common;
|
||||
import org.sonarqube.ws.WsTests;
|
||||
|
||||
import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
|
||||
|
||||
public class ListAction implements TestsWsAction {
|
||||
public static final String TEST_ID = "testId";
|
||||
public static final String TEST_FILE_ID = "testFileId";
|
||||
@ -85,7 +87,7 @@ public class ListAction implements TestsWsAction {
|
||||
.setSince("5.2")
|
||||
.setResponseExample(Resources.getResource(getClass(), "tests-example-list.json"))
|
||||
.setHandler(this)
|
||||
.addPagingParams(100);
|
||||
.addPagingParams(100, MAX_LIMIT);
|
||||
|
||||
action
|
||||
.createParam(TEST_FILE_ID)
|
||||
|
@ -41,6 +41,8 @@ import org.sonar.server.es.SearchResult;
|
||||
import org.sonar.server.user.index.UserDoc;
|
||||
import org.sonar.server.user.index.UserIndex;
|
||||
|
||||
import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
|
||||
|
||||
public class SearchAction implements UsersWsAction {
|
||||
|
||||
private final UserIndex userIndex;
|
||||
@ -62,7 +64,7 @@ public class SearchAction implements UsersWsAction {
|
||||
.setResponseExample(getClass().getResource("example-search.json"));
|
||||
|
||||
action.addFieldsParam(UserJsonWriter.FIELDS);
|
||||
action.addPagingParams(50);
|
||||
action.addPagingParams(50, MAX_LIMIT);
|
||||
|
||||
action.createParam(Param.TEXT_QUERY)
|
||||
.setDescription("Filter on login or name.");
|
||||
|
@ -40,6 +40,8 @@ import org.sonar.db.MyBatis;
|
||||
import org.sonar.db.user.GroupDto;
|
||||
import org.sonar.server.es.SearchOptions;
|
||||
|
||||
import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
|
||||
|
||||
public class SearchAction implements UserGroupsWsAction {
|
||||
|
||||
private static final String FIELD_ID = "id";
|
||||
@ -62,7 +64,7 @@ public class SearchAction implements UserGroupsWsAction {
|
||||
.setResponseExample(getClass().getResource("example-search.json"))
|
||||
.setSince("5.2")
|
||||
.addFieldsParam(ALL_FIELDS)
|
||||
.addPagingParams(100)
|
||||
.addPagingParams(100, MAX_LIMIT)
|
||||
.addSearchQuery("sonar-users", "names");
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@ import org.sonar.server.ws.WsTester;
|
||||
import org.sonar.test.DbTests;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
|
||||
|
||||
@Category(DbTests.class)
|
||||
public class SearchViewComponentsActionTest {
|
||||
@ -128,6 +129,19 @@ public class SearchViewComponentsActionTest {
|
||||
request.execute();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fail_when_above_max_page_size() throws Exception {
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
db.prepareDbUnit(getClass(), "shared.xml");
|
||||
userSessionRule.login("john").addProjectUuidPermissions(UserRole.USER, "EFGH");
|
||||
WsTester.TestRequest request = newRequest()
|
||||
.setParam(Param.PAGE_SIZE, String.valueOf(MAX_LIMIT + 10))
|
||||
.setParam("componentId", "EFGH")
|
||||
.setParam("q", "st");
|
||||
|
||||
request.execute();
|
||||
}
|
||||
|
||||
private WsTester.TestRequest newRequest() {
|
||||
return ws.newGetRequest("api/components", "search_view_components");
|
||||
}
|
||||
|
@ -19,7 +19,9 @@
|
||||
*/
|
||||
package org.sonar.server.es;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.sonar.api.utils.text.JsonWriter;
|
||||
import org.sonar.server.search.QueryContext;
|
||||
import org.sonar.test.JsonAssert;
|
||||
@ -31,6 +33,9 @@ import static org.junit.Assert.fail;
|
||||
|
||||
public class SearchOptionsTest {
|
||||
|
||||
@Rule
|
||||
public ExpectedException expectedException = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void defaults() {
|
||||
SearchOptions options = new SearchOptions();
|
||||
@ -86,9 +91,14 @@ public class SearchOptionsTest {
|
||||
public void max_limit() {
|
||||
SearchOptions options = new SearchOptions().setLimit(42);
|
||||
assertThat(options.getLimit()).isEqualTo(42);
|
||||
}
|
||||
|
||||
options.setLimit(SearchOptions.MAX_LIMIT + 10);
|
||||
assertThat(options.getLimit()).isEqualTo(QueryContext.MAX_LIMIT);
|
||||
@Test
|
||||
public void fail_when_limit_is_greater_than_max_limit() {
|
||||
expectedException.expect(IllegalArgumentException.class);
|
||||
expectedException.expectMessage("Page size must be less than " + SearchOptions.MAX_LIMIT);
|
||||
|
||||
new SearchOptions().setLimit(SearchOptions.MAX_LIMIT + 10);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -99,7 +109,7 @@ public class SearchOptionsTest {
|
||||
|
||||
@Test
|
||||
public void max_page_size() {
|
||||
SearchOptions options = new SearchOptions().setPage(3, QueryContext.MAX_LIMIT + 10);
|
||||
SearchOptions options = new SearchOptions().setPage(3, QueryContext.MAX_LIMIT);
|
||||
assertThat(options.getOffset()).isEqualTo(QueryContext.MAX_LIMIT * 2);
|
||||
assertThat(options.getLimit()).isEqualTo(QueryContext.MAX_LIMIT);
|
||||
}
|
||||
|
@ -915,22 +915,6 @@ public class IssueIndexTest {
|
||||
assertThat(result.getTotal()).isEqualTo(12);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void search_with_max_limit() {
|
||||
ComponentDto project = ComponentTesting.newProjectDto();
|
||||
ComponentDto file = ComponentTesting.newFileDto(project);
|
||||
List<IssueDoc> issues = newArrayList();
|
||||
for (int i = 0; i < 500; i++) {
|
||||
String key = "ISSUE" + i;
|
||||
issues.add(IssueTesting.newDoc(key, file));
|
||||
}
|
||||
indexIssues(issues.toArray(new IssueDoc[] {}));
|
||||
|
||||
IssueQuery.Builder query = IssueQuery.builder(userSessionRule);
|
||||
SearchResult<IssueDoc> result = index.search(query.build(), new SearchOptions().setLimit(Integer.MAX_VALUE));
|
||||
assertThat(result.getDocs()).hasSize(SearchOptions.MAX_LIMIT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sort_by_status() {
|
||||
ComponentDto project = ComponentTesting.newProjectDto();
|
||||
|
@ -25,6 +25,7 @@ import org.junit.Before;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.sonar.api.issue.Issue;
|
||||
import org.sonar.api.rule.RuleStatus;
|
||||
import org.sonar.api.security.DefaultGroups;
|
||||
@ -59,12 +60,14 @@ import org.sonar.server.ws.WsTester;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
|
||||
|
||||
public class SearchActionMediumTest {
|
||||
|
||||
@ClassRule
|
||||
public static ServerTester tester = new ServerTester().withStartupTasks().withEsIndexes();
|
||||
|
||||
@Rule
|
||||
public ExpectedException expectedException = ExpectedException.none();
|
||||
@Rule
|
||||
public UserSessionRule userSessionRule = UserSessionRule.forServerTester(tester);
|
||||
|
||||
@ -629,6 +632,17 @@ public class SearchActionMediumTest {
|
||||
result.assertJson(this.getClass(), "default_page_size_is_100.json");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fail_when_page_size_above_limit() throws Exception {
|
||||
expectedException.expect(IllegalArgumentException.class);
|
||||
|
||||
WsTester.TestRequest request = wsTester
|
||||
.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
|
||||
.setParam(WebService.Param.PAGE_SIZE, String.valueOf(MAX_LIMIT + 10));
|
||||
|
||||
request.execute();
|
||||
}
|
||||
|
||||
private RuleDto newRule() {
|
||||
RuleDto rule = RuleTesting.newXooX1()
|
||||
.setName("Rule name")
|
||||
|
@ -40,18 +40,18 @@ import org.sonar.db.DbSession;
|
||||
import org.sonar.db.DbTester;
|
||||
import org.sonar.db.component.ComponentDao;
|
||||
import org.sonar.db.component.ComponentDto;
|
||||
import org.sonar.db.component.ComponentTesting;
|
||||
import org.sonar.db.component.SnapshotDao;
|
||||
import org.sonar.db.component.SnapshotTesting;
|
||||
import org.sonar.db.measure.custom.CustomMeasureDao;
|
||||
import org.sonar.db.measure.custom.CustomMeasureDto;
|
||||
import org.sonar.db.metric.MetricDao;
|
||||
import org.sonar.db.metric.MetricDto;
|
||||
import org.sonar.server.component.ComponentFinder;
|
||||
import org.sonar.db.component.ComponentTesting;
|
||||
import org.sonar.db.component.SnapshotTesting;
|
||||
import org.sonar.server.db.DbClient;
|
||||
import org.sonar.server.es.EsTester;
|
||||
import org.sonar.server.exceptions.ForbiddenException;
|
||||
import org.sonar.server.exceptions.NotFoundException;
|
||||
import org.sonar.db.measure.custom.CustomMeasureDao;
|
||||
import org.sonar.db.metric.MetricDao;
|
||||
import org.sonar.server.tester.UserSessionRule;
|
||||
import org.sonar.server.user.index.UserDoc;
|
||||
import org.sonar.server.user.index.UserIndex;
|
||||
@ -62,6 +62,7 @@ import org.sonar.server.ws.WsTester;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.sonar.db.measure.custom.CustomMeasureTesting.newCustomMeasureDto;
|
||||
import static org.sonar.db.metric.MetricTesting.newMetricDto;
|
||||
import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
|
||||
|
||||
public class SearchActionTest {
|
||||
|
||||
@ -280,6 +281,16 @@ public class SearchActionTest {
|
||||
assertThat(response).contains("text-value-1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fail_when_above_page_size_limit() throws Exception {
|
||||
expectedException.expect(IllegalArgumentException.class);
|
||||
|
||||
newRequest()
|
||||
.setParam(SearchAction.PARAM_PROJECT_ID, DEFAULT_PROJECT_UUID)
|
||||
.setParam(WebService.Param.PAGE_SIZE, String.valueOf(MAX_LIMIT + 10))
|
||||
.execute();
|
||||
}
|
||||
|
||||
private ComponentDto insertDefaultProject() {
|
||||
return insertProject(DEFAULT_PROJECT_UUID, DEFAULT_PROJECT_KEY);
|
||||
}
|
||||
@ -302,7 +313,8 @@ public class SearchActionTest {
|
||||
}
|
||||
|
||||
private static MetricDto newCustomMetric(String metricKey) {
|
||||
return newMetricDto().setEnabled(true).setUserManaged(true).setKey(metricKey).setDomain(metricKey + "-domain").setShortName(metricKey + "-name").setValueType(ValueType.STRING.name());
|
||||
return newMetricDto().setEnabled(true).setUserManaged(true).setKey(metricKey).setDomain(metricKey + "-domain").setShortName(metricKey + "-name")
|
||||
.setValueType(ValueType.STRING.name());
|
||||
}
|
||||
|
||||
private CustomMeasureDto insertCustomMeasure(int id, MetricDto metric) {
|
||||
|
@ -1,5 +0,0 @@
|
||||
{
|
||||
"total": 501,
|
||||
"p": 1,
|
||||
"ps": 999999
|
||||
}
|
Loading…
Reference in New Issue
Block a user