diff --git a/core/src/main/java/org/apache/iceberg/rest/CatalogHandlers.java b/core/src/main/java/org/apache/iceberg/rest/CatalogHandlers.java index e4e3c065fb55..746da5ffcab0 100644 --- a/core/src/main/java/org/apache/iceberg/rest/CatalogHandlers.java +++ b/core/src/main/java/org/apache/iceberg/rest/CatalogHandlers.java @@ -80,6 +80,7 @@ public class CatalogHandlers { private static final Schema EMPTY_SCHEMA = new Schema(); + private static final String INTIAL_PAGE_TOKEN = ""; private CatalogHandlers() {} @@ -117,6 +118,29 @@ public static ListNamespacesResponse listNamespaces( return ListNamespacesResponse.builder().addAll(results).build(); } + public static ListNamespacesResponse listNamespaces( + SupportsNamespaces catalog, Namespace parent, String pageToken, String pageSize) { + List results; + List subResults; + + if (parent.isEmpty()) { + results = catalog.listNamespaces(); + } else { + results = catalog.listNamespaces(parent); + } + + int start = INTIAL_PAGE_TOKEN.equals(pageToken) ? 0 : Integer.parseInt(pageToken); + int end = start + Integer.parseInt(pageSize); + subResults = results.subList(start, end); + String nextToken = String.valueOf(end); + + if (end >= results.size()) { + nextToken = null; + } + + return ListNamespacesResponse.builder().addAll(subResults).nextPageToken(nextToken).build(); + } + public static CreateNamespaceResponse createNamespace( SupportsNamespaces catalog, CreateNamespaceRequest request) { Namespace namespace = request.namespace(); @@ -174,6 +198,23 @@ public static ListTablesResponse listTables(Catalog catalog, Namespace namespace return ListTablesResponse.builder().addAll(idents).build(); } + public static ListTablesResponse listTables( + Catalog catalog, Namespace namespace, String pageToken, String pageSize) { + List results = catalog.listTables(namespace); + List subResults; + + int start = INTIAL_PAGE_TOKEN.equals(pageToken) ? 0 : Integer.parseInt(pageToken); + int end = start + Integer.parseInt(pageSize); + subResults = results.subList(start, end); + String nextToken = String.valueOf(end); + + if (end >= results.size()) { + nextToken = null; + } + + return ListTablesResponse.builder().addAll(subResults).nextPageToken(nextToken).build(); + } + public static LoadTableResponse stageTableCreate( Catalog catalog, Namespace namespace, CreateTableRequest request) { request.validate(); @@ -397,6 +438,23 @@ public static ListTablesResponse listViews(ViewCatalog catalog, Namespace namesp return ListTablesResponse.builder().addAll(catalog.listViews(namespace)).build(); } + public static ListTablesResponse listViews( + ViewCatalog catalog, Namespace namespace, String pageToken, String pageSize) { + List results = catalog.listViews(namespace); + List subResults; + + int start = INTIAL_PAGE_TOKEN.equals(pageToken) ? 0 : Integer.parseInt(pageToken); + int end = start + Integer.parseInt(pageSize); + subResults = results.subList(start, end); + String nextToken = String.valueOf(end); + + if (end >= results.size()) { + nextToken = null; + } + + return ListTablesResponse.builder().addAll(subResults).nextPageToken(nextToken).build(); + } + public static LoadViewResponse createView( ViewCatalog catalog, Namespace namespace, CreateViewRequest request) { request.validate(); diff --git a/core/src/main/java/org/apache/iceberg/rest/RESTSessionCatalog.java b/core/src/main/java/org/apache/iceberg/rest/RESTSessionCatalog.java index f8a47f19fdb9..a1933e123d54 100644 --- a/core/src/main/java/org/apache/iceberg/rest/RESTSessionCatalog.java +++ b/core/src/main/java/org/apache/iceberg/rest/RESTSessionCatalog.java @@ -114,6 +114,7 @@ public class RESTSessionCatalog extends BaseViewSessionCatalog private static final String DEFAULT_FILE_IO_IMPL = "org.apache.iceberg.io.ResolvingFileIO"; private static final String REST_METRICS_REPORTING_ENABLED = "rest-metrics-reporting-enabled"; private static final String REST_SNAPSHOT_LOADING_MODE = "snapshot-loading-mode"; + public static final String REST_PAGE_SIZE = "rest-page-size"; private static final List TOKEN_PREFERENCE_ORDER = ImmutableList.of( OAuth2Properties.ID_TOKEN_TYPE, @@ -136,6 +137,7 @@ public class RESTSessionCatalog extends BaseViewSessionCatalog private FileIO io = null; private MetricsReporter reporter = null; private boolean reportingViaRestEnabled; + private Integer pageSize = null; private CloseableGroup closeables = null; // a lazy thread pool for token refresh @@ -228,6 +230,12 @@ public void initialize(String name, Map unresolved) { client, tokenRefreshExecutor(), token, expiresAtMillis(mergedProps), catalogAuth); } + this.pageSize = PropertyUtil.propertyAsNullableInt(mergedProps, REST_PAGE_SIZE); + if (pageSize != null) { + Preconditions.checkArgument( + pageSize > 0, "Invalid value for %s, must be a positive integer", REST_PAGE_SIZE); + } + this.io = newFileIO(SessionContext.createEmpty(), mergedProps); this.fileIOCloser = newFileIOCloser(); @@ -278,14 +286,27 @@ public void setConf(Object newConf) { @Override public List listTables(SessionContext context, Namespace ns) { checkNamespaceIsValid(ns); + Map queryParams = Maps.newHashMap(); + ImmutableList.Builder tables = ImmutableList.builder(); + String pageToken = ""; + if (pageSize != null) { + queryParams.put("pageSize", String.valueOf(pageSize)); + } - ListTablesResponse response = - client.get( - paths.tables(ns), - ListTablesResponse.class, - headers(context), - ErrorHandlers.namespaceErrorHandler()); - return response.identifiers(); + do { + queryParams.put("pageToken", pageToken); + ListTablesResponse response = + client.get( + paths.tables(ns), + queryParams, + ListTablesResponse.class, + headers(context), + ErrorHandlers.namespaceErrorHandler()); + pageToken = response.nextPageToken(); + tables.addAll(response.identifiers()); + } while (pageToken != null); + + return tables.build(); } @Override @@ -494,22 +515,31 @@ public void createNamespace( @Override public List listNamespaces(SessionContext context, Namespace namespace) { - Map queryParams; - if (namespace.isEmpty()) { - queryParams = ImmutableMap.of(); - } else { - // query params should be unescaped - queryParams = ImmutableMap.of("parent", RESTUtil.NAMESPACE_JOINER.join(namespace.levels())); + Map queryParams = Maps.newHashMap(); + if (!namespace.isEmpty()) { + queryParams.put("parent", RESTUtil.NAMESPACE_JOINER.join(namespace.levels())); } - ListNamespacesResponse response = - client.get( - paths.namespaces(), - queryParams, - ListNamespacesResponse.class, - headers(context), - ErrorHandlers.namespaceErrorHandler()); - return response.namespaces(); + ImmutableList.Builder namespaces = ImmutableList.builder(); + String pageToken = ""; + if (pageSize != null) { + queryParams.put("pageSize", String.valueOf(pageSize)); + } + + do { + queryParams.put("pageToken", pageToken); + ListNamespacesResponse response = + client.get( + paths.namespaces(), + queryParams, + ListNamespacesResponse.class, + headers(context), + ErrorHandlers.namespaceErrorHandler()); + pageToken = response.nextPageToken(); + namespaces.addAll(response.namespaces()); + } while (pageToken != null); + + return namespaces.build(); } @Override @@ -1048,14 +1078,27 @@ public void commitTransaction(SessionContext context, List commits) @Override public List listViews(SessionContext context, Namespace namespace) { checkNamespaceIsValid(namespace); + Map queryParams = Maps.newHashMap(); + ImmutableList.Builder views = ImmutableList.builder(); + String pageToken = ""; + if (pageSize != null) { + queryParams.put("pageSize", String.valueOf(pageSize)); + } - ListTablesResponse response = - client.get( - paths.views(namespace), - ListTablesResponse.class, - headers(context), - ErrorHandlers.namespaceErrorHandler()); - return response.identifiers(); + do { + queryParams.put("pageToken", pageToken); + ListTablesResponse response = + client.get( + paths.views(namespace), + queryParams, + ListTablesResponse.class, + headers(context), + ErrorHandlers.namespaceErrorHandler()); + pageToken = response.nextPageToken(); + views.addAll(response.identifiers()); + } while (pageToken != null); + + return views.build(); } @Override diff --git a/core/src/main/java/org/apache/iceberg/rest/responses/ListNamespacesResponse.java b/core/src/main/java/org/apache/iceberg/rest/responses/ListNamespacesResponse.java index 13a599e1a76c..8feeda6f2b8b 100644 --- a/core/src/main/java/org/apache/iceberg/rest/responses/ListNamespacesResponse.java +++ b/core/src/main/java/org/apache/iceberg/rest/responses/ListNamespacesResponse.java @@ -29,13 +29,15 @@ public class ListNamespacesResponse implements RESTResponse { private List namespaces; + private String nextPageToken; public ListNamespacesResponse() { // Required for Jackson deserialization } - private ListNamespacesResponse(List namespaces) { + private ListNamespacesResponse(List namespaces, String nextPageToken) { this.namespaces = namespaces; + this.nextPageToken = nextPageToken; validate(); } @@ -48,9 +50,16 @@ public List namespaces() { return namespaces != null ? namespaces : ImmutableList.of(); } + public String nextPageToken() { + return nextPageToken; + } + @Override public String toString() { - return MoreObjects.toStringHelper(this).add("namespaces", namespaces()).toString(); + return MoreObjects.toStringHelper(this) + .add("namespaces", namespaces()) + .add("next-page-token", nextPageToken()) + .toString(); } public static Builder builder() { @@ -59,6 +68,7 @@ public static Builder builder() { public static class Builder { private final ImmutableList.Builder namespaces = ImmutableList.builder(); + private String nextPageToken; private Builder() {} @@ -75,8 +85,13 @@ public Builder addAll(Collection toAdd) { return this; } + public Builder nextPageToken(String pageToken) { + nextPageToken = pageToken; + return this; + } + public ListNamespacesResponse build() { - return new ListNamespacesResponse(namespaces.build()); + return new ListNamespacesResponse(namespaces.build(), nextPageToken); } } } diff --git a/core/src/main/java/org/apache/iceberg/rest/responses/ListTablesResponse.java b/core/src/main/java/org/apache/iceberg/rest/responses/ListTablesResponse.java index 3c99c12c9023..1db05709b470 100644 --- a/core/src/main/java/org/apache/iceberg/rest/responses/ListTablesResponse.java +++ b/core/src/main/java/org/apache/iceberg/rest/responses/ListTablesResponse.java @@ -30,13 +30,15 @@ public class ListTablesResponse implements RESTResponse { private List identifiers; + private String nextPageToken; public ListTablesResponse() { // Required for Jackson deserialization } - private ListTablesResponse(List identifiers) { + private ListTablesResponse(List identifiers, String nextPageToken) { this.identifiers = identifiers; + this.nextPageToken = nextPageToken; validate(); } @@ -49,9 +51,16 @@ public List identifiers() { return identifiers != null ? identifiers : ImmutableList.of(); } + public String nextPageToken() { + return nextPageToken; + } + @Override public String toString() { - return MoreObjects.toStringHelper(this).add("identifiers", identifiers).toString(); + return MoreObjects.toStringHelper(this) + .add("identifiers", identifiers) + .add("next-page-token", nextPageToken()) + .toString(); } public static Builder builder() { @@ -60,6 +69,7 @@ public static Builder builder() { public static class Builder { private final ImmutableList.Builder identifiers = ImmutableList.builder(); + private String nextPageToken; private Builder() {} @@ -76,8 +86,13 @@ public Builder addAll(Collection toAdd) { return this; } + public Builder nextPageToken(String pageToken) { + nextPageToken = pageToken; + return this; + } + public ListTablesResponse build() { - return new ListTablesResponse(identifiers.build()); + return new ListTablesResponse(identifiers.build(), nextPageToken); } } } diff --git a/core/src/test/java/org/apache/iceberg/rest/RESTCatalogAdapter.java b/core/src/test/java/org/apache/iceberg/rest/RESTCatalogAdapter.java index 7fccc4e974e3..357b05e85c20 100644 --- a/core/src/test/java/org/apache/iceberg/rest/RESTCatalogAdapter.java +++ b/core/src/test/java/org/apache/iceberg/rest/RESTCatalogAdapter.java @@ -298,7 +298,17 @@ public T handleRequest( ns = Namespace.empty(); } - return castResponse(responseType, CatalogHandlers.listNamespaces(asNamespaceCatalog, ns)); + String pageToken = PropertyUtil.propertyAsString(vars, "pageToken", null); + String pageSize = PropertyUtil.propertyAsString(vars, "pageSize", null); + + if (pageSize != null) { + return castResponse( + responseType, + CatalogHandlers.listNamespaces(asNamespaceCatalog, ns, pageToken, pageSize)); + } else { + return castResponse( + responseType, CatalogHandlers.listNamespaces(asNamespaceCatalog, ns)); + } } break; @@ -339,7 +349,14 @@ public T handleRequest( case LIST_TABLES: { Namespace namespace = namespaceFromPathVars(vars); - return castResponse(responseType, CatalogHandlers.listTables(catalog, namespace)); + String pageToken = PropertyUtil.propertyAsString(vars, "pageToken", null); + String pageSize = PropertyUtil.propertyAsString(vars, "pageSize", null); + if (pageSize != null) { + return castResponse( + responseType, CatalogHandlers.listTables(catalog, namespace, pageToken, pageSize)); + } else { + return castResponse(responseType, CatalogHandlers.listTables(catalog, namespace)); + } } case CREATE_TABLE: @@ -412,7 +429,16 @@ public T handleRequest( { if (null != asViewCatalog) { Namespace namespace = namespaceFromPathVars(vars); - return castResponse(responseType, CatalogHandlers.listViews(asViewCatalog, namespace)); + String pageToken = PropertyUtil.propertyAsString(vars, "pageToken", null); + String pageSize = PropertyUtil.propertyAsString(vars, "pageSize", null); + if (pageSize != null) { + return castResponse( + responseType, + CatalogHandlers.listViews(asViewCatalog, namespace, pageToken, pageSize)); + } else { + return castResponse( + responseType, CatalogHandlers.listViews(asViewCatalog, namespace)); + } } break; } diff --git a/core/src/test/java/org/apache/iceberg/rest/TestRESTCatalog.java b/core/src/test/java/org/apache/iceberg/rest/TestRESTCatalog.java index 18d832b3cd46..95380424e7ad 100644 --- a/core/src/test/java/org/apache/iceberg/rest/TestRESTCatalog.java +++ b/core/src/test/java/org/apache/iceberg/rest/TestRESTCatalog.java @@ -77,7 +77,10 @@ import org.apache.iceberg.rest.auth.OAuth2Util; import org.apache.iceberg.rest.requests.UpdateTableRequest; import org.apache.iceberg.rest.responses.ConfigResponse; +import org.apache.iceberg.rest.responses.CreateNamespaceResponse; import org.apache.iceberg.rest.responses.ErrorResponse; +import org.apache.iceberg.rest.responses.ListNamespacesResponse; +import org.apache.iceberg.rest.responses.ListTablesResponse; import org.apache.iceberg.rest.responses.LoadTableResponse; import org.apache.iceberg.rest.responses.OAuthTokenResponse; import org.apache.iceberg.types.Types; @@ -2329,6 +2332,148 @@ public void multipleDiffsAgainstMultipleTablesLastFails() { assertThat(schema2.columns()).hasSize(1); } + @Test + public void testInvalidPageSize() { + RESTCatalogAdapter adapter = Mockito.spy(new RESTCatalogAdapter(backendCatalog)); + RESTCatalog catalog = + new RESTCatalog(SessionCatalog.SessionContext.createEmpty(), (config) -> adapter); + Assertions.assertThatThrownBy( + () -> + catalog.initialize( + "test", ImmutableMap.of(RESTSessionCatalog.REST_PAGE_SIZE, "-1"))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage( + String.format( + "Invalid value for %s, must be a positive integer", + RESTSessionCatalog.REST_PAGE_SIZE)); + } + + @Test + public void testPaginationForListNamespaces() { + RESTCatalogAdapter adapter = Mockito.spy(new RESTCatalogAdapter(backendCatalog)); + RESTCatalog catalog = + new RESTCatalog(SessionCatalog.SessionContext.createEmpty(), (config) -> adapter); + catalog.initialize("test", ImmutableMap.of(RESTSessionCatalog.REST_PAGE_SIZE, "10")); + int numberOfItems = 30; + String namespaceName = "newdb"; + + // create several namespaces for listing and verify + for (int i = 0; i < numberOfItems; i++) { + String nameSpaceName = namespaceName + i; + catalog.createNamespace(Namespace.of(nameSpaceName)); + } + + assertThat(catalog.listNamespaces()).hasSize(numberOfItems); + + Mockito.verify(adapter) + .execute( + eq(HTTPMethod.GET), + eq("v1/config"), + any(), + any(), + eq(ConfigResponse.class), + any(), + any()); + + Mockito.verify(adapter, times(numberOfItems)) + .execute( + eq(HTTPMethod.POST), + eq("v1/namespaces"), + any(), + any(), + eq(CreateNamespaceResponse.class), + any(), + any()); + + // verify initial request with empty pageToken + Mockito.verify(adapter) + .handleRequest( + eq(RESTCatalogAdapter.Route.LIST_NAMESPACES), + eq(ImmutableMap.of("pageToken", "", "pageSize", "10")), + any(), + eq(ListNamespacesResponse.class)); + + // verify second request with updated pageToken + Mockito.verify(adapter) + .handleRequest( + eq(RESTCatalogAdapter.Route.LIST_NAMESPACES), + eq(ImmutableMap.of("pageToken", "10", "pageSize", "10")), + any(), + eq(ListNamespacesResponse.class)); + + // verify third request with update pageToken + Mockito.verify(adapter) + .handleRequest( + eq(RESTCatalogAdapter.Route.LIST_NAMESPACES), + eq(ImmutableMap.of("pageToken", "20", "pageSize", "10")), + any(), + eq(ListNamespacesResponse.class)); + } + + @Test + public void testPaginationForListTables() { + RESTCatalogAdapter adapter = Mockito.spy(new RESTCatalogAdapter(backendCatalog)); + RESTCatalog catalog = + new RESTCatalog(SessionCatalog.SessionContext.createEmpty(), (config) -> adapter); + catalog.initialize("test", ImmutableMap.of(RESTSessionCatalog.REST_PAGE_SIZE, "10")); + int numberOfItems = 30; + String namespaceName = "newdb"; + String tableName = "newtable"; + catalog.createNamespace(Namespace.of(namespaceName)); + + // create several tables under namespace for listing and verify + for (int i = 0; i < numberOfItems; i++) { + TableIdentifier tableIdentifier = TableIdentifier.of(namespaceName, tableName + i); + catalog.createTable(tableIdentifier, SCHEMA); + } + + assertThat(catalog.listTables(Namespace.of(namespaceName))).hasSize(numberOfItems); + + Mockito.verify(adapter) + .execute( + eq(HTTPMethod.GET), + eq("v1/config"), + any(), + any(), + eq(ConfigResponse.class), + any(), + any()); + + Mockito.verify(adapter, times(numberOfItems)) + .execute( + eq(HTTPMethod.POST), + eq(String.format("v1/namespaces/%s/tables", namespaceName)), + any(), + any(), + eq(LoadTableResponse.class), + any(), + any()); + + // verify initial request with empty pageToken + Mockito.verify(adapter) + .handleRequest( + eq(RESTCatalogAdapter.Route.LIST_TABLES), + eq(ImmutableMap.of("pageToken", "", "pageSize", "10", "namespace", namespaceName)), + any(), + eq(ListTablesResponse.class)); + + // verify second request with updated pageToken + Mockito.verify(adapter) + .handleRequest( + eq(RESTCatalogAdapter.Route.LIST_TABLES), + eq(ImmutableMap.of("pageToken", "10", "pageSize", "10", "namespace", namespaceName)), + any(), + eq(ListTablesResponse.class)); + + // verify third request with update pageToken + Mockito.verify(adapter) + .handleRequest( + eq(RESTCatalogAdapter.Route.LIST_TABLES), + eq(ImmutableMap.of("pageToken", "20", "pageSize", "10", "namespace", namespaceName)), + any(), + eq(ListTablesResponse.class)); + } + @Test public void testCleanupUncommitedFilesForCleanableFailures() { RESTCatalogAdapter adapter = Mockito.spy(new RESTCatalogAdapter(backendCatalog)); diff --git a/core/src/test/java/org/apache/iceberg/rest/TestRESTViewCatalog.java b/core/src/test/java/org/apache/iceberg/rest/TestRESTViewCatalog.java index 0b29da70426b..f67c4b078e5d 100644 --- a/core/src/test/java/org/apache/iceberg/rest/TestRESTViewCatalog.java +++ b/core/src/test/java/org/apache/iceberg/rest/TestRESTViewCatalog.java @@ -18,20 +18,31 @@ */ package org.apache.iceberg.rest; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.File; import java.nio.file.Path; +import java.util.List; import java.util.Map; import java.util.UUID; import java.util.function.Consumer; import org.apache.iceberg.CatalogProperties; import org.apache.iceberg.catalog.Catalog; +import org.apache.iceberg.catalog.Namespace; import org.apache.iceberg.catalog.SessionCatalog; +import org.apache.iceberg.catalog.TableIdentifier; import org.apache.iceberg.inmemory.InMemoryCatalog; import org.apache.iceberg.relocated.com.google.common.collect.ImmutableMap; import org.apache.iceberg.rest.RESTCatalogAdapter.HTTPMethod; +import org.apache.iceberg.rest.responses.ConfigResponse; import org.apache.iceberg.rest.responses.ErrorResponse; +import org.apache.iceberg.rest.responses.ListTablesResponse; +import org.apache.iceberg.rest.responses.LoadViewResponse; import org.apache.iceberg.view.ViewCatalogTests; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.gzip.GzipHandler; @@ -39,7 +50,9 @@ import org.eclipse.jetty.servlet.ServletHolder; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; +import org.mockito.Mockito; public class TestRESTViewCatalog extends ViewCatalogTests { private static final ObjectMapper MAPPER = RESTObjectMapper.mapper(); @@ -144,6 +157,78 @@ public void closeCatalog() throws Exception { } } + @Test + public void testPaginationForListViews() { + RESTCatalogAdapter adapter = Mockito.spy(new RESTCatalogAdapter(backendCatalog)); + RESTCatalog catalog = + new RESTCatalog(SessionCatalog.SessionContext.createEmpty(), (config) -> adapter); + catalog.initialize("test", ImmutableMap.of(RESTSessionCatalog.REST_PAGE_SIZE, "10")); + + int numberOfItems = 30; + String namespaceName = "newdb"; + String viewName = "newview"; + + // create initial namespace + catalog().createNamespace(Namespace.of(namespaceName)); + + // create several views under namespace, based off a table for listing and verify + for (int i = 0; i < numberOfItems; i++) { + TableIdentifier viewIndentifier = TableIdentifier.of(namespaceName, viewName + i); + catalog + .buildView(viewIndentifier) + .withSchema(SCHEMA) + .withDefaultNamespace(viewIndentifier.namespace()) + .withQuery("spark", "select * from ns.tbl") + .create(); + } + List views = catalog.listViews(Namespace.of(namespaceName)); + assertThat(views).hasSize(numberOfItems); + + Mockito.verify(adapter) + .execute( + eq(HTTPMethod.GET), + eq("v1/config"), + any(), + any(), + eq(ConfigResponse.class), + any(), + any()); + + Mockito.verify(adapter, times(numberOfItems)) + .execute( + eq(HTTPMethod.POST), + eq(String.format("v1/namespaces/%s/views", namespaceName)), + any(), + any(), + eq(LoadViewResponse.class), + any(), + any()); + + // verify initial request with empty pageToken + Mockito.verify(adapter) + .handleRequest( + eq(RESTCatalogAdapter.Route.LIST_VIEWS), + eq(ImmutableMap.of("pageToken", "", "pageSize", "10", "namespace", namespaceName)), + any(), + eq(ListTablesResponse.class)); + + // verify second request with update pageToken + Mockito.verify(adapter) + .handleRequest( + eq(RESTCatalogAdapter.Route.LIST_VIEWS), + eq(ImmutableMap.of("pageToken", "10", "pageSize", "10", "namespace", namespaceName)), + any(), + eq(ListTablesResponse.class)); + + // verify third request with update pageToken + Mockito.verify(adapter) + .handleRequest( + eq(RESTCatalogAdapter.Route.LIST_VIEWS), + eq(ImmutableMap.of("pageToken", "20", "pageSize", "10", "namespace", namespaceName)), + any(), + eq(ListTablesResponse.class)); + } + @Override protected RESTCatalog catalog() { return restCatalog; diff --git a/core/src/test/java/org/apache/iceberg/rest/responses/TestListNamespacesResponse.java b/core/src/test/java/org/apache/iceberg/rest/responses/TestListNamespacesResponse.java index bfe5a662b219..d9ed801de0cb 100644 --- a/core/src/test/java/org/apache/iceberg/rest/responses/TestListNamespacesResponse.java +++ b/core/src/test/java/org/apache/iceberg/rest/responses/TestListNamespacesResponse.java @@ -34,11 +34,11 @@ public class TestListNamespacesResponse extends RequestResponseTestBase