diff --git a/github/github-accessors.go b/github/github-accessors.go index 27c88d51c39..c6e6f959932 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -14910,6 +14910,14 @@ func (l *ListGlobalSecurityAdvisoriesOptions) GetUpdated() string { return *l.Updated } +// GetFeatured returns the Featured field if it's non-nil, zero value otherwise. +func (l *ListLicensesOptions) GetFeatured() bool { + if l == nil || l.Featured == nil { + return false + } + return *l.Featured +} + // GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. func (l *ListOrganizations) GetTotalCount() int { if l == nil || l.TotalCount == nil { diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index 835f310e0b7..6004471ee11 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -19411,6 +19411,17 @@ func TestListGlobalSecurityAdvisoriesOptions_GetUpdated(tt *testing.T) { l.GetUpdated() } +func TestListLicensesOptions_GetFeatured(tt *testing.T) { + tt.Parallel() + var zeroValue bool + l := &ListLicensesOptions{Featured: &zeroValue} + l.GetFeatured() + l = &ListLicensesOptions{} + l.GetFeatured() + l = nil + l.GetFeatured() +} + func TestListOrganizations_GetTotalCount(tt *testing.T) { tt.Parallel() var zeroValue int diff --git a/github/github-iterators.go b/github/github-iterators.go index 691b5ee10ce..a66e293bcde 100644 --- a/github/github-iterators.go +++ b/github/github-iterators.go @@ -1664,6 +1664,37 @@ func (s *IssuesService) ListRepositoryEventsIter(ctx context.Context, owner stri } } +// ListIter returns an iterator that paginates through all results of List. +func (s *LicensesService) ListIter(ctx context.Context, opts *ListLicensesOptions) iter.Seq2[*License, error] { + return func(yield func(*License, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListLicensesOptions{} + } else { + opts = Ptr(*opts) + } + + for { + items, resp, err := s.List(ctx, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range items { + if !yield(item, nil) { + return + } + } + + if resp.NextPage == 0 { + break + } + opts.ListOptions.Page = resp.NextPage + } + } +} + // ListMarketplacePurchasesForUserIter returns an iterator that paginates through all results of ListMarketplacePurchasesForUser. func (s *MarketplaceService) ListMarketplacePurchasesForUserIter(ctx context.Context, opts *ListOptions) iter.Seq2[*MarketplacePurchase, error] { return func(yield func(*MarketplacePurchase, error) bool) { diff --git a/github/github-iterators_test.go b/github/github-iterators_test.go index fa5197ccf22..613928f2c3c 100644 --- a/github/github-iterators_test.go +++ b/github/github-iterators_test.go @@ -3831,6 +3831,78 @@ func TestIssuesService_ListRepositoryEventsIter(t *testing.T) { } } +func TestLicensesService_ListIter(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + var callNum int + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + callNum++ + switch callNum { + case 1: + w.Header().Set("Link", `; rel="next"`) + fmt.Fprint(w, `[{},{},{}]`) + case 2: + fmt.Fprint(w, `[{},{},{},{}]`) + case 3: + fmt.Fprint(w, `[{},{}]`) + case 4: + w.WriteHeader(http.StatusNotFound) + case 5: + fmt.Fprint(w, `[{},{}]`) + } + }) + + iter := client.Licenses.ListIter(t.Context(), nil) + var gotItems int + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 7; gotItems != want { + t.Errorf("client.Licenses.ListIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListLicensesOptions{} + iter = client.Licenses.ListIter(t.Context(), opts) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + } + if want := 2; gotItems != want { + t.Errorf("client.Licenses.ListIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Licenses.ListIter(t.Context(), nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Licenses.ListIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Licenses.ListIter(t.Context(), nil) + gotItems = 0 + iter(func(item *License, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Licenses.ListIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + func TestMarketplaceService_ListMarketplacePurchasesForUserIter(t *testing.T) { t.Parallel() client, mux, _ := setup(t) diff --git a/github/licenses.go b/github/licenses.go index 60dfd71a193..c589a8152d6 100644 --- a/github/licenses.go +++ b/github/licenses.go @@ -58,13 +58,26 @@ func (l License) String() string { return Stringify(l) } +// ListLicensesOptions specifies the optional parameters to the LicensesService.List method. +type ListLicensesOptions struct { + Featured *bool `url:"featured,omitempty"` + + ListOptions +} + // List popular open source licenses. // // GitHub API docs: https://docs.github.com/rest/licenses/licenses#get-all-commonly-used-licenses // //meta:operation GET /licenses -func (s *LicensesService) List(ctx context.Context) ([]*License, *Response, error) { - req, err := s.client.NewRequest("GET", "licenses", nil) +func (s *LicensesService) List(ctx context.Context, opts *ListLicensesOptions) ([]*License, *Response, error) { + u := "licenses" + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) if err != nil { return nil, nil, err } diff --git a/github/licenses_test.go b/github/licenses_test.go index 94b0b98727a..700667a4bdd 100644 --- a/github/licenses_test.go +++ b/github/licenses_test.go @@ -115,11 +115,13 @@ func TestLicensesService_List(t *testing.T) { mux.HandleFunc("/licenses", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") + testFormValues(t, r, values{"featured": "true", "page": "2", "per_page": "20"}) fmt.Fprint(w, `[{"key":"mit","name":"MIT","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","featured":true}]`) }) + opts := &ListLicensesOptions{Featured: Ptr(true), ListOptions: ListOptions{Page: 2, PerPage: 20}} ctx := t.Context() - licenses, _, err := client.Licenses.List(ctx) + licenses, _, err := client.Licenses.List(ctx, opts) if err != nil { t.Errorf("Licenses.List returned error: %v", err) } @@ -137,7 +139,7 @@ func TestLicensesService_List(t *testing.T) { const methodName = "List" testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { - got, resp, err := client.Licenses.List(ctx) + got, resp, err := client.Licenses.List(ctx, opts) if got != nil { t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) } diff --git a/test/integration/licences_test.go b/test/integration/licences_test.go new file mode 100644 index 00000000000..ed52311d833 --- /dev/null +++ b/test/integration/licences_test.go @@ -0,0 +1,36 @@ +// Copyright 2026 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build integration + +package integration + +import ( + "testing" + + "github.com/google/go-github/v82/github" +) + +func TestLicenses_ListIter(t *testing.T) { + opts := &github.ListLicensesOptions{ + Featured: github.Ptr(true), + ListOptions: github.ListOptions{ + Page: 1, + PerPage: 1, + }, + } + + var featuredLicensesCount int + for _, err := range client.Licenses.ListIter(t.Context(), opts) { + if err != nil { + t.Fatalf("Licenses.ListIter returned error during iteration: %v", err) + } + featuredLicensesCount++ + } + + if featuredLicensesCount < 2 { + t.Errorf("Licenses.ListIter returned fewer than 2 featured licenses: %v", featuredLicensesCount) + } +}