This is an automated email from the ASF dual-hosted git repository.

zeroshade pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-go.git


The following commit(s) were added to refs/heads/main by this push:
     new 3ce3b4bb fix(table): add pagination support to ListNamespaces (#694)
3ce3b4bb is described below

commit 3ce3b4bb51c0aa060d2bf6826dbbfd453c8067f9
Author: Sumisha <[email protected]>
AuthorDate: Fri Jan 23 23:31:46 2026 +0530

    fix(table): add pagination support to ListNamespaces (#694)
    
    Change ListNamespaces to support pagination.
---
 catalog/rest/rest.go      | 38 ++++++++++++++++++++++++++++++----
 catalog/rest/rest_test.go | 52 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 86 insertions(+), 4 deletions(-)

diff --git a/catalog/rest/rest.go b/catalog/rest/rest.go
index 0a7e4700..e0d1c94b 100644
--- a/catalog/rest/rest.go
+++ b/catalog/rest/rest.go
@@ -997,23 +997,53 @@ func (r *Catalog) DropNamespace(ctx context.Context, 
namespace table.Identifier)
 }
 
 func (r *Catalog) ListNamespaces(ctx context.Context, parent table.Identifier) 
([]table.Identifier, error) {
+       var allNamespaces []table.Identifier
+       pageSize := r.getPageSize(ctx)
+       var pageToken string
+
+       for {
+               namespaces, nextPageToken, err := r.listNamespacesPage(ctx, 
parent, pageToken, pageSize)
+               if err != nil {
+                       return nil, err
+               }
+               allNamespaces = append(allNamespaces, namespaces...)
+               if nextPageToken == "" {
+                       break
+               }
+               pageToken = nextPageToken
+       }
+
+       return allNamespaces, nil
+}
+
+func (r *Catalog) listNamespacesPage(ctx context.Context, parent 
table.Identifier, pageToken string, pageSize int) ([]table.Identifier, string, 
error) {
        uri := r.baseURI.JoinPath("namespaces")
+
+       v := url.Values{}
        if len(parent) != 0 {
-               v := url.Values{}
                v.Set("parent", strings.Join(parent, namespaceSeparator))
+       }
+       if pageSize >= 0 {
+               v.Set("pageSize", strconv.Itoa(pageSize))
+       }
+       if pageToken != "" {
+               v.Set("pageToken", pageToken)
+       }
+       if len(v) > 0 {
                uri.RawQuery = v.Encode()
        }
 
        type rsptype struct {
-               Namespaces []table.Identifier `json:"namespaces"`
+               Namespaces    []table.Identifier `json:"namespaces"`
+               NextPageToken string             
`json:"next-page-token,omitempty"`
        }
 
        rsp, err := doGet[rsptype](ctx, uri, []string{}, r.cl, 
map[int]error{http.StatusNotFound: catalog.ErrNoSuchNamespace})
        if err != nil {
-               return nil, err
+               return nil, "", err
        }
 
-       return rsp.Namespaces, nil
+       return rsp.Namespaces, rsp.NextPageToken, nil
 }
 
 func (r *Catalog) LoadNamespaceProperties(ctx context.Context, namespace 
table.Identifier) (iceberg.Properties, error) {
diff --git a/catalog/rest/rest_test.go b/catalog/rest/rest_test.go
index 5216d6be..91ae11c8 100644
--- a/catalog/rest/rest_test.go
+++ b/catalog/rest/rest_test.go
@@ -747,6 +747,58 @@ func (r *RestCatalogSuite) TestListNamespaces400() {
        r.ErrorContains(err, "Namespace does not exist: personal in warehouse 
8bcb0838-50fc-472d-9ddb-8feb89ef5f1e")
 }
 
+func (r *RestCatalogSuite) TestListNamespacesPagination() {
+       // Track the number of requests and page tokens received
+       requestCount := 0
+
+       r.mux.HandleFunc("/v1/namespaces", func(w http.ResponseWriter, req 
*http.Request) {
+               r.Require().Equal(http.MethodGet, req.Method)
+
+               for k, v := range TestHeaders {
+                       r.Equal(v, req.Header.Values(k))
+               }
+
+               pageToken := req.URL.Query().Get("pageToken")
+               requestCount++
+
+               switch requestCount {
+               case 1:
+                       // First request - no pageToken expected
+                       r.Empty(pageToken)
+                       json.NewEncoder(w).Encode(map[string]any{
+                               "namespaces":      []table.Identifier{{"ns1"}, 
{"ns2"}},
+                               "next-page-token": "token1",
+                       })
+               case 2:
+                       // Second request - expect token1
+                       r.Equal("token1", pageToken)
+                       json.NewEncoder(w).Encode(map[string]any{
+                               "namespaces":      []table.Identifier{{"ns3"}, 
{"ns4"}},
+                               "next-page-token": "token2",
+                       })
+               case 3:
+                       // Third request - expect token2, no next token (last 
page)
+                       r.Equal("token2", pageToken)
+                       json.NewEncoder(w).Encode(map[string]any{
+                               "namespaces": []table.Identifier{{"ns5"}},
+                       })
+               default:
+                       r.Fail("unexpected request count: %d", requestCount)
+               }
+       })
+
+       cat, err := rest.NewCatalog(context.Background(), "rest", r.srv.URL, 
rest.WithOAuthToken(TestToken))
+       r.Require().NoError(err)
+
+       results, err := cat.ListNamespaces(context.Background(), nil)
+       r.Require().NoError(err)
+
+       // Verify all namespaces from all pages are returned
+       r.Equal([]table.Identifier{{"ns1"}, {"ns2"}, {"ns3"}, {"ns4"}, 
{"ns5"}}, results)
+       // Verify 3 requests were made (3 pages)
+       r.Equal(3, requestCount)
+}
+
 func (r *RestCatalogSuite) TestCreateNamespace200() {
        r.mux.HandleFunc("/v1/namespaces", func(w http.ResponseWriter, req 
*http.Request) {
                r.Require().Equal(http.MethodPost, req.Method)

Reply via email to