Skip to content

Commit

Permalink
Retrieve all user repo perms with a single API call (#3211)
Browse files Browse the repository at this point in the history
This pull request addresses the issue #3210.

Ideally, the Bitbucket API should include repository permissions when
utilizing the 'get all repositories' endpoint. However, as it currently
does not provide this information, a viable solution is to fetch all
permissions for every repository and then employ a dictionary to
associate each repository with its respective permissions.

Without implementing this fix, logging in becomes problematic for users
with access to a substantial number of repositories (300+), as the
process takes over 2 minutes to complete.

---------

Co-authored-by: Alberto Alcón <[email protected]>
  • Loading branch information
healdropper and Alberto Alcón authored Jan 19, 2024
1 parent 7b29d1d commit 07479dd
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 26 deletions.
17 changes: 12 additions & 5 deletions server/forge/bitbucket/bitbucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,19 +203,26 @@ func (c *config) Repos(ctx context.Context, u *model.User) ([]*model.Repo, error
return nil, err
}

userPermisions, err := client.ListPermissionsAll()
if err != nil {
return nil, err
}

userPermissionsByRepo := make(map[string]*internal.RepoPerm)
for _, permission := range userPermisions {
userPermissionsByRepo[permission.Repo.FullName] = permission
}

var all []*model.Repo
for _, workspace := range workspaces {
repos, err := client.ListReposAll(workspace.Slug)
if err != nil {
return nil, err
}
for _, repo := range repos {
perm, err := client.GetPermission(repo.FullName)
if err != nil {
return nil, err
if perm, ok := userPermissionsByRepo[repo.FullName]; ok {
all = append(all, convertRepo(repo, perm))
}

all = append(all, convertRepo(repo, perm))
}
}
return all, nil
Expand Down
49 changes: 30 additions & 19 deletions server/forge/bitbucket/fixtures/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,21 +185,11 @@ func getUserRepos(c *gin.Context) {
}
}

func permission(p string) string {
return fmt.Sprintf(permissionPayload, p)
}

func getPermissions(c *gin.Context) {
query := c.Request.URL.Query()["q"][0]
switch query {
case `repository.full_name="test_name/permission_read"`:
c.String(200, permission("read"))
case `repository.full_name="test_name/permission_write"`:
c.String(200, permission("write"))
case `repository.full_name="test_name/permission_admin"`:
c.String(200, permission("admin"))
default:
c.String(200, permission("read"))
if c.Query("page") == "" || c.Query("page") == "1" {
c.String(200, permissionsPayLoad)
} else {
c.String(200, "{\"values\":[]}")
}
}

Expand Down Expand Up @@ -367,14 +357,35 @@ const workspacesPayload = `
}
`

const permissionPayload = `
const permissionsPayLoad = `
{
"pagelen": 1,
"pagelen": 100,
"page": 1,
"values": [
{
"permission": "%s"
"repository": {
"full_name": "test_name/repo_name"
},
"permission": "read"
},
{
"repository": {
"full_name": "test_name/permission_read"
},
"permission": "read"
},
{
"repository": {
"full_name": "test_name/permission_write"
},
"permission": "write"
},
{
"repository": {
"full_name": "test_name/permission_admin"
},
"permission": "admin"
}
],
"page": 1
]
}
`
22 changes: 20 additions & 2 deletions server/forge/bitbucket/internal/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ const (
const (
pathUser = "%s/2.0/user/"
pathEmails = "%s/2.0/user/emails"
pathPermissions = "%s/2.0/user/permissions/repositories?q=repository.full_name=%q"
pathPermission = "%s/2.0/user/permissions/repositories?q=repository.full_name=%q"
pathPermissions = "%s/2.0/user/permissions/repositories?%s"
pathWorkspaces = "%s/2.0/workspaces/?%s"
pathWorkspace = "%s/2.0/workspaces/%s"
pathRepo = "%s/2.0/repositories/%s/%s"
Expand Down Expand Up @@ -161,7 +162,7 @@ func (c *Client) CreateStatus(owner, name, revision string, status *PipelineStat

func (c *Client) GetPermission(fullName string) (*RepoPerm, error) {
out := new(RepoPermResp)
uri := fmt.Sprintf(pathPermissions, c.base, fullName)
uri := fmt.Sprintf(pathPermission, c.base, fullName)
_, err := c.do(uri, get, nil, out)
if err != nil {
return nil, err
Expand All @@ -173,6 +174,23 @@ func (c *Client) GetPermission(fullName string) (*RepoPerm, error) {
return out.Values[0], nil
}

func (c *Client) ListPermissions(opts *ListOpts) (*RepoPermResp, error) {
out := new(RepoPermResp)
uri := fmt.Sprintf(pathPermissions, c.base, opts.Encode())
_, err := c.do(uri, get, nil, out)
return out, err
}

func (c *Client) ListPermissionsAll() ([]*RepoPerm, error) {
return shared_utils.Paginate(func(page int) ([]*RepoPerm, error) {
resp, err := c.ListPermissions(&ListOpts{Page: page, PageLen: 100})
if err != nil {
return nil, err
}
return resp.Values, nil
})
}

func (c *Client) ListBranches(owner, name string, opts *ListOpts) ([]*Branch, error) {
out := new(BranchResp)
uri := fmt.Sprintf(pathBranches, c.base, owner, name, opts.Encode())
Expand Down
1 change: 1 addition & 0 deletions server/forge/bitbucket/internal/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ type RepoPermResp struct {

type RepoPerm struct {
Permission string `json:"permission"`
Repo Repo `json:"repository"`
}

type BranchResp struct {
Expand Down

0 comments on commit 07479dd

Please sign in to comment.