From bebfb1db1485f78f7b604a1aface78ec96131b31 Mon Sep 17 00:00:00 2001 From: khushboosharma Date: Fri, 28 Nov 2025 19:26:15 +0530 Subject: [PATCH] github_my_repository Table Erroring With Fine-Grained Personal Access Token closes #520 --- github/repo_utils.go | 17 +++++++ github/table_github_my_repository.go | 67 +++++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/github/repo_utils.go b/github/repo_utils.go index 1e0e5b6..574cdcd 100644 --- a/github/repo_utils.go +++ b/github/repo_utils.go @@ -3,6 +3,7 @@ package github import ( "context" "fmt" + "os" "slices" "github.com/shurcooL/githubv4" @@ -407,6 +408,10 @@ func extractRepoFromHydrateItem(h *plugin.HydrateData) (models.Repository, error } func appendRepoColumnIncludes(m *map[string]interface{}, cols []string) { + appendRepoColumnIncludesWithQueryData(m, cols, nil) +} + +func appendRepoColumnIncludesWithQueryData(m *map[string]interface{}, cols []string, d *plugin.QueryData) { optionals := map[string]string{ "allow_update_branch": "includeAllowUpdateBranch", "archived_at": "includeArchivedAt", @@ -480,6 +485,18 @@ func appendRepoColumnIncludes(m *map[string]interface{}, cols []string) { for key, value := range optionals { (*m)[value] = githubv4.Boolean(slices.Contains(cols, key)) } + + // Handle interaction_ability for Fine-Grained PATs in github_my_repository table + // With Fine-grained access token we are getting field error even though we have proper access. + // https://spec.graphql.org/October2021/#sec-Errors.Field-errors + // https://spec.graphql.org/October2021/#sec-Handling-Field-Errors + if d != nil && slices.Contains(cols, "interaction_ability") { + githubConfig := GetConfig(d.Connection) + token := os.Getenv("GITHUB_TOKEN") + if isGitHubPAT(token) || (githubConfig.Token != nil && isGitHubPAT(*githubConfig.Token)) { + (*m)["includeUserInteractionAbility"] = githubv4.Boolean(false) + } + } } func repoHydrateAllowUpdateBranch(_ context.Context, _ *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { diff --git a/github/table_github_my_repository.go b/github/table_github_my_repository.go index b62d149..64d341a 100644 --- a/github/table_github_my_repository.go +++ b/github/table_github_my_repository.go @@ -2,12 +2,35 @@ package github import ( "context" + "os" + "strings" + + "github.com/google/go-github/v55/github" "github.com/shurcooL/githubv4" "github.com/turbot/steampipe-plugin-github/github/models" + "github.com/turbot/steampipe-plugin-sdk/v5/grpc/proto" "github.com/turbot/steampipe-plugin-sdk/v5/plugin" + "github.com/turbot/steampipe-plugin-sdk/v5/plugin/transform" ) func tableGitHubMyRepository() *plugin.Table { + // Get shared columns and override hooks column with Fine-Grained PAT handling + columns := sharedRepositoryColumns() + + // Override hooks column to use the Fine-Grained PAT-aware hydrate function + for i, col := range columns { + if col.Name == "hooks" { + columns[i] = &plugin.Column{ + Name: "hooks", + Type: proto.ColumnType_JSON, + Description: "The API Hooks URL.", + Hydrate: hydrateMyRepositoryHooksFromV3, + Transform: transform.FromValue(), + } + break + } + } + return &plugin.Table{ Name: "github_my_repository", Description: "GitHub Repositories that you are associated with. GitHub Repositories contain all of your project's files and each file's revision history.", @@ -15,7 +38,7 @@ func tableGitHubMyRepository() *plugin.Table { Hydrate: tableGitHubMyRepositoryList, ShouldIgnoreError: isNotFoundError([]string{"404"}), }, - Columns: commonColumns(sharedRepositoryColumns()), + Columns: commonColumns(columns), } } @@ -39,7 +62,7 @@ func tableGitHubMyRepositoryList(ctx context.Context, d *plugin.QueryData, h *pl "pageSize": githubv4.Int(pageSize), "cursor": (*githubv4.String)(nil), } - appendRepoColumnIncludes(&variables, d.QueryContext.Columns) + appendRepoColumnIncludesWithQueryData(&variables, d.QueryContext.Columns, d) for { err := client.Query(ctx, &query, variables) @@ -66,3 +89,43 @@ func tableGitHubMyRepositoryList(ctx context.Context, d *plugin.QueryData, h *pl return nil, nil } + +// hydrateMyRepositoryHooksFromV3 is a version of hydrateRepositoryHooksFromV3 +// that skips hooks for Fine-Grained PATs (only for github_my_repository table) +func hydrateMyRepositoryHooksFromV3(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { + // Skip hooks for Fine-Grained PATs to avoid field errors + // With Fine-grained access token we are getting field error even though we have proper access. + // https://spec.graphql.org/October2021/#sec-Errors.Field-errors + // https://spec.graphql.org/October2021/#sec-Handling-Field-Errors + githubConfig := GetConfig(d.Connection) + token := os.Getenv("GITHUB_TOKEN") + if isGitHubPAT(token) || (githubConfig.Token != nil && isGitHubPAT(*githubConfig.Token)) { + return nil, nil + } + + repo, err := extractRepoFromHydrateItem(h) + if err != nil { + return nil, err + } + owner := repo.Owner.Login + repoName := repo.Name + + client := connect(ctx, d) + var repositoryHooks []*github.Hook + opt := &github.ListOptions{PerPage: 100} + + for { + hooks, resp, err := client.Repositories.ListHooks(ctx, owner, repoName, opt) + if err != nil && strings.Contains(err.Error(), "Not Found") { + return nil, nil + } else if err != nil { + return nil, err + } + repositoryHooks = append(repositoryHooks, hooks...) + if resp.NextPage == 0 { + break + } + opt.Page = resp.NextPage + } + return repositoryHooks, nil +}