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

klesh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/devlake.git


The following commit(s) were added to refs/heads/main by this push:
     new c4a15bf89 fix(bitbucket): paginate remote-scopes API to return all 
repositories (#8999) (#9004)
c4a15bf89 is described below

commit c4a15bf894a7108c61df49798ff13d43329fd7e2
Author: Bramhanand Lingala <[email protected]>
AuthorDate: Sun Jul 26 08:35:08 2026 -0400

    fix(bitbucket): paginate remote-scopes API to return all repositories 
(#8999) (#9004)
---
 backend/plugins/bitbucket/api/remote_api.go      | 14 +++-
 backend/plugins/bitbucket/api/remote_api_test.go | 83 ++++++++++++++++++++++++
 backend/plugins/bitbucket/models/repo.go         |  2 +
 3 files changed, 98 insertions(+), 1 deletion(-)

diff --git a/backend/plugins/bitbucket/api/remote_api.go 
b/backend/plugins/bitbucket/api/remote_api.go
index cf60b1bd5..36fa45a0d 100644
--- a/backend/plugins/bitbucket/api/remote_api.go
+++ b/backend/plugins/bitbucket/api/remote_api.go
@@ -107,6 +107,12 @@ func listBitbucketWorkspaces(
                        FullName: r.GroupName(),
                })
        }
+       if resBody.Next != "" {
+               nextPage = &BitbucketRemotePagination{
+                       Page:    page.Page + 1,
+                       PageLen: page.PageLen,
+               }
+       }
        return
 }
 
@@ -123,7 +129,7 @@ func listBitbucketRepos(
        var res *http.Response
        // list projects part
        res, err = apiClient.Get(fmt.Sprintf("/repositories/%s", workspace), 
url.Values{
-               "fields":  
{"values.name,values.full_name,values.language,values.description,values.owner.display_name,values.created_on,values.updated_on,values.links.clone,values.links.html,pagelen,page,size"},
+               "fields":  
{"values.name,values.full_name,values.language,values.description,values.owner.display_name,values.created_on,values.updated_on,values.links.clone,values.links.html,pagelen,page,size,next"},
                "page":    {fmt.Sprintf("%v", page.Page)},
                "pagelen": {fmt.Sprintf("%v", page.PageLen)},
        }, nil)
@@ -153,6 +159,12 @@ func listBitbucketRepos(
                        Data:     r.ConvertApiScope(),
                })
        }
+       if resBody.Next != "" {
+               nextPage = &BitbucketRemotePagination{
+                       Page:    page.Page + 1,
+                       PageLen: page.PageLen,
+               }
+       }
        return
 }
 
diff --git a/backend/plugins/bitbucket/api/remote_api_test.go 
b/backend/plugins/bitbucket/api/remote_api_test.go
new file mode 100644
index 000000000..26bdbc4a2
--- /dev/null
+++ b/backend/plugins/bitbucket/api/remote_api_test.go
@@ -0,0 +1,83 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package api
+
+import (
+       "io"
+       "net/http"
+       "net/url"
+       "strings"
+       "testing"
+
+       "github.com/stretchr/testify/assert"
+
+       "github.com/apache/incubator-devlake/core/errors"
+       "github.com/apache/incubator-devlake/core/plugin"
+)
+
+type fakeApiClient struct{ body string }
+
+func (f *fakeApiClient) SetData(name string, data interface{})                 
   {}
+func (f *fakeApiClient) GetData(name string) interface{}                       
   { return nil }
+func (f *fakeApiClient) SetHeaders(headers map[string]string)                  
   {}
+func (f *fakeApiClient) SetBeforeFunction(callback 
plugin.ApiClientBeforeRequest) {}
+func (f *fakeApiClient) GetBeforeFunction() plugin.ApiClientBeforeRequest      
   { return nil }
+func (f *fakeApiClient) SetAfterFunction(callback 
plugin.ApiClientAfterResponse)  {}
+func (f *fakeApiClient) GetAfterFunction() plugin.ApiClientAfterResponse       
   { return nil }
+
+func (f *fakeApiClient) Get(path string, query url.Values, headers 
http.Header) (*http.Response, errors.Error) {
+       return &http.Response{StatusCode: 200, Body: 
io.NopCloser(strings.NewReader(f.body))}, nil
+}
+func (f *fakeApiClient) Post(path string, query url.Values, body interface{}, 
headers http.Header) (*http.Response, errors.Error) {
+       return nil, nil
+}
+
+func TestListBitbucketRepos_ReturnsNextPage(t *testing.T) {
+       client := &fakeApiClient{body: `{"pagelen":2,"page":1,"size":4,
+               
"next":"https://api.bitbucket.org/2.0/repositories/myworkspace?page=2";,
+               "values":[{"name":"repo-a","full_name":"myworkspace/repo-a"},
+               {"name":"repo-b","full_name":"myworkspace/repo-b"}]}`}
+       children, nextPage, err := listBitbucketRepos(client, "myworkspace", 
BitbucketRemotePagination{Page: 1, PageLen: 2})
+       assert.Nil(t, err)
+       assert.Len(t, children, 2)
+       if assert.NotNil(t, nextPage) {
+               assert.Equal(t, 2, nextPage.Page)
+       }
+}
+
+func TestListBitbucketRepos_LastPageHasNoNextPage(t *testing.T) {
+       client := &fakeApiClient{body: `{"pagelen":2,"page":2,"size":4,
+               "values":[{"name":"repo-c","full_name":"myworkspace/repo-c"},
+               {"name":"repo-d","full_name":"myworkspace/repo-d"}]}`}
+       children, nextPage, err := listBitbucketRepos(client, "myworkspace", 
BitbucketRemotePagination{Page: 2, PageLen: 2})
+       assert.Nil(t, err)
+       assert.Len(t, children, 2)
+       assert.Nil(t, nextPage)
+}
+
+func TestListBitbucketWorkspaces_ReturnsNextPage(t *testing.T) {
+       client := &fakeApiClient{body: `{"pagelen":1,"page":1,"size":2,
+               "next":"https://api.bitbucket.org/2.0/user/workspaces?page=2";,
+               "values":[{"workspace":{"slug":"ws-a","name":"Workspace A"}}]}`}
+       children, nextPage, err := listBitbucketWorkspaces(client, 
BitbucketRemotePagination{Page: 1, PageLen: 1})
+       assert.Nil(t, err)
+       assert.Len(t, children, 1)
+       if assert.NotNil(t, nextPage) {
+               assert.Equal(t, 2, nextPage.Page)
+       }
+}
diff --git a/backend/plugins/bitbucket/models/repo.go 
b/backend/plugins/bitbucket/models/repo.go
index b33df3615..126990a94 100644
--- a/backend/plugins/bitbucket/models/repo.go
+++ b/backend/plugins/bitbucket/models/repo.go
@@ -114,6 +114,7 @@ type WorkspaceResponse struct {
        Pagelen int             `json:"pagelen"`
        Page    int             `json:"page"`
        Size    int             `json:"size"`
+       Next    string          `json:"next"`
        Values  []GroupResponse `json:"values"`
 }
 
@@ -142,6 +143,7 @@ type ReposResponse struct {
        Pagelen int                `json:"pagelen"`
        Page    int                `json:"page"`
        Size    int                `json:"size"`
+       Next    string             `json:"next"`
        Values  []BitbucketApiRepo `json:"values"`
 }
 

Reply via email to