Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add List fine-grained personal access tokens with access to organization resources API #3215

Merged
merged 4 commits into from
Jul 27, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 62 additions & 3 deletions github/orgs_personal_access_tokens.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"context"
"fmt"
"net/http"
"net/url"
"strings"
)

// PersonalAccessToken represents the minimal representation of an organization programmatic access grant.
Expand Down Expand Up @@ -46,20 +48,52 @@ type PersonalAccessToken struct {
TokenLastUsedAt *Timestamp `json:"token_last_used_at"`
}

// ListFineGrainedPATOptions specifies optional parameters to ListFineGrainedPersonalAccessTokens.
type ListFineGrainedPATOptions struct {
// The property by which to sort the results.
// Default: created_at
// Value: created_at
Sort string `url:"sort,omitempty"`

// The direction to sort the results by.
// Default: desc
// Value: asc, desc
Direction string `url:"direction,omitempty"`

// A list of owner usernames to use to filter the results.
Owner []string `url:"-"`

// The name of the repository to use to filter the results.
Repository string `url:"repository,omitempty"`

// The permission to use to filter the results.
Permission string `url:"permission,omitempty"`

// Only show fine-grained personal access tokens used before the given time.
// This is a timestamp in ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ.
LastUsedBefore string `url:"last_used_before,omitempty"`

// Only show fine-grained personal access tokens used after the given time.
// This is a timestamp in ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ.
LastUsedAfter string `url:"last_used_after,omitempty"`

ListOptions
}

// ListFineGrainedPersonalAccessTokens lists approved fine-grained personal access tokens owned by organization members that can access organization resources.
// Only GitHub Apps can call this API, using the `Personal access tokens` organization permissions (read).
//
// GitHub API docs: https://docs.github.com/rest/orgs/personal-access-tokens#list-fine-grained-personal-access-tokens-with-access-to-organization-resources
//
//meta:operation GET /orgs/{org}/personal-access-tokens
func (s *OrganizationsService) ListFineGrainedPersonalAccessTokens(ctx context.Context, org string, opts *ListOptions) ([]*PersonalAccessToken, *Response, error) {
func (s *OrganizationsService) ListFineGrainedPersonalAccessTokens(ctx context.Context, org string, opts *ListFineGrainedPATOptions) ([]*PersonalAccessToken, *Response, error) {
u := fmt.Sprintf("orgs/%v/personal-access-tokens", org)
u, err := addOptions(u, opts)
u, err := addListFineGrainedPATOptions(u, opts)
if err != nil {
return nil, nil, err
}

req, err := s.client.NewRequest(http.MethodGet, u, &opts)
req, err := s.client.NewRequest(http.MethodGet, u, opts)
if err != nil {
return nil, nil, err
}
Expand Down Expand Up @@ -97,3 +131,28 @@ func (s *OrganizationsService) ReviewPersonalAccessTokenRequest(ctx context.Cont

return s.client.Do(ctx, req, nil)
}

// GitHub API expects the owner parameter to be a list of strings in the `owner[]=...` format.
// This function adds the owner parameter to the URL query string with the correct format if it is set.
func addListFineGrainedPATOptions(s string, opts *ListFineGrainedPATOptions) (string, error) {
u, err := addOptions(s, opts)
if err != nil {
return s, err
}

if len(opts.Owner) > 0 {
ownerVals := make([]string, len(opts.Owner))
for i, owner := range opts.Owner {
ownerVals[i] = fmt.Sprintf("owner[]=%s", url.QueryEscape(owner))
}
ownerQuery := strings.Join(ownerVals, "&")

if strings.Contains(u, "?") {
u += "&" + ownerQuery
} else {
u += "?" + ownerQuery
}
}

return u, nil
}
21 changes: 19 additions & 2 deletions github/orgs_personal_access_tokens_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,19 @@ func TestOrganizationsService_ListFineGrainedPersonalAccessTokens(t *testing.T)

mux.HandleFunc("/orgs/o/personal-access-tokens", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testFormValues(t, r, values{"per_page": "2", "page": "2"})
expectedQuery := values{
"per_page": "2",
"page": "2",
"sort": "created_at",
"direction": "desc",
"owner[]": "octocat",
}
for key, expectedValue := range expectedQuery {
actualValue := r.URL.Query().Get(key)
if actualValue != expectedValue {
t.Errorf("Expected query param %s to be %s, got %s", key, expectedValue, actualValue)
}
}
fmt.Fprint(w, `
[
{
Expand Down Expand Up @@ -65,7 +77,12 @@ func TestOrganizationsService_ListFineGrainedPersonalAccessTokens(t *testing.T)
]`)
})

opts := &ListOptions{Page: 2, PerPage: 2}
opts := &ListFineGrainedPATOptions{
ListOptions: ListOptions{Page: 2, PerPage: 2},
Sort: "created_at",
Direction: "desc",
Owner: []string{"octocat"},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please modify your test case to list multiple owners in order to demonstrate how this code behaves for this case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done!

}
ctx := context.Background()
tokens, resp, err := client.Organizations.ListFineGrainedPersonalAccessTokens(ctx, "o", opts)
if err != nil {
Expand Down