From 180811d5714fba8d51c4602fc0832cc2bc0f1fe6 Mon Sep 17 00:00:00 2001 From: Harsh Garg Date: Fri, 20 Sep 2024 09:42:36 +0530 Subject: [PATCH] Adding AbstractListAction and renaming pagination related classes Signed-off-by: Harsh Garg --- .../org/opensearch/action/ActionModule.java | 7 +- .../java/org/opensearch/common/Table.java | 12 +- .../java/org/opensearch/rest/RestRequest.java | 16 +- .../rest/action/cat/AbstractCatAction.java | 29 +-- .../rest/action/cat/RestAliasAction.java | 2 +- .../rest/action/cat/RestAllocationAction.java | 2 +- .../action/cat/RestCatRecoveryAction.java | 2 +- .../cat/RestCatSegmentReplicationAction.java | 2 +- .../action/cat/RestClusterManagerAction.java | 2 +- .../rest/action/cat/RestCountAction.java | 2 +- .../rest/action/cat/RestFielddataAction.java | 2 +- .../rest/action/cat/RestHealthAction.java | 2 +- .../rest/action/cat/RestIndicesAction.java | 31 +-- .../opensearch/rest/action/cat/RestTable.java | 16 +- .../rest/action/list/AbstractListAction.java | 60 ++++++ .../action/list/RestIndicesListAction.java | 48 +++-- .../rest/action/list/RestListAction.java | 5 +- .../IndexBasedPaginationStrategy.java | 191 ------------------ .../pagination/IndexPaginationStrategy.java | 185 +++++++++++++++++ ...natedQueryRequest.java => PageParams.java} | 15 +- ...natedQueryResponse.java => PageToken.java} | 12 +- .../rest/pagination/PaginationStrategy.java | 4 +- ...java => IndexPaginationStrategyTests.java} | 160 +++++++-------- 23 files changed, 415 insertions(+), 392 deletions(-) create mode 100644 server/src/main/java/org/opensearch/rest/action/list/AbstractListAction.java delete mode 100644 server/src/main/java/org/opensearch/rest/pagination/IndexBasedPaginationStrategy.java create mode 100644 server/src/main/java/org/opensearch/rest/pagination/IndexPaginationStrategy.java rename server/src/main/java/org/opensearch/rest/pagination/{PaginatedQueryRequest.java => PageParams.java} (63%) rename server/src/main/java/org/opensearch/rest/pagination/{PaginatedQueryResponse.java => PageToken.java} (76%) rename server/src/test/java/org/opensearch/rest/pagination/{IndexBasedPaginationStrategyTests.java => IndexPaginationStrategyTests.java} (54%) diff --git a/server/src/main/java/org/opensearch/action/ActionModule.java b/server/src/main/java/org/opensearch/action/ActionModule.java index 9e31189ea8b5e..eb853e615a89c 100644 --- a/server/src/main/java/org/opensearch/action/ActionModule.java +++ b/server/src/main/java/org/opensearch/action/ActionModule.java @@ -459,6 +459,7 @@ import org.opensearch.rest.action.ingest.RestGetPipelineAction; import org.opensearch.rest.action.ingest.RestPutPipelineAction; import org.opensearch.rest.action.ingest.RestSimulatePipelineAction; +import org.opensearch.rest.action.list.AbstractListAction; import org.opensearch.rest.action.list.RestIndicesListAction; import org.opensearch.rest.action.list.RestListAction; import org.opensearch.rest.action.search.RestClearScrollAction; @@ -801,11 +802,11 @@ private ActionFilters setupActionFilters(List actionPlugins) { public void initRestHandlers(Supplier nodesInCluster) { List catActions = new ArrayList<>(); - List listActions = new ArrayList<>(); + List listActions = new ArrayList<>(); Consumer registerHandler = handler -> { if (handler instanceof AbstractCatAction) { - if (((AbstractCatAction) handler).isActionPaginated()) { - listActions.add((AbstractCatAction) handler); + if (handler instanceof AbstractListAction && ((AbstractListAction) handler).isActionPaginated()) { + listActions.add((AbstractListAction) handler); } else { catActions.add((AbstractCatAction) handler); } diff --git a/server/src/main/java/org/opensearch/common/Table.java b/server/src/main/java/org/opensearch/common/Table.java index 1ba238f8de45b..133ec3052e6c9 100644 --- a/server/src/main/java/org/opensearch/common/Table.java +++ b/server/src/main/java/org/opensearch/common/Table.java @@ -34,7 +34,7 @@ import org.opensearch.common.time.DateFormatter; import org.opensearch.core.common.Strings; -import org.opensearch.rest.pagination.PaginatedQueryResponse; +import org.opensearch.rest.pagination.PageToken; import java.time.Instant; import java.time.ZoneOffset; @@ -63,14 +63,14 @@ public class Table { /** * paginatedQueryResponse if null will imply the Table response is not paginated. */ - private PaginatedQueryResponse paginatedQueryResponse; + private PageToken pageToken; public static final String EPOCH = "epoch"; public static final String TIMESTAMP = "timestamp"; public Table() {} - public Table(@Nullable PaginatedQueryResponse paginatedQueryResponse) { - this.paginatedQueryResponse = paginatedQueryResponse; + public Table(@Nullable PageToken pageToken) { + this.pageToken = pageToken; } public Table startHeaders() { @@ -241,8 +241,8 @@ public Map getAliasMap() { return headerAliasMap; } - public PaginatedQueryResponse getPaginatedQueryResponse() { - return paginatedQueryResponse; + public PageToken getPageToken() { + return pageToken; } /** diff --git a/server/src/main/java/org/opensearch/rest/RestRequest.java b/server/src/main/java/org/opensearch/rest/RestRequest.java index 1b9f5708a00fe..f241b567c3204 100644 --- a/server/src/main/java/org/opensearch/rest/RestRequest.java +++ b/server/src/main/java/org/opensearch/rest/RestRequest.java @@ -51,7 +51,7 @@ import org.opensearch.core.xcontent.XContentParser; import org.opensearch.http.HttpChannel; import org.opensearch.http.HttpRequest; -import org.opensearch.rest.pagination.PaginatedQueryRequest; +import org.opensearch.rest.pagination.PageParams; import java.io.IOException; import java.io.InputStream; @@ -68,9 +68,9 @@ import static org.opensearch.common.unit.TimeValue.parseTimeValue; import static org.opensearch.core.common.unit.ByteSizeValue.parseBytesSizeValue; -import static org.opensearch.rest.pagination.PaginatedQueryRequest.PAGINATED_QUERY_PARAM_NEXT_TOKEN_KEY; -import static org.opensearch.rest.pagination.PaginatedQueryRequest.PAGINATED_QUERY_PARAM_SIZE_KEY; -import static org.opensearch.rest.pagination.PaginatedQueryRequest.PAGINATED_QUERY_PARAM_SORT_KEY; +import static org.opensearch.rest.pagination.PageParams.PARAM_NEXT_TOKEN; +import static org.opensearch.rest.pagination.PageParams.PARAM_SIZE; +import static org.opensearch.rest.pagination.PageParams.PARAM_SORT; /** * REST Request @@ -595,12 +595,8 @@ public static MediaType parseContentType(List header) { throw new IllegalArgumentException("empty Content-Type header"); } - public PaginatedQueryRequest parsePaginatedQueryParams(String defaultSortOrder, int defaultPageSize) { - return new PaginatedQueryRequest( - param(PAGINATED_QUERY_PARAM_NEXT_TOKEN_KEY), - param(PAGINATED_QUERY_PARAM_SORT_KEY, defaultSortOrder), - paramAsInt(PAGINATED_QUERY_PARAM_SIZE_KEY, defaultPageSize) - ); + public PageParams parsePaginatedQueryParams(String defaultSortOrder, int defaultPageSize) { + return new PageParams(param(PARAM_NEXT_TOKEN), param(PARAM_SORT, defaultSortOrder), paramAsInt(PARAM_SIZE, defaultPageSize)); } /** diff --git a/server/src/main/java/org/opensearch/rest/action/cat/AbstractCatAction.java b/server/src/main/java/org/opensearch/rest/action/cat/AbstractCatAction.java index c85a458d0f0c6..6f4e060363bfb 100644 --- a/server/src/main/java/org/opensearch/rest/action/cat/AbstractCatAction.java +++ b/server/src/main/java/org/opensearch/rest/action/cat/AbstractCatAction.java @@ -40,13 +40,11 @@ import org.opensearch.rest.BaseRestHandler; import org.opensearch.rest.BytesRestResponse; import org.opensearch.rest.RestRequest; -import org.opensearch.rest.pagination.PaginatedQueryRequest; import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; -import java.util.Objects; import java.util.Set; import static org.opensearch.rest.action.cat.RestTable.buildHelpWidths; @@ -59,11 +57,9 @@ */ public abstract class AbstractCatAction extends BaseRestHandler { - protected PaginatedQueryRequest paginatedQueryRequest; - protected abstract RestChannelConsumer doCatRequest(RestRequest request, NodeClient client); - public abstract void documentation(StringBuilder sb); + protected abstract void documentation(StringBuilder sb); protected abstract Table getTableWithHeader(RestRequest request); @@ -89,10 +85,6 @@ public RestChannelConsumer prepareRequest(final RestRequest request, final NodeC channel.sendResponse(new BytesRestResponse(RestStatus.OK, BytesRestResponse.TEXT_CONTENT_TYPE, bytesOutput.bytes())); }; } else { - if (isActionPaginated()) { - this.paginatedQueryRequest = validateAndGetPaginationMetadata(request); - assert Objects.nonNull(paginatedQueryRequest) : "paginatedQueryRequest can not be null for paginated queries"; - } return doCatRequest(request, client); } } @@ -106,23 +98,4 @@ protected Set responseParams() { return RESPONSE_PARAMS; } - /** - * - * @return boolean denoting whether the RestAction will output paginated responses or not. - * Is kept false by default, every paginated action to override and return true. - */ - public boolean isActionPaginated() { - return false; - } - - /** - * - * @return Metadata that can be extracted out from the rest request. Each paginated action to override and provide - * its own implementation. Query params supported by the action specific to pagination along with the respective validations, - * should be added here. - */ - protected PaginatedQueryRequest validateAndGetPaginationMetadata(RestRequest restRequest) { - return null; - } - } diff --git a/server/src/main/java/org/opensearch/rest/action/cat/RestAliasAction.java b/server/src/main/java/org/opensearch/rest/action/cat/RestAliasAction.java index a1cf1ca57c04f..4600dddbb361d 100644 --- a/server/src/main/java/org/opensearch/rest/action/cat/RestAliasAction.java +++ b/server/src/main/java/org/opensearch/rest/action/cat/RestAliasAction.java @@ -89,7 +89,7 @@ public RestResponse buildResponse(GetAliasesResponse response) throws Exception } @Override - public void documentation(StringBuilder sb) { + protected void documentation(StringBuilder sb) { sb.append("/_cat/aliases\n"); sb.append("/_cat/aliases/{alias}\n"); } diff --git a/server/src/main/java/org/opensearch/rest/action/cat/RestAllocationAction.java b/server/src/main/java/org/opensearch/rest/action/cat/RestAllocationAction.java index 2aa2b0aaad893..07b0fbbe4a911 100644 --- a/server/src/main/java/org/opensearch/rest/action/cat/RestAllocationAction.java +++ b/server/src/main/java/org/opensearch/rest/action/cat/RestAllocationAction.java @@ -78,7 +78,7 @@ public String getName() { } @Override - public void documentation(StringBuilder sb) { + protected void documentation(StringBuilder sb) { sb.append("/_cat/allocation\n"); } diff --git a/server/src/main/java/org/opensearch/rest/action/cat/RestCatRecoveryAction.java b/server/src/main/java/org/opensearch/rest/action/cat/RestCatRecoveryAction.java index 9e9b8c2e36b5b..26efd9929afea 100644 --- a/server/src/main/java/org/opensearch/rest/action/cat/RestCatRecoveryAction.java +++ b/server/src/main/java/org/opensearch/rest/action/cat/RestCatRecoveryAction.java @@ -77,7 +77,7 @@ public String getName() { } @Override - public void documentation(StringBuilder sb) { + protected void documentation(StringBuilder sb) { sb.append("/_cat/recovery\n"); sb.append("/_cat/recovery/{index}\n"); } diff --git a/server/src/main/java/org/opensearch/rest/action/cat/RestCatSegmentReplicationAction.java b/server/src/main/java/org/opensearch/rest/action/cat/RestCatSegmentReplicationAction.java index 2f76a68e7221e..aa325443ba6c9 100644 --- a/server/src/main/java/org/opensearch/rest/action/cat/RestCatSegmentReplicationAction.java +++ b/server/src/main/java/org/opensearch/rest/action/cat/RestCatSegmentReplicationAction.java @@ -58,7 +58,7 @@ public String getName() { } @Override - public void documentation(StringBuilder sb) { + protected void documentation(StringBuilder sb) { sb.append("/_cat/segment_replication\n"); sb.append("/_cat/segment_replication/{index}\n"); } diff --git a/server/src/main/java/org/opensearch/rest/action/cat/RestClusterManagerAction.java b/server/src/main/java/org/opensearch/rest/action/cat/RestClusterManagerAction.java index 3d6211ae50f1a..8f7f9e5bd20a7 100644 --- a/server/src/main/java/org/opensearch/rest/action/cat/RestClusterManagerAction.java +++ b/server/src/main/java/org/opensearch/rest/action/cat/RestClusterManagerAction.java @@ -69,7 +69,7 @@ public String getName() { } @Override - public void documentation(StringBuilder sb) { + protected void documentation(StringBuilder sb) { sb.append("/_cat/cluster_manager\n"); } diff --git a/server/src/main/java/org/opensearch/rest/action/cat/RestCountAction.java b/server/src/main/java/org/opensearch/rest/action/cat/RestCountAction.java index 781cfa425f369..9c054ffe1bcc7 100644 --- a/server/src/main/java/org/opensearch/rest/action/cat/RestCountAction.java +++ b/server/src/main/java/org/opensearch/rest/action/cat/RestCountAction.java @@ -71,7 +71,7 @@ public String getName() { } @Override - public void documentation(StringBuilder sb) { + protected void documentation(StringBuilder sb) { sb.append("/_cat/count\n"); sb.append("/_cat/count/{index}\n"); } diff --git a/server/src/main/java/org/opensearch/rest/action/cat/RestFielddataAction.java b/server/src/main/java/org/opensearch/rest/action/cat/RestFielddataAction.java index cd54b169230d0..04bbdeeadc4c4 100644 --- a/server/src/main/java/org/opensearch/rest/action/cat/RestFielddataAction.java +++ b/server/src/main/java/org/opensearch/rest/action/cat/RestFielddataAction.java @@ -82,7 +82,7 @@ public RestResponse buildResponse(NodesStatsResponse nodeStatses) throws Excepti } @Override - public void documentation(StringBuilder sb) { + protected void documentation(StringBuilder sb) { sb.append("/_cat/fielddata\n"); sb.append("/_cat/fielddata/{fields}\n"); } diff --git a/server/src/main/java/org/opensearch/rest/action/cat/RestHealthAction.java b/server/src/main/java/org/opensearch/rest/action/cat/RestHealthAction.java index 898c3a05cec34..b4d336f4c10c0 100644 --- a/server/src/main/java/org/opensearch/rest/action/cat/RestHealthAction.java +++ b/server/src/main/java/org/opensearch/rest/action/cat/RestHealthAction.java @@ -69,7 +69,7 @@ public boolean allowSystemIndexAccessByDefault() { } @Override - public void documentation(StringBuilder sb) { + protected void documentation(StringBuilder sb) { sb.append("/_cat/health\n"); } diff --git a/server/src/main/java/org/opensearch/rest/action/cat/RestIndicesAction.java b/server/src/main/java/org/opensearch/rest/action/cat/RestIndicesAction.java index 6de67bb7d1add..8bf8e201cfc4d 100644 --- a/server/src/main/java/org/opensearch/rest/action/cat/RestIndicesAction.java +++ b/server/src/main/java/org/opensearch/rest/action/cat/RestIndicesAction.java @@ -61,8 +61,9 @@ import org.opensearch.rest.RestRequest; import org.opensearch.rest.RestResponse; import org.opensearch.rest.action.RestResponseListener; -import org.opensearch.rest.pagination.IndexBasedPaginationStrategy; -import org.opensearch.rest.pagination.PaginatedQueryResponse; +import org.opensearch.rest.action.list.AbstractListAction; +import org.opensearch.rest.pagination.IndexPaginationStrategy; +import org.opensearch.rest.pagination.PageToken; import java.time.Instant; import java.time.ZoneOffset; @@ -89,7 +90,7 @@ * * @opensearch.api */ -public class RestIndicesAction extends AbstractCatAction { +public class RestIndicesAction extends AbstractListAction { private static final DateFormatter STRICT_DATE_TIME_FORMATTER = DateFormatter.forPattern("strict_date_time"); private static final DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(RestIndicesAction.class); @@ -114,7 +115,7 @@ public boolean allowSystemIndexAccessByDefault() { } @Override - public void documentation(StringBuilder sb) { + protected void documentation(StringBuilder sb) { sb.append("/_cat/indices\n"); sb.append("/_cat/indices/{index}\n"); } @@ -177,14 +178,14 @@ public void onResponse(final GetSettingsResponse getSettingsResponse) { new ActionListener() { @Override public void onResponse(ClusterStateResponse clusterStateResponse) { - IndexBasedPaginationStrategy paginationStrategy = getPaginationStrategy(clusterStateResponse); + IndexPaginationStrategy paginationStrategy = getPaginationStrategy(clusterStateResponse); final String[] indicesToBeQueried = getIndicesToBeQueried(indices, paginationStrategy); final GroupedActionListener groupedListener = createGroupedListener( request, 4, listener, indicesToBeQueried, - getPaginatedQueryResponse(paginationStrategy) + getPageToken(paginationStrategy) ); groupedListener.onResponse(getSettingsResponse); groupedListener.onResponse(clusterStateResponse); @@ -309,7 +310,7 @@ private GroupedActionListener createGroupedListener( final int size, final ActionListener listener, final String[] indicesToBeQueried, - final PaginatedQueryResponse paginatedQueryResponse + final PageToken pageToken ) { return new GroupedActionListener<>(new ActionListener>() { @Override @@ -340,7 +341,7 @@ public void onResponse(final Collection responses) { indicesStats, indicesStates, indicesToBeQueried, - paginatedQueryResponse + pageToken ); listener.onResponse(responseTable); } catch (Exception e) { @@ -373,8 +374,8 @@ protected Table getTableWithHeader(final RestRequest request) { return getTableWithHeader(request, null); } - protected Table getTableWithHeader(final RestRequest request, final PaginatedQueryResponse paginatedQueryResponse) { - Table table = new Table(paginatedQueryResponse); + protected Table getTableWithHeader(final RestRequest request, final PageToken pageToken) { + Table table = new Table(pageToken); table.startHeaders(); table.addCell("health", "alias:h;desc:current health status"); table.addCell("status", "alias:s;desc:open/close status"); @@ -745,10 +746,10 @@ protected Table buildTable( final Map indicesStats, final Map indicesMetadatas, final String[] indicesToBeQueried, - final PaginatedQueryResponse paginatedQueryResponse + final PageToken pageToken ) { final String healthParam = request.param("health"); - final Table table = getTableWithHeader(request, paginatedQueryResponse); + final Table table = getTableWithHeader(request, pageToken); indicesSettings.forEach((indexName, settings) -> { if (indicesMetadatas.containsKey(indexName) == false) { @@ -1039,15 +1040,15 @@ private static A extractResponse(final Collection displayHeaders = buildDisplayHeaders(table, request); - if (Objects.nonNull(table.getPaginatedQueryResponse())) { - assert Objects.nonNull(table.getPaginatedQueryResponse().getPaginatedElement()) + if (Objects.nonNull(table.getPageToken())) { + assert Objects.nonNull(table.getPageToken().getPaginatedEntity()) : "Paginated element is required in-case of paginated responses"; builder.startObject(); - builder.field(PAGINATED_RESPONSE_NEXT_TOKEN_KEY, table.getPaginatedQueryResponse().getNextToken()); - builder.startArray(table.getPaginatedQueryResponse().getPaginatedElement()); + builder.field(PAGINATED_RESPONSE_NEXT_TOKEN_KEY, table.getPageToken().getNextToken()); + builder.startArray(table.getPageToken().getPaginatedEntity()); } else { builder.startArray(); } @@ -109,7 +109,7 @@ public static RestResponse buildXContentBuilder(Table table, RestChannel channel builder.endObject(); } builder.endArray(); - if (Objects.nonNull(table.getPaginatedQueryResponse())) { + if (Objects.nonNull(table.getPageToken())) { builder.endObject(); } return new BytesRestResponse(RestStatus.OK, builder); @@ -151,8 +151,8 @@ public static RestResponse buildTextPlainResponse(Table table, RestChannel chann out.append("\n"); } // Adding a new row for next_token, in the response if the table is paginated. - if (Objects.nonNull(table.getPaginatedQueryResponse())) { - out.append("next_token" + " " + table.getPaginatedQueryResponse().getNextToken()); + if (Objects.nonNull(table.getPageToken())) { + out.append("next_token" + " " + table.getPageToken().getNextToken()); out.append("\n"); } out.close(); diff --git a/server/src/main/java/org/opensearch/rest/action/list/AbstractListAction.java b/server/src/main/java/org/opensearch/rest/action/list/AbstractListAction.java new file mode 100644 index 0000000000000..1f5be657f425d --- /dev/null +++ b/server/src/main/java/org/opensearch/rest/action/list/AbstractListAction.java @@ -0,0 +1,60 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.rest.action.list; + +import org.opensearch.client.node.NodeClient; +import org.opensearch.rest.RestRequest; +import org.opensearch.rest.action.cat.AbstractCatAction; +import org.opensearch.rest.pagination.PageParams; + +import java.io.IOException; +import java.util.Objects; + +/** + * Base Transport action class for _list API. + * + * @opensearch.api + */ +public abstract class AbstractListAction extends AbstractCatAction { + + protected PageParams pageParams; + + protected abstract void documentation(StringBuilder sb); + + @Override + public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException { + boolean helpWanted = request.paramAsBoolean("help", false); + if (helpWanted || isActionPaginated() == false) { + return super.prepareRequest(request, client); + } + this.pageParams = validateAndGetPageParams(request); + assert Objects.nonNull(pageParams) : "pageParams can not be null for paginated queries"; + return doCatRequest(request, client); + } + + /** + * + * @return boolean denoting whether the RestAction will output paginated responses or not. + * Is kept false by default, every paginated action to override and return true. + */ + public boolean isActionPaginated() { + return false; + } + + /** + * + * @return Metadata that can be extracted out from the rest request. Each paginated action to override and provide + * its own implementation. Query params supported by the action specific to pagination along with the respective validations, + * should be added here. + */ + protected PageParams validateAndGetPageParams(RestRequest restRequest) { + return null; + } + +} diff --git a/server/src/main/java/org/opensearch/rest/action/list/RestIndicesListAction.java b/server/src/main/java/org/opensearch/rest/action/list/RestIndicesListAction.java index a6c6775b0f47a..e70fdd77d8894 100644 --- a/server/src/main/java/org/opensearch/rest/action/list/RestIndicesListAction.java +++ b/server/src/main/java/org/opensearch/rest/action/list/RestIndicesListAction.java @@ -16,9 +16,9 @@ import org.opensearch.common.settings.Settings; import org.opensearch.rest.RestRequest; import org.opensearch.rest.action.cat.RestIndicesAction; -import org.opensearch.rest.pagination.IndexBasedPaginationStrategy; -import org.opensearch.rest.pagination.PaginatedQueryRequest; -import org.opensearch.rest.pagination.PaginatedQueryResponse; +import org.opensearch.rest.pagination.IndexPaginationStrategy; +import org.opensearch.rest.pagination.PageParams; +import org.opensearch.rest.pagination.PageToken; import java.util.List; import java.util.Map; @@ -27,6 +27,8 @@ import static java.util.Arrays.asList; import static java.util.Collections.unmodifiableList; import static org.opensearch.rest.RestRequest.Method.GET; +import static org.opensearch.rest.pagination.PageParams.PARAM_ASC_SORT_VALUE; +import static org.opensearch.rest.pagination.PageParams.PARAM_DESC_SORT_VALUE; /** * _list API action to output indices in pages. @@ -35,8 +37,8 @@ */ public class RestIndicesListAction extends RestIndicesAction { - protected static final int MAX_SUPPORTED_LIST_INDICES_PAGE_SIZE_STRING = 1000; - protected static final int DEFAULT_LIST_INDICES_PAGE_SIZE_STRING = 1000; + protected static final int MAX_SUPPORTED_LIST_INDICES_PAGE_SIZE_STRING = 5000; + protected static final int DEFAULT_LIST_INDICES_PAGE_SIZE_STRING = 500; @Override public List routes() { @@ -60,28 +62,24 @@ public boolean isActionPaginated() { } @Override - protected PaginatedQueryRequest validateAndGetPaginationMetadata(RestRequest restRequest) { - PaginatedQueryRequest paginatedQueryRequest = restRequest.parsePaginatedQueryParams( - "ascending", - DEFAULT_LIST_INDICES_PAGE_SIZE_STRING - ); + protected PageParams validateAndGetPageParams(RestRequest restRequest) { + PageParams pageParams = restRequest.parsePaginatedQueryParams(PARAM_ASC_SORT_VALUE, DEFAULT_LIST_INDICES_PAGE_SIZE_STRING); // validating pageSize - if (paginatedQueryRequest.getSize() <= 0) { + if (pageParams.getSize() <= 0) { throw new IllegalArgumentException("size must be greater than zero"); - } else if (paginatedQueryRequest.getSize() > MAX_SUPPORTED_LIST_INDICES_PAGE_SIZE_STRING) { + } else if (pageParams.getSize() > MAX_SUPPORTED_LIST_INDICES_PAGE_SIZE_STRING) { throw new IllegalArgumentException("size should be less than [" + MAX_SUPPORTED_LIST_INDICES_PAGE_SIZE_STRING + "]"); } // Validating sort order - if (!Objects.equals(paginatedQueryRequest.getSort(), "ascending") - && !Objects.equals(paginatedQueryRequest.getSort(), "descending")) { - throw new IllegalArgumentException("value of sort can either be ascending or descending"); + if (!(PARAM_ASC_SORT_VALUE.equals(pageParams.getSort()) || PARAM_DESC_SORT_VALUE.equals(pageParams.getSort()))) { + throw new IllegalArgumentException("value of sort can either be asc or desc"); } // Next Token in the request will be validated by the IndexStrategyTokenParser itself. - if (Objects.nonNull(paginatedQueryRequest.getRequestedTokenStr())) { - IndexBasedPaginationStrategy.IndexStrategyToken.validateIndexStrategyToken(paginatedQueryRequest.getRequestedTokenStr()); + if (Objects.nonNull(pageParams.getRequestedToken())) { + IndexPaginationStrategy.IndexStrategyToken.validateIndexStrategyToken(pageParams.getRequestedToken()); } - return paginatedQueryRequest; + return pageParams; } @Override @@ -92,7 +90,7 @@ protected Table buildTable( final Map indicesStats, final Map indicesMetadatas, final String[] indicesToBeQueried, - final PaginatedQueryResponse paginatedQueryResponse + final PageToken paginatedQueryResponse ) { final String healthParam = request.param("health"); final Table table = getTableWithHeader(request, paginatedQueryResponse); @@ -106,16 +104,16 @@ protected Table buildTable( } @Override - protected IndexBasedPaginationStrategy getPaginationStrategy(ClusterStateResponse clusterStateResponse) { - return new IndexBasedPaginationStrategy(paginatedQueryRequest, clusterStateResponse.getState()); + protected IndexPaginationStrategy getPaginationStrategy(ClusterStateResponse clusterStateResponse) { + return new IndexPaginationStrategy(pageParams, clusterStateResponse.getState()); } @Override - protected PaginatedQueryResponse getPaginatedQueryResponse(IndexBasedPaginationStrategy paginationStrategy) { - return paginationStrategy.getPaginatedQueryResponse(); + protected PageToken getPageToken(IndexPaginationStrategy paginationStrategy) { + return paginationStrategy.getResponseToken(); } - protected String[] getIndicesToBeQueried(String[] indices, IndexBasedPaginationStrategy paginationStrategy) { - return paginationStrategy.getElementsFromRequestedToken().toArray(new String[0]); + protected String[] getIndicesToBeQueried(String[] indices, IndexPaginationStrategy paginationStrategy) { + return paginationStrategy.getRequestedEntities().toArray(new String[0]); } } diff --git a/server/src/main/java/org/opensearch/rest/action/list/RestListAction.java b/server/src/main/java/org/opensearch/rest/action/list/RestListAction.java index 32e5824b45cec..4b8551ea7e14a 100644 --- a/server/src/main/java/org/opensearch/rest/action/list/RestListAction.java +++ b/server/src/main/java/org/opensearch/rest/action/list/RestListAction.java @@ -13,7 +13,6 @@ import org.opensearch.rest.BaseRestHandler; import org.opensearch.rest.BytesRestResponse; import org.opensearch.rest.RestRequest; -import org.opensearch.rest.action.cat.AbstractCatAction; import java.io.IOException; import java.util.List; @@ -32,10 +31,10 @@ public class RestListAction extends BaseRestHandler { private static final String LIST_NL = LIST + "\n"; private final String HELP; - public RestListAction(List listActions) { + public RestListAction(List listActions) { StringBuilder sb = new StringBuilder(); sb.append(LIST_NL); - for (AbstractCatAction listAction : listActions) { + for (AbstractListAction listAction : listActions) { listAction.documentation(sb); } HELP = sb.toString(); diff --git a/server/src/main/java/org/opensearch/rest/pagination/IndexBasedPaginationStrategy.java b/server/src/main/java/org/opensearch/rest/pagination/IndexBasedPaginationStrategy.java deleted file mode 100644 index 02c08a7a46a3b..0000000000000 --- a/server/src/main/java/org/opensearch/rest/pagination/IndexBasedPaginationStrategy.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -package org.opensearch.rest.pagination; - -import org.opensearch.OpenSearchParseException; -import org.opensearch.cluster.ClusterState; -import org.opensearch.cluster.metadata.IndexMetadata; -import org.opensearch.common.Nullable; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Objects; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -import static org.opensearch.rest.pagination.PaginatedQueryRequest.PAGINATED_QUERY_ASCENDING_SORT; - -/** - * This strategy can be used by the Rest APIs wanting to paginate the responses based on Indices. - * The strategy considers create timestamps of indices as the keys to iterate over pages. - * - * @opensearch.internal - */ -public class IndexBasedPaginationStrategy implements PaginationStrategy { - - private final PaginatedQueryResponse paginatedQueryResponse; - private final List indicesFromRequestedToken; - - private static final String DEFAULT_INDICES_PAGINATED_ELEMENT = "indices"; - - public IndexBasedPaginationStrategy(PaginatedQueryRequest paginatedQueryRequest, ClusterState clusterState) { - // Get list of indices metadata sorted by their creation time and filtered by the last send index - List sortedIndicesList = PaginationStrategy.getSortedIndexMetadata( - clusterState, - getMetadataListFilter(paginatedQueryRequest.getRequestedTokenStr(), paginatedQueryRequest.getSort()), - getMetadataListComparator(paginatedQueryRequest.getSort()) - ); - List metadataListForRequestedToken = getMetadataListForRequestedToken(sortedIndicesList, paginatedQueryRequest); - this.indicesFromRequestedToken = metadataListForRequestedToken.stream() - .map(metadata -> metadata.getIndex().getName()) - .collect(Collectors.toList()); - this.paginatedQueryResponse = getPaginatedResponseForRequestedToken(paginatedQueryRequest.getSize(), sortedIndicesList); - } - - private static Predicate getMetadataListFilter(String requestedTokenStr, String sortOrder) { - boolean isAscendingSort = sortOrder.equals(PAGINATED_QUERY_ASCENDING_SORT); - IndexStrategyToken requestedToken = Objects.isNull(requestedTokenStr) || requestedTokenStr.isEmpty() - ? null - : new IndexStrategyToken(requestedTokenStr); - if (Objects.isNull(requestedToken)) { - return indexMetadata -> true; - } - return indexMetadata -> { - if (indexMetadata.getIndex().getName().equals(requestedToken.nameOfLastRespondedIndex)) { - return false; - } else if (indexMetadata.getCreationDate() == requestedToken.creationTimeOfLastRespondedIndex) { - return isAscendingSort - ? indexMetadata.getIndex().getName().compareTo(requestedToken.nameOfLastRespondedIndex) > 0 - : indexMetadata.getIndex().getName().compareTo(requestedToken.nameOfLastRespondedIndex) < 0; - } - return isAscendingSort - ? indexMetadata.getCreationDate() > requestedToken.creationTimeOfLastRespondedIndex - : indexMetadata.getCreationDate() < requestedToken.creationTimeOfLastRespondedIndex; - }; - } - - private static Comparator getMetadataListComparator(String sortOrder) { - boolean isAscendingSort = sortOrder.equals(PAGINATED_QUERY_ASCENDING_SORT); - return (metadata1, metadata2) -> { - if (metadata1.getCreationDate() == metadata2.getCreationDate()) { - return isAscendingSort - ? metadata1.getIndex().getName().compareTo(metadata2.getIndex().getName()) - : metadata2.getIndex().getName().compareTo(metadata1.getIndex().getName()); - } - return isAscendingSort - ? Long.compare(metadata1.getCreationDate(), metadata2.getCreationDate()) - : Long.compare(metadata2.getCreationDate(), metadata1.getCreationDate()); - }; - } - - private List getMetadataListForRequestedToken( - List sortedIndicesList, - PaginatedQueryRequest paginatedQueryRequest - ) { - if (sortedIndicesList.isEmpty()) { - return new ArrayList<>(); - } - return sortedIndicesList.subList(0, Math.min(paginatedQueryRequest.getSize(), sortedIndicesList.size())); - } - - private PaginatedQueryResponse getPaginatedResponseForRequestedToken(int pageSize, List sortedIndicesList) { - if (sortedIndicesList.size() <= pageSize) { - return new PaginatedQueryResponse(null, DEFAULT_INDICES_PAGINATED_ELEMENT); - } - return new PaginatedQueryResponse( - new IndexStrategyToken( - sortedIndicesList.get(pageSize - 1).getCreationDate(), - sortedIndicesList.get(pageSize - 1).getIndex().getName() - ).generateEncryptedToken(), - DEFAULT_INDICES_PAGINATED_ELEMENT - ); - } - - @Override - @Nullable - public PaginatedQueryResponse getPaginatedQueryResponse() { - return paginatedQueryResponse; - } - - @Override - public List getElementsFromRequestedToken() { - return Objects.isNull(indicesFromRequestedToken) ? new ArrayList<>() : indicesFromRequestedToken; - } - - /** - * TokenParser to be used by {@link IndexBasedPaginationStrategy}. - * Token would like: IndexNumberToStartTheNextPageFrom + | + CreationTimeOfLastRespondedIndex + | + - * QueryStartTime + | + NameOfLastRespondedIndex - */ - public static class IndexStrategyToken { - - private static final String TOKEN_JOIN_DELIMITER = "|"; - private static final String TOKEN_SPLIT_REGEX = "\\|"; - private static final int CREATION_TIME_FIELD_POSITION_IN_TOKEN = 0; - private static final int INDEX_NAME_FIELD_POSITION_IN_TOKEN = 1; - - /** - * Represents creation times of last index which was displayed in the previous page. - * Used to identify the new start point in case the indices get created/deleted while queries are executed. - */ - private final long creationTimeOfLastRespondedIndex; - - /** - * Represents name of the last index which was displayed in the previous page. - * Used to identify whether the sorted list of indices has changed or not. - */ - private final String nameOfLastRespondedIndex; - - public IndexStrategyToken(String requestedTokenString) { - validateIndexStrategyToken(requestedTokenString); - String decryptedToken = PaginationStrategy.decryptStringToken(requestedTokenString); - final String[] decryptedTokenElements = decryptedToken.split(TOKEN_SPLIT_REGEX); - this.creationTimeOfLastRespondedIndex = Long.parseLong(decryptedTokenElements[CREATION_TIME_FIELD_POSITION_IN_TOKEN]); - this.nameOfLastRespondedIndex = decryptedTokenElements[INDEX_NAME_FIELD_POSITION_IN_TOKEN]; - } - - public IndexStrategyToken(long creationTimeOfLastRespondedIndex, String nameOfLastRespondedIndex) { - Objects.requireNonNull(nameOfLastRespondedIndex, "index name should be provided"); - this.creationTimeOfLastRespondedIndex = creationTimeOfLastRespondedIndex; - this.nameOfLastRespondedIndex = nameOfLastRespondedIndex; - } - - public String generateEncryptedToken() { - return PaginationStrategy.encryptStringToken( - String.join(TOKEN_JOIN_DELIMITER, String.valueOf(creationTimeOfLastRespondedIndex), nameOfLastRespondedIndex) - ); - } - - /** - * Will perform simple validations on token received in the request and initialize the data members. - * The token should be base64 encoded, and should contain the expected number of elements separated by "$". - * The timestamps should also be a valid long. - * - * @param requestedTokenString string denoting the encoded next token requested by the user - */ - public static void validateIndexStrategyToken(String requestedTokenString) { - Objects.requireNonNull(requestedTokenString, "requestedTokenString can not be null"); - String decryptedToken = PaginationStrategy.decryptStringToken(requestedTokenString); - final String[] decryptedTokenElements = decryptedToken.split(TOKEN_SPLIT_REGEX); - if (decryptedTokenElements.length != 2) { - throw new OpenSearchParseException(INCORRECT_TAINTED_NEXT_TOKEN_ERROR_MESSAGE); - } - try { - long creationTimeOfLastRespondedIndex = Long.parseLong(decryptedTokenElements[CREATION_TIME_FIELD_POSITION_IN_TOKEN]); - if (creationTimeOfLastRespondedIndex <= 0) { - throw new OpenSearchParseException(INCORRECT_TAINTED_NEXT_TOKEN_ERROR_MESSAGE); - } - } catch (NumberFormatException exception) { - throw new OpenSearchParseException(INCORRECT_TAINTED_NEXT_TOKEN_ERROR_MESSAGE); - } - } - } - -} diff --git a/server/src/main/java/org/opensearch/rest/pagination/IndexPaginationStrategy.java b/server/src/main/java/org/opensearch/rest/pagination/IndexPaginationStrategy.java new file mode 100644 index 0000000000000..3a84226dc85cf --- /dev/null +++ b/server/src/main/java/org/opensearch/rest/pagination/IndexPaginationStrategy.java @@ -0,0 +1,185 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.rest.pagination; + +import org.opensearch.OpenSearchParseException; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.common.Nullable; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import static org.opensearch.rest.pagination.PageParams.PARAM_ASC_SORT_VALUE; + +/** + * This strategy can be used by the Rest APIs wanting to paginate the responses based on Indices. + * The strategy considers create timestamps of indices as the keys to iterate over pages. + * + * @opensearch.internal + */ +public class IndexPaginationStrategy implements PaginationStrategy { + private static final String DEFAULT_INDICES_PAGINATED_ELEMENT = "indices"; + + private static final Comparator ASC_COMPARATOR = (metadata1, metadata2) -> { + if (metadata1.getCreationDate() == metadata2.getCreationDate()) { + return metadata1.getIndex().getName().compareTo(metadata2.getIndex().getName()); + } + return Long.compare(metadata1.getCreationDate(), metadata2.getCreationDate()); + }; + private static final Comparator DESC_COMPARATOR = (metadata1, metadata2) -> { + if (metadata1.getCreationDate() == metadata2.getCreationDate()) { + return metadata2.getIndex().getName().compareTo(metadata1.getIndex().getName()); + } + return Long.compare(metadata2.getCreationDate(), metadata1.getCreationDate()); + }; + + private final PageToken pageToken; + private final List requestedIndices; + + public IndexPaginationStrategy(PageParams pageParams, ClusterState clusterState) { + // Get list of indices metadata sorted by their creation time and filtered by the last sent index + List sortedIndices = PaginationStrategy.getSortedIndexMetadata( + clusterState, + getMetadataFilter(pageParams.getRequestedToken(), pageParams.getSort()), + PARAM_ASC_SORT_VALUE.equals(pageParams.getSort()) ? ASC_COMPARATOR : DESC_COMPARATOR + ); + // Trim sortedIndicesList to get the list of indices metadata to be sent as response + List metadataSublist = getMetadataSubList(sortedIndices, pageParams.getSize()); + // Get list of index names from the trimmed metadataSublist + this.requestedIndices = metadataSublist.stream().map(metadata -> metadata.getIndex().getName()).collect(Collectors.toList()); + this.pageToken = getResponseToken( + pageParams.getSize(), + sortedIndices.size(), + metadataSublist.isEmpty() ? null : metadataSublist.get(metadataSublist.size() - 1) + ); + } + + private static Predicate getMetadataFilter(String requestedTokenStr, String sortOrder) { + boolean isAscendingSort = sortOrder.equals(PARAM_ASC_SORT_VALUE); + IndexStrategyToken requestedToken = Objects.isNull(requestedTokenStr) || requestedTokenStr.isEmpty() + ? null + : new IndexStrategyToken(requestedTokenStr); + if (Objects.isNull(requestedToken)) { + return indexMetadata -> true; + } + return metadata -> { + if (metadata.getIndex().getName().equals(requestedToken.lastIndexName)) { + return false; + } else if (metadata.getCreationDate() == requestedToken.lastIndexCreationTime) { + return isAscendingSort + ? metadata.getIndex().getName().compareTo(requestedToken.lastIndexName) > 0 + : metadata.getIndex().getName().compareTo(requestedToken.lastIndexName) < 0; + } + return isAscendingSort + ? metadata.getCreationDate() > requestedToken.lastIndexCreationTime + : metadata.getCreationDate() < requestedToken.lastIndexCreationTime; + }; + } + + private List getMetadataSubList(List sortedIndices, final int pageSize) { + if (sortedIndices.isEmpty()) { + return new ArrayList<>(); + } + return sortedIndices.subList(0, Math.min(pageSize, sortedIndices.size())); + } + + private PageToken getResponseToken(final int pageSize, final int totalIndices, IndexMetadata lastIndex) { + if (totalIndices <= pageSize) { + return new PageToken(null, DEFAULT_INDICES_PAGINATED_ELEMENT); + } + return new PageToken( + new IndexStrategyToken(lastIndex.getCreationDate(), lastIndex.getIndex().getName()).generateEncryptedToken(), + DEFAULT_INDICES_PAGINATED_ELEMENT + ); + } + + @Override + @Nullable + public PageToken getResponseToken() { + return pageToken; + } + + @Override + public List getRequestedEntities() { + return Objects.isNull(requestedIndices) ? new ArrayList<>() : requestedIndices; + } + + /** + * TokenParser to be used by {@link IndexPaginationStrategy}. + * Token would look like: IndexNumberToStartTheNextPageFrom + | + CreationTimeOfLastRespondedIndex + | + + * QueryStartTime + | + NameOfLastRespondedIndex + */ + public static class IndexStrategyToken { + + private static final String JOIN_DELIMITER = "|"; + private static final String SPLIT_REGEX = "\\|"; + private static final int CREATE_TIME_POS_IN_TOKEN = 0; + private static final int INDEX_NAME_POS_IN_TOKEN = 1; + + /** + * Represents creation times of last index which was displayed in the page. + * Used to identify the new start point in case the indices get created/deleted while queries are executed. + */ + private final long lastIndexCreationTime; + + /** + * Represents name of the last index which was displayed in the page. + * Used to identify whether the sorted list of indices has changed or not. + */ + private final String lastIndexName; + + public IndexStrategyToken(String requestedTokenString) { + validateIndexStrategyToken(requestedTokenString); + String decryptedToken = PaginationStrategy.decryptStringToken(requestedTokenString); + final String[] decryptedTokenElements = decryptedToken.split(SPLIT_REGEX); + this.lastIndexCreationTime = Long.parseLong(decryptedTokenElements[CREATE_TIME_POS_IN_TOKEN]); + this.lastIndexName = decryptedTokenElements[INDEX_NAME_POS_IN_TOKEN]; + } + + public IndexStrategyToken(long creationTimeOfLastRespondedIndex, String nameOfLastRespondedIndex) { + Objects.requireNonNull(nameOfLastRespondedIndex, "index name should be provided"); + this.lastIndexCreationTime = creationTimeOfLastRespondedIndex; + this.lastIndexName = nameOfLastRespondedIndex; + } + + public String generateEncryptedToken() { + return PaginationStrategy.encryptStringToken(String.join(JOIN_DELIMITER, String.valueOf(lastIndexCreationTime), lastIndexName)); + } + + /** + * Will perform simple validations on token received in the request. + * Token should be base64 encoded, and should contain the expected number of elements separated by "|". + * Timestamps should also be a valid long. + * + * @param requestedTokenStr string denoting the encoded token requested by the user. + */ + public static void validateIndexStrategyToken(String requestedTokenStr) { + Objects.requireNonNull(requestedTokenStr, "requestedTokenString can not be null"); + String decryptedToken = PaginationStrategy.decryptStringToken(requestedTokenStr); + final String[] decryptedTokenElements = decryptedToken.split(SPLIT_REGEX); + if (decryptedTokenElements.length != 2) { + throw new OpenSearchParseException(INCORRECT_TAINTED_NEXT_TOKEN_ERROR_MESSAGE); + } + try { + long creationTimeOfLastRespondedIndex = Long.parseLong(decryptedTokenElements[CREATE_TIME_POS_IN_TOKEN]); + if (creationTimeOfLastRespondedIndex <= 0) { + throw new OpenSearchParseException(INCORRECT_TAINTED_NEXT_TOKEN_ERROR_MESSAGE); + } + } catch (NumberFormatException exception) { + throw new OpenSearchParseException(INCORRECT_TAINTED_NEXT_TOKEN_ERROR_MESSAGE); + } + } + } + +} diff --git a/server/src/main/java/org/opensearch/rest/pagination/PaginatedQueryRequest.java b/server/src/main/java/org/opensearch/rest/pagination/PageParams.java similarity index 63% rename from server/src/main/java/org/opensearch/rest/pagination/PaginatedQueryRequest.java rename to server/src/main/java/org/opensearch/rest/pagination/PageParams.java index eb18429676c1c..9b2074bc3fed0 100644 --- a/server/src/main/java/org/opensearch/rest/pagination/PaginatedQueryRequest.java +++ b/server/src/main/java/org/opensearch/rest/pagination/PageParams.java @@ -15,18 +15,19 @@ * Class specific to paginated queries, which will contain common query params required by a paginated API. */ @PublicApi(since = "3.0.0") -public class PaginatedQueryRequest { +public class PageParams { - public static final String PAGINATED_QUERY_PARAM_SORT_KEY = "sort"; - public static final String PAGINATED_QUERY_PARAM_NEXT_TOKEN_KEY = "next_token"; - public static final String PAGINATED_QUERY_PARAM_SIZE_KEY = "size"; - public static final String PAGINATED_QUERY_ASCENDING_SORT = "ascending"; + public static final String PARAM_SORT = "sort"; + public static final String PARAM_NEXT_TOKEN = "next_token"; + public static final String PARAM_SIZE = "size"; + public static final String PARAM_ASC_SORT_VALUE = "asc"; + public static final String PARAM_DESC_SORT_VALUE = "desc"; private final String requestedTokenStr; private final String sort; private final int size; - public PaginatedQueryRequest(String requestedToken, String sort, int size) { + public PageParams(String requestedToken, String sort, int size) { this.requestedTokenStr = requestedToken; this.sort = sort; this.size = size; @@ -36,7 +37,7 @@ public String getSort() { return sort; } - public String getRequestedTokenStr() { + public String getRequestedToken() { return requestedTokenStr; } diff --git a/server/src/main/java/org/opensearch/rest/pagination/PaginatedQueryResponse.java b/server/src/main/java/org/opensearch/rest/pagination/PageToken.java similarity index 76% rename from server/src/main/java/org/opensearch/rest/pagination/PaginatedQueryResponse.java rename to server/src/main/java/org/opensearch/rest/pagination/PageToken.java index b9abe839b7230..d62e1be695715 100644 --- a/server/src/main/java/org/opensearch/rest/pagination/PaginatedQueryResponse.java +++ b/server/src/main/java/org/opensearch/rest/pagination/PageToken.java @@ -12,7 +12,7 @@ * Pagination response metadata for a paginated query. * @opensearch.internal */ -public class PaginatedQueryResponse { +public class PageToken { public static final String PAGINATED_RESPONSE_NEXT_TOKEN_KEY = "next_token"; @@ -24,19 +24,19 @@ public class PaginatedQueryResponse { /** * String denoting the element which is being paginated (for e.g. shards, indices..). */ - private final String paginatedElement; + private final String paginatedEntity; - public PaginatedQueryResponse(String nextToken, String paginatedElement) { + public PageToken(String nextToken, String paginatedElement) { assert paginatedElement != null : "paginatedElement must be specified for a paginated response"; this.nextToken = nextToken; - this.paginatedElement = paginatedElement; + this.paginatedEntity = paginatedElement; } public String getNextToken() { return nextToken; } - public String getPaginatedElement() { - return paginatedElement; + public String getPaginatedEntity() { + return paginatedEntity; } } diff --git a/server/src/main/java/org/opensearch/rest/pagination/PaginationStrategy.java b/server/src/main/java/org/opensearch/rest/pagination/PaginationStrategy.java index 8675b85236755..7f9825a7cc09b 100644 --- a/server/src/main/java/org/opensearch/rest/pagination/PaginationStrategy.java +++ b/server/src/main/java/org/opensearch/rest/pagination/PaginationStrategy.java @@ -35,13 +35,13 @@ public interface PaginationStrategy { * * @return Base64 encoded string, which can be used to fetch next page of response. */ - PaginatedQueryResponse getPaginatedQueryResponse(); + PageToken getResponseToken(); /** * * @return List of elements fetched corresponding to the store and token received by the strategy. */ - List getElementsFromRequestedToken(); + List getRequestedEntities(); /** * diff --git a/server/src/test/java/org/opensearch/rest/pagination/IndexBasedPaginationStrategyTests.java b/server/src/test/java/org/opensearch/rest/pagination/IndexPaginationStrategyTests.java similarity index 54% rename from server/src/test/java/org/opensearch/rest/pagination/IndexBasedPaginationStrategyTests.java rename to server/src/test/java/org/opensearch/rest/pagination/IndexPaginationStrategyTests.java index 8def1420d86c4..575874cbe6a47 100644 --- a/server/src/test/java/org/opensearch/rest/pagination/IndexBasedPaginationStrategyTests.java +++ b/server/src/test/java/org/opensearch/rest/pagination/IndexPaginationStrategyTests.java @@ -27,7 +27,7 @@ import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_CREATION_DATE; import static com.carrotsearch.randomizedtesting.RandomizedTest.getRandom; -public class IndexBasedPaginationStrategyTests extends OpenSearchTestCase { +public class IndexPaginationStrategyTests extends OpenSearchTestCase { public void testRetrieveAllIndicesInAscendingOrder() { List indexNumberList = new ArrayList<>(); @@ -46,21 +46,21 @@ public void testRetrieveAllIndicesInAscendingOrder() { String requestedToken = null; int totalPagesToFetch = (int) Math.ceil(totalIndices / (pageSize * 1.0)); for (int pageNumber = 1; pageNumber <= totalPagesToFetch; pageNumber++) { - PaginatedQueryRequest paginatedQueryRequest = new PaginatedQueryRequest(requestedToken, "ascending", pageSize); - IndexBasedPaginationStrategy paginationStrategy = new IndexBasedPaginationStrategy(paginatedQueryRequest, clusterState); + PageParams pageParams = new PageParams(requestedToken, "asc", pageSize); + IndexPaginationStrategy paginationStrategy = new IndexPaginationStrategy(pageParams, clusterState); if (pageNumber < totalPagesToFetch) { - assertNotNull(paginationStrategy.getPaginatedQueryResponse().getNextToken()); + assertNotNull(paginationStrategy.getResponseToken().getNextToken()); } else { - assertNull(paginationStrategy.getPaginatedQueryResponse().getNextToken()); + assertNull(paginationStrategy.getResponseToken().getNextToken()); } - requestedToken = paginationStrategy.getPaginatedQueryResponse().getNextToken(); + requestedToken = paginationStrategy.getResponseToken().getNextToken(); // Asserting all the indices received int responseItr = 0; for (int indexNumber = (pageNumber - 1) * pageSize; indexNumber < Math.min(100, pageNumber * pageSize); indexNumber++) { - assertEquals("test-index-" + (indexNumber + 1), paginationStrategy.getElementsFromRequestedToken().get(responseItr)); + assertEquals("test-index-" + (indexNumber + 1), paginationStrategy.getRequestedEntities().get(responseItr)); responseItr++; } - assertEquals(responseItr, paginationStrategy.getElementsFromRequestedToken().size()); + assertEquals(responseItr, paginationStrategy.getRequestedEntities().size()); } } } @@ -83,22 +83,22 @@ public void testRetrieveAllIndicesInDescendingOrder() { int totalPagesToFetch = (int) Math.ceil(totalIndices / (pageSize * 1.0)); int startIndexNumber = totalIndices; for (int pageNumber = 1; pageNumber <= totalPagesToFetch; pageNumber++) { - PaginatedQueryRequest paginatedQueryRequest = new PaginatedQueryRequest(requestedToken, "descending", pageSize); - IndexBasedPaginationStrategy paginationStrategy = new IndexBasedPaginationStrategy(paginatedQueryRequest, clusterState); + PageParams pageParams = new PageParams(requestedToken, "desc", pageSize); + IndexPaginationStrategy paginationStrategy = new IndexPaginationStrategy(pageParams, clusterState); if (pageNumber < totalPagesToFetch) { - assertNotNull(paginationStrategy.getPaginatedQueryResponse().getNextToken()); + assertNotNull(paginationStrategy.getResponseToken().getNextToken()); } else { - assertNull(paginationStrategy.getPaginatedQueryResponse().getNextToken()); + assertNull(paginationStrategy.getResponseToken().getNextToken()); } - requestedToken = paginationStrategy.getPaginatedQueryResponse().getNextToken(); + requestedToken = paginationStrategy.getResponseToken().getNextToken(); // Asserting all the indices received int responseItr = 0; int endIndexNumberForPage = Math.max(startIndexNumber - pageSize, 0); for (; startIndexNumber > endIndexNumberForPage; startIndexNumber--) { - assertEquals("test-index-" + startIndexNumber, paginationStrategy.getElementsFromRequestedToken().get(responseItr)); + assertEquals("test-index-" + startIndexNumber, paginationStrategy.getRequestedEntities().get(responseItr)); responseItr++; } - assertEquals(responseItr, paginationStrategy.getElementsFromRequestedToken().size()); + assertEquals(responseItr, paginationStrategy.getRequestedEntities().size()); } } } @@ -106,108 +106,108 @@ public void testRetrieveAllIndicesInDescendingOrder() { public void testRetrieveAllIndicesWhenIndicesGetDeletedAndCreatedInBetween() { // Query1 with 4 indices in clusterState (test-index1,2,3,4) ClusterState clusterState = getRandomClusterState(List.of(1, 2, 3, 4)); - PaginatedQueryRequest paginatedQueryRequest = new PaginatedQueryRequest(null, "ascending", 1); - IndexBasedPaginationStrategy paginationStrategy = new IndexBasedPaginationStrategy(paginatedQueryRequest, clusterState); - assertEquals(1, paginationStrategy.getElementsFromRequestedToken().size()); - assertEquals("test-index-1", paginationStrategy.getElementsFromRequestedToken().get(0)); - assertNotNull(paginationStrategy.getPaginatedQueryResponse().getNextToken()); + PageParams pageParams = new PageParams(null, "asc", 1); + IndexPaginationStrategy paginationStrategy = new IndexPaginationStrategy(pageParams, clusterState); + assertEquals(1, paginationStrategy.getRequestedEntities().size()); + assertEquals("test-index-1", paginationStrategy.getRequestedEntities().get(0)); + assertNotNull(paginationStrategy.getResponseToken().getNextToken()); // Adding index5 to clusterState, before executing next query. clusterState = addIndexToClusterState(clusterState, 5); - paginatedQueryRequest = new PaginatedQueryRequest(paginationStrategy.getPaginatedQueryResponse().getNextToken(), "ascending", 1); - paginationStrategy = new IndexBasedPaginationStrategy(paginatedQueryRequest, clusterState); - assertEquals(1, paginationStrategy.getElementsFromRequestedToken().size()); - assertEquals("test-index-2", paginationStrategy.getElementsFromRequestedToken().get(0)); - assertNotNull(paginationStrategy.getPaginatedQueryResponse().getNextToken()); + pageParams = new PageParams(paginationStrategy.getResponseToken().getNextToken(), "asc", 1); + paginationStrategy = new IndexPaginationStrategy(pageParams, clusterState); + assertEquals(1, paginationStrategy.getRequestedEntities().size()); + assertEquals("test-index-2", paginationStrategy.getRequestedEntities().get(0)); + assertNotNull(paginationStrategy.getResponseToken().getNextToken()); // Deleting test-index-2 which has already been displayed, still test-index-2 should get displayed clusterState = deleteIndexFromClusterState(clusterState, 2); - paginatedQueryRequest = new PaginatedQueryRequest(paginationStrategy.getPaginatedQueryResponse().getNextToken(), "ascending", 1); - paginationStrategy = new IndexBasedPaginationStrategy(paginatedQueryRequest, clusterState); - assertEquals(1, paginationStrategy.getElementsFromRequestedToken().size()); - assertEquals("test-index-3", paginationStrategy.getElementsFromRequestedToken().get(0)); - assertNotNull(paginationStrategy.getPaginatedQueryResponse().getNextToken()); + pageParams = new PageParams(paginationStrategy.getResponseToken().getNextToken(), "asc", 1); + paginationStrategy = new IndexPaginationStrategy(pageParams, clusterState); + assertEquals(1, paginationStrategy.getRequestedEntities().size()); + assertEquals("test-index-3", paginationStrategy.getRequestedEntities().get(0)); + assertNotNull(paginationStrategy.getResponseToken().getNextToken()); // Deleting test-index-4 which is not yet displayed which otherwise should have been displayed in the following query // instead test-index-5 should now get displayed. clusterState = deleteIndexFromClusterState(clusterState, 4); - paginatedQueryRequest = new PaginatedQueryRequest(paginationStrategy.getPaginatedQueryResponse().getNextToken(), "ascending", 1); - paginationStrategy = new IndexBasedPaginationStrategy(paginatedQueryRequest, clusterState); - assertEquals(1, paginationStrategy.getElementsFromRequestedToken().size()); - assertEquals("test-index-5", paginationStrategy.getElementsFromRequestedToken().get(0)); - assertNull(paginationStrategy.getPaginatedQueryResponse().getNextToken()); + pageParams = new PageParams(paginationStrategy.getResponseToken().getNextToken(), "asc", 1); + paginationStrategy = new IndexPaginationStrategy(pageParams, clusterState); + assertEquals(1, paginationStrategy.getRequestedEntities().size()); + assertEquals("test-index-5", paginationStrategy.getRequestedEntities().get(0)); + assertNull(paginationStrategy.getResponseToken().getNextToken()); } public void testRetrieveAllIndicesWhenIndicesGetDeletedAndCreatedInBetweenWithDescOrder() { // Query1 with 4 indices in clusterState (test-index1,2,3,4). ClusterState clusterState = getRandomClusterState(List.of(1, 2, 3, 4)); - PaginatedQueryRequest paginatedQueryRequest = new PaginatedQueryRequest(null, "descending", 1); - IndexBasedPaginationStrategy paginationStrategy = new IndexBasedPaginationStrategy(paginatedQueryRequest, clusterState); - assertEquals(1, paginationStrategy.getElementsFromRequestedToken().size()); - assertEquals("test-index-4", paginationStrategy.getElementsFromRequestedToken().get(0)); - assertNotNull(paginationStrategy.getPaginatedQueryResponse().getNextToken()); + PageParams pageParams = new PageParams(null, "desc", 1); + IndexPaginationStrategy paginationStrategy = new IndexPaginationStrategy(pageParams, clusterState); + assertEquals(1, paginationStrategy.getRequestedEntities().size()); + assertEquals("test-index-4", paginationStrategy.getRequestedEntities().get(0)); + assertNotNull(paginationStrategy.getResponseToken().getNextToken()); // adding test-index-5 to clusterState, before executing next query. clusterState = addIndexToClusterState(clusterState, 5); - paginatedQueryRequest = new PaginatedQueryRequest(paginationStrategy.getPaginatedQueryResponse().getNextToken(), "descending", 1); - paginationStrategy = new IndexBasedPaginationStrategy(paginatedQueryRequest, clusterState); - assertEquals(1, paginationStrategy.getElementsFromRequestedToken().size()); - assertEquals("test-index-3", paginationStrategy.getElementsFromRequestedToken().get(0)); - assertNotNull(paginationStrategy.getPaginatedQueryResponse().getNextToken()); + pageParams = new PageParams(paginationStrategy.getResponseToken().getNextToken(), "desc", 1); + paginationStrategy = new IndexPaginationStrategy(pageParams, clusterState); + assertEquals(1, paginationStrategy.getRequestedEntities().size()); + assertEquals("test-index-3", paginationStrategy.getRequestedEntities().get(0)); + assertNotNull(paginationStrategy.getResponseToken().getNextToken()); // Deleting test-index-3 which has already been displayed, still index2 should get displayed. clusterState = deleteIndexFromClusterState(clusterState, 3); - paginatedQueryRequest = new PaginatedQueryRequest(paginationStrategy.getPaginatedQueryResponse().getNextToken(), "descending", 1); - paginationStrategy = new IndexBasedPaginationStrategy(paginatedQueryRequest, clusterState); - assertEquals(1, paginationStrategy.getElementsFromRequestedToken().size()); - assertEquals("test-index-2", paginationStrategy.getElementsFromRequestedToken().get(0)); - assertNotNull(paginationStrategy.getPaginatedQueryResponse().getNextToken()); + pageParams = new PageParams(paginationStrategy.getResponseToken().getNextToken(), "desc", 1); + paginationStrategy = new IndexPaginationStrategy(pageParams, clusterState); + assertEquals(1, paginationStrategy.getRequestedEntities().size()); + assertEquals("test-index-2", paginationStrategy.getRequestedEntities().get(0)); + assertNotNull(paginationStrategy.getResponseToken().getNextToken()); // Deleting test-index-1 which is not yet displayed which otherwise should have been displayed in the following query. clusterState = deleteIndexFromClusterState(clusterState, 1); - paginatedQueryRequest = new PaginatedQueryRequest(paginationStrategy.getPaginatedQueryResponse().getNextToken(), "descending", 1); - paginationStrategy = new IndexBasedPaginationStrategy(paginatedQueryRequest, clusterState); - assertEquals(0, paginationStrategy.getElementsFromRequestedToken().size()); - assertNull(paginationStrategy.getPaginatedQueryResponse().getNextToken()); + pageParams = new PageParams(paginationStrategy.getResponseToken().getNextToken(), "desc", 1); + paginationStrategy = new IndexPaginationStrategy(pageParams, clusterState); + assertEquals(0, paginationStrategy.getRequestedEntities().size()); + assertNull(paginationStrategy.getResponseToken().getNextToken()); } public void testRetrieveAllIndicesWhenMultipleIndicesGetDeletedInBetweenAtOnce() { // Query1 with 5 indices in clusterState (test-index1,2,3,4,5). ClusterState clusterState = getRandomClusterState(List.of(1, 2, 3, 4, 5)); - PaginatedQueryRequest paginatedQueryRequest = new PaginatedQueryRequest(null, "ascending", 1); - IndexBasedPaginationStrategy paginationStrategy = new IndexBasedPaginationStrategy(paginatedQueryRequest, clusterState); - assertEquals(1, paginationStrategy.getElementsFromRequestedToken().size()); - assertEquals("test-index-1", paginationStrategy.getElementsFromRequestedToken().get(0)); - assertNotNull(paginationStrategy.getPaginatedQueryResponse().getNextToken()); + PageParams pageParams = new PageParams(null, "asc", 1); + IndexPaginationStrategy paginationStrategy = new IndexPaginationStrategy(pageParams, clusterState); + assertEquals(1, paginationStrategy.getRequestedEntities().size()); + assertEquals("test-index-1", paginationStrategy.getRequestedEntities().get(0)); + assertNotNull(paginationStrategy.getResponseToken().getNextToken()); // executing next query without any changes to clusterState - paginatedQueryRequest = new PaginatedQueryRequest(paginationStrategy.getPaginatedQueryResponse().getNextToken(), "ascending", 1); - paginationStrategy = new IndexBasedPaginationStrategy(paginatedQueryRequest, clusterState); - assertEquals(1, paginationStrategy.getElementsFromRequestedToken().size()); - assertEquals("test-index-2", paginationStrategy.getElementsFromRequestedToken().get(0)); - assertNotNull(paginationStrategy.getPaginatedQueryResponse().getNextToken()); + pageParams = new PageParams(paginationStrategy.getResponseToken().getNextToken(), "asc", 1); + paginationStrategy = new IndexPaginationStrategy(pageParams, clusterState); + assertEquals(1, paginationStrategy.getRequestedEntities().size()); + assertEquals("test-index-2", paginationStrategy.getRequestedEntities().get(0)); + assertNotNull(paginationStrategy.getResponseToken().getNextToken()); // Deleting test-index-1, test-index-2 & test-index-3 and executing next query. test-index-4 should get displayed. clusterState = deleteIndexFromClusterState(clusterState, 1); clusterState = deleteIndexFromClusterState(clusterState, 2); clusterState = deleteIndexFromClusterState(clusterState, 3); - paginatedQueryRequest = new PaginatedQueryRequest(paginationStrategy.getPaginatedQueryResponse().getNextToken(), "ascending", 1); - paginationStrategy = new IndexBasedPaginationStrategy(paginatedQueryRequest, clusterState); - assertEquals(1, paginationStrategy.getElementsFromRequestedToken().size()); - assertEquals("test-index-4", paginationStrategy.getElementsFromRequestedToken().get(0)); - assertNotNull(paginationStrategy.getPaginatedQueryResponse().getNextToken()); + pageParams = new PageParams(paginationStrategy.getResponseToken().getNextToken(), "asc", 1); + paginationStrategy = new IndexPaginationStrategy(pageParams, clusterState); + assertEquals(1, paginationStrategy.getRequestedEntities().size()); + assertEquals("test-index-4", paginationStrategy.getRequestedEntities().get(0)); + assertNotNull(paginationStrategy.getResponseToken().getNextToken()); // Executing the last query without any further change. Should result in test-index-5 and nextToken as null. - paginatedQueryRequest = new PaginatedQueryRequest(paginationStrategy.getPaginatedQueryResponse().getNextToken(), "ascending", 1); - paginationStrategy = new IndexBasedPaginationStrategy(paginatedQueryRequest, clusterState); - assertEquals(1, paginationStrategy.getElementsFromRequestedToken().size()); - assertEquals("test-index-5", paginationStrategy.getElementsFromRequestedToken().get(0)); - assertNull(paginationStrategy.getPaginatedQueryResponse().getNextToken()); + pageParams = new PageParams(paginationStrategy.getResponseToken().getNextToken(), "asc", 1); + paginationStrategy = new IndexPaginationStrategy(pageParams, clusterState); + assertEquals(1, paginationStrategy.getRequestedEntities().size()); + assertEquals("test-index-5", paginationStrategy.getRequestedEntities().get(0)); + assertNull(paginationStrategy.getResponseToken().getNextToken()); } public void testCreatingIndexStrategyPageTokenWithRequestedTokenNull() { try { - new IndexBasedPaginationStrategy.IndexStrategyToken(null); + new IndexPaginationStrategy.IndexStrategyToken(null); fail("expected exception"); } catch (Exception e) { assert e.getMessage().contains("requestedTokenString can not be null"); @@ -215,30 +215,30 @@ public void testCreatingIndexStrategyPageTokenWithRequestedTokenNull() { } public void testIndexStrategyPageTokenWithWronglyEncryptedRequestToken() { - assertThrows(OpenSearchParseException.class, () -> new IndexBasedPaginationStrategy.IndexStrategyToken("3%4%5")); + assertThrows(OpenSearchParseException.class, () -> new IndexPaginationStrategy.IndexStrategyToken("3%4%5")); } public void testIndexStrategyPageTokenWithIncorrectNumberOfElementsInRequestedToken() { assertThrows( OpenSearchParseException.class, - () -> new IndexBasedPaginationStrategy.IndexStrategyToken(PaginationStrategy.encryptStringToken("1725361543")) + () -> new IndexPaginationStrategy.IndexStrategyToken(PaginationStrategy.encryptStringToken("1725361543")) ); assertThrows( OpenSearchParseException.class, - () -> new IndexBasedPaginationStrategy.IndexStrategyToken(PaginationStrategy.encryptStringToken("1|1725361543|index|12345")) + () -> new IndexPaginationStrategy.IndexStrategyToken(PaginationStrategy.encryptStringToken("1|1725361543|index|12345")) ); } public void testIndexStrategyPageTokenWithInvalidValuesInRequestedToken() { assertThrows( OpenSearchParseException.class, - () -> new IndexBasedPaginationStrategy.IndexStrategyToken(PaginationStrategy.encryptStringToken("-1725361543|index")) + () -> new IndexPaginationStrategy.IndexStrategyToken(PaginationStrategy.encryptStringToken("-1725361543|index")) ); } public void testCreatingIndexStrategyPageTokenWithNameOfLastRespondedIndexNull() { try { - new IndexBasedPaginationStrategy.IndexStrategyToken(1234l, null); + new IndexPaginationStrategy.IndexStrategyToken(1234l, null); fail("expected exception"); } catch (Exception e) { assert e.getMessage().contains("index name should be provided");