From 9e1764e375f60a86dc4a4efcfc9bdf70b68693f9 Mon Sep 17 00:00:00 2001 From: Tyagiquamar Date: Thu, 17 Sep 2026 01:13:49 +0530 Subject: [PATCH 1/2] fix(admin): paginate GitHub organization listing in GetGithubUserStatus (#5284) --- admin/server/github.go | 24 +++++++++++++++++++++-- admin/server/github_test.go | 38 +++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/admin/server/github.go b/admin/server/github.go index 2a197f6eb30f..0afef53f68a8 100644 --- a/admin/server/github.go +++ b/admin/server/github.go @@ -90,12 +90,12 @@ func (s *Server) GetGithubUserStatus(ctx context.Context, req *adminv1.GetGithub client := github.NewTokenClient(ctx, token) // List all the private organizations for the authenticated user - orgs, _, err := client.Organizations.List(ctx, "", nil) + orgs, err := listOrganizations(ctx, client, "") if err != nil { return nil, fmt.Errorf("failed to get user organizations: %w", err) } // List all the public organizations for the authenticated user - publicOrgs, _, err := client.Organizations.List(ctx, user.GithubUsername, nil) + publicOrgs, err := listOrganizations(ctx, client, user.GithubUsername) if err != nil { return nil, fmt.Errorf("failed to get user organizations: %w", err) } @@ -1074,6 +1074,26 @@ func (s *Server) userAccessToken(ctx context.Context, user *database.User) (stri return oauthToken.AccessToken, nil } +// listOrganizations returns all organizations visible for the given user, +// following pagination. An empty user lists the organizations of the +// authenticated user. +func listOrganizations(ctx context.Context, client *github.Client, user string) ([]*github.Organization, error) { + orgs := make([]*github.Organization, 0) + opts := &github.ListOptions{PerPage: 100} + for { + page, resp, err := client.Organizations.List(ctx, user, opts) + if err != nil { + return nil, err + } + orgs = append(orgs, page...) + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + return orgs, nil +} + func (s *Server) fetchReposForUser(ctx context.Context, client *github.Client) ([]*adminv1.ListGithubUserReposResponse_Repo, error) { repos := make([]*adminv1.ListGithubUserReposResponse_Repo, 0) page := 1 diff --git a/admin/server/github_test.go b/admin/server/github_test.go index 74e648b34d34..b55c84bf92be 100644 --- a/admin/server/github_test.go +++ b/admin/server/github_test.go @@ -2,14 +2,52 @@ package server import ( "context" + "fmt" + "net/http" + "net/http/httptest" + "net/url" "os" "os/exec" "path/filepath" "testing" + "github.com/google/go-github/v71/github" "github.com/stretchr/testify/require" ) +func TestListOrganizationsPagination(t *testing.T) { + var srv *httptest.Server + srv = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + if r.URL.Query().Get("page") == "2" { + _, _ = w.Write([]byte(`[{"login":"org3"}]`)) + return + } + w.Header().Set("Link", fmt.Sprintf(`<%s%s?page=2>; rel="next"`, srv.URL, r.URL.Path)) + _, _ = w.Write([]byte(`[{"login":"org1"},{"login":"org2"}]`)) + })) + defer srv.Close() + + client := github.NewClient(nil) + base, err := url.Parse(srv.URL + "/") + require.NoError(t, err) + client.BaseURL = base + client.UploadURL = base + + // Organizations of the authenticated user. + orgs, err := listOrganizations(context.Background(), client, "") + require.NoError(t, err) + require.Len(t, orgs, 3) + require.Equal(t, "org1", orgs[0].GetLogin()) + require.Equal(t, "org2", orgs[1].GetLogin()) + require.Equal(t, "org3", orgs[2].GetLogin()) + + // Organizations of a named user. + orgs, err = listOrganizations(context.Background(), client, "octocat") + require.NoError(t, err) + require.Len(t, orgs, 3) +} + func TestMirrorGitRepo(t *testing.T) { src := setupMirrorSourceRepo(t) dest := t.TempDir() From 2582f860fa7d27c78c83ea204dc973cd1c9f43f6 Mon Sep 17 00:00:00 2001 From: Anshul Khandelwal <12948312+k-anshul@users.noreply.github.com> Date: Thu, 17 Sep 2026 11:29:25 +0530 Subject: [PATCH 2/2] move helper to the bottom of the file --- admin/server/github.go | 40 ++++++++++++++++++------------------- admin/server/github_test.go | 5 ----- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/admin/server/github.go b/admin/server/github.go index 0afef53f68a8..15b6ddd52830 100644 --- a/admin/server/github.go +++ b/admin/server/github.go @@ -1074,26 +1074,6 @@ func (s *Server) userAccessToken(ctx context.Context, user *database.User) (stri return oauthToken.AccessToken, nil } -// listOrganizations returns all organizations visible for the given user, -// following pagination. An empty user lists the organizations of the -// authenticated user. -func listOrganizations(ctx context.Context, client *github.Client, user string) ([]*github.Organization, error) { - orgs := make([]*github.Organization, 0) - opts := &github.ListOptions{PerPage: 100} - for { - page, resp, err := client.Organizations.List(ctx, user, opts) - if err != nil { - return nil, err - } - orgs = append(orgs, page...) - if resp.NextPage == 0 { - break - } - opts.Page = resp.NextPage - } - return orgs, nil -} - func (s *Server) fetchReposForUser(ctx context.Context, client *github.Client) ([]*adminv1.ListGithubUserReposResponse_Repo, error) { repos := make([]*adminv1.ListGithubUserReposResponse_Repo, 0) page := 1 @@ -1268,6 +1248,26 @@ func (s *Server) githubAppInstallationURL(state githubConnectState) (string, err return urlutil.MustWithQuery(res, map[string]string{"state": string(stateJSON)}), nil } +// listOrganizations returns all organizations visible for the given user, +// following pagination. An empty user lists the organizations of the +// authenticated user. +func listOrganizations(ctx context.Context, client *github.Client, user string) ([]*github.Organization, error) { + orgs := make([]*github.Organization, 0) + opts := &github.ListOptions{PerPage: 100} + for { + page, resp, err := client.Organizations.List(ctx, user, opts) + if err != nil { + return nil, err + } + orgs = append(orgs, page...) + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + return orgs, nil +} + func fromStringPtr(s *string) string { if s == nil { return "" diff --git a/admin/server/github_test.go b/admin/server/github_test.go index b55c84bf92be..f92ac5d14f31 100644 --- a/admin/server/github_test.go +++ b/admin/server/github_test.go @@ -41,11 +41,6 @@ func TestListOrganizationsPagination(t *testing.T) { require.Equal(t, "org1", orgs[0].GetLogin()) require.Equal(t, "org2", orgs[1].GetLogin()) require.Equal(t, "org3", orgs[2].GetLogin()) - - // Organizations of a named user. - orgs, err = listOrganizations(context.Background(), client, "octocat") - require.NoError(t, err) - require.Len(t, orgs, 3) } func TestMirrorGitRepo(t *testing.T) {