Note: this package leverages the new
iter
package, and it needs Go1.23
.
The gh-iter
package provides an iterator that can be used with the google/go-github
client.
It supports automatic pagination with generic types.
package main
import (
"fmt"
ghiter "github.com/enrichman/gh-iter"
"github.com/google/go-github/v66/github"
)
func main() {
// init your Github client
client := github.NewClient(nil)
// create an iterator, and start looping! 🎉
users := ghiter.NewFromFn(client.Users.ListAll)
for u := range users.All() {
fmt.Println(*u.Login)
}
// check if the loop stopped because of an error
if err := users.Err(); err != nil {
// something happened :(
panic(err)
}
}
Depending of the API you need to use you can create an iterator from one of the three provided constructor:
ghiter.NewFromFn(client.Users.ListAll)
ghiter.NewFromFn1(client.Repositories.ListByUser, "enrichman")
ghiter.NewFromFn2(client.Issues.ListByRepo, "enrichman", "gh-iter")
Then you can simply loop through the objects with the All()
method.
You can tweak the iteration providing your own options. They will be updated during the loop.
For example if you want to request only 5 repositories per request:
ghiter.NewFromFn1(client.Repositories.ListByUser, "enrichman").
Opts(&github.RepositoryListByUserOptions{
ListOptions: github.ListOptions{PerPage: 5},
})
If you don't provide a context with the Ctx()
func an empty context.Background
will be used. You can use a custom context to have a more granular control, for example if you want to close the iteration from a timeout, or with a manual cancellation.
You can check if the int
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
repos := ghiter.NewFromFn1(client.Repositories.ListByUser, "enrichman").Ctx(ctx)
for repo := range repos.All() {
if *repo.Name == "myrepo" {
fmt.Println(*repo.Name)
cancel()
}
}
Some APIs do not match the "standard" string arguments, or the returned type is not an array. In these cases you can still use this package, but you will need to provide a "custom func" to the ghiter.NewFromFn
constructor.
For example the client.Teams.ListTeamReposByID
needs the orgID, teamID int64
arguments:
repos := ghiter.NewFromFn(func(ctx context.Context, opts *github.ListOptions) ([]*github.Repository, *github.Response, error) {
return client.Teams.ListTeamReposByID(ctx, 123, 456, opts)
})
In case the returned object is not an array you will have to "unwrap" it.
For example the client.Teams.ListIDPGroupsInOrganization
returns a IDPGroupList, and not a slice.
idpGroups := ghiter.NewFromFn(func(ctx context.Context, opts *github.ListCursorOptions) ([]*github.IDPGroup, *github.Response, error) {
groups, resp, err := client.Teams.ListIDPGroupsInOrganization(ctx, "myorg", opts)
// remember to check for nil!
if groups != nil {
return groups.Groups, resp, err
}
return nil, resp, err
})
If you like the project please star it on Github 🌟, and feel free to drop me a note, or open an issue!