diff --git a/github/enterprise_codesecurity_configurations.go b/github/enterprise_codesecurity_configurations.go index 978a32ad8b7..ba4e7405839 100644 --- a/github/enterprise_codesecurity_configurations.go +++ b/github/enterprise_codesecurity_configurations.go @@ -20,14 +20,7 @@ import ( // supplied GitHub API will return an error. PerPage controls the number of items // per page (max 100 per GitHub API docs). type ListEnterpriseCodeSecurityConfigurationOptions struct { - // A cursor, as given in the Link header. If specified, the query only searches for security configurations before this cursor. - Before string `url:"before,omitempty"` - - // A cursor, as given in the Link header. If specified, the query only searches for security configurations after this cursor. - After string `url:"after,omitempty"` - - // For paginated result sets, the number of results to include per page. - PerPage int `url:"per_page,omitempty"` + ListCursorOptions } // ListCodeSecurityConfigurations lists all code security configurations available in an enterprise. diff --git a/github/enterprise_codesecurity_configurations_test.go b/github/enterprise_codesecurity_configurations_test.go index 5a807f158d5..c299c0a0e02 100644 --- a/github/enterprise_codesecurity_configurations_test.go +++ b/github/enterprise_codesecurity_configurations_test.go @@ -16,7 +16,13 @@ import ( func TestEnterpriseService_ListCodeSecurityConfigurations(t *testing.T) { t.Parallel() - opts := &ListEnterpriseCodeSecurityConfigurationOptions{Before: "1", After: "2", PerPage: 30} + opts := &ListEnterpriseCodeSecurityConfigurationOptions{ + ListCursorOptions: ListCursorOptions{ + Before: "1", + After: "2", + PerPage: 30, + }, + } ctx := t.Context() client, mux, _ := setup(t) @@ -391,7 +397,14 @@ func TestEnterpriseService_SetDefaultCodeSecurityConfiguration(t *testing.T) { func TestEnterpriseService_ListCodeSecurityConfigurationRepositories(t *testing.T) { t.Parallel() - opts := &ListCodeSecurityConfigurationRepositoriesOptions{Before: "1", After: "2", PerPage: 30, Status: "attached"} + opts := &ListCodeSecurityConfigurationRepositoriesOptions{ + ListCursorOptions: ListCursorOptions{ + Before: "1", + After: "2", + PerPage: 30, + }, + Status: "attached", + } ctx := t.Context() client, mux, _ := setup(t) diff --git a/github/github-iterators.go b/github/github-iterators.go index ddeb888747f..a7a0453ef8d 100644 --- a/github/github-iterators.go +++ b/github/github-iterators.go @@ -979,6 +979,68 @@ func (s *EnterpriseService) ListAssignmentsIter(ctx context.Context, enterprise } } +// ListCodeSecurityConfigurationRepositoriesIter returns an iterator that paginates through all results of ListCodeSecurityConfigurationRepositories. +func (s *EnterpriseService) ListCodeSecurityConfigurationRepositoriesIter(ctx context.Context, enterprise string, configurationID int64, opts *ListCodeSecurityConfigurationRepositoriesOptions) iter.Seq2[*RepositoryAttachment, error] { + return func(yield func(*RepositoryAttachment, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListCodeSecurityConfigurationRepositoriesOptions{} + } else { + opts = Ptr(*opts) + } + + for { + items, resp, err := s.ListCodeSecurityConfigurationRepositories(ctx, enterprise, configurationID, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range items { + if !yield(item, nil) { + return + } + } + + if resp.After == "" { + break + } + opts.ListCursorOptions.After = resp.After + } + } +} + +// ListCodeSecurityConfigurationsIter returns an iterator that paginates through all results of ListCodeSecurityConfigurations. +func (s *EnterpriseService) ListCodeSecurityConfigurationsIter(ctx context.Context, enterprise string, opts *ListEnterpriseCodeSecurityConfigurationOptions) iter.Seq2[*CodeSecurityConfiguration, error] { + return func(yield func(*CodeSecurityConfiguration, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListEnterpriseCodeSecurityConfigurationOptions{} + } else { + opts = Ptr(*opts) + } + + for { + items, resp, err := s.ListCodeSecurityConfigurations(ctx, enterprise, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range items { + if !yield(item, nil) { + return + } + } + + if resp.After == "" { + break + } + opts.ListCursorOptions.After = resp.After + } + } +} + // ListOrganizationCustomPropertyValuesIter returns an iterator that paginates through all results of ListOrganizationCustomPropertyValues. func (s *EnterpriseService) ListOrganizationCustomPropertyValuesIter(ctx context.Context, enterprise string, opts *ListOptions) iter.Seq2[*EnterpriseCustomPropertiesValues, error] { return func(yield func(*EnterpriseCustomPropertiesValues, error) bool) { @@ -1910,6 +1972,37 @@ func (s *OrganizationsService) ListBlockedUsersIter(ctx context.Context, org str } } +// ListCodeSecurityConfigurationRepositoriesIter returns an iterator that paginates through all results of ListCodeSecurityConfigurationRepositories. +func (s *OrganizationsService) ListCodeSecurityConfigurationRepositoriesIter(ctx context.Context, org string, configurationID int64, opts *ListCodeSecurityConfigurationRepositoriesOptions) iter.Seq2[*RepositoryAttachment, error] { + return func(yield func(*RepositoryAttachment, error) bool) { + // Create a copy of opts to avoid mutating the caller's struct + if opts == nil { + opts = &ListCodeSecurityConfigurationRepositoriesOptions{} + } else { + opts = Ptr(*opts) + } + + for { + items, resp, err := s.ListCodeSecurityConfigurationRepositories(ctx, org, configurationID, opts) + if err != nil { + yield(nil, err) + return + } + + for _, item := range items { + if !yield(item, nil) { + return + } + } + + if resp.After == "" { + break + } + opts.ListCursorOptions.After = resp.After + } + } +} + // ListCredentialAuthorizationsIter returns an iterator that paginates through all results of ListCredentialAuthorizations. func (s *OrganizationsService) ListCredentialAuthorizationsIter(ctx context.Context, org string, opts *CredentialAuthorizationsListOptions) iter.Seq2[*CredentialAuthorization, error] { return func(yield func(*CredentialAuthorization, error) bool) { diff --git a/github/github-iterators_test.go b/github/github-iterators_test.go index b8412d9dfa6..916d182be2b 100644 --- a/github/github-iterators_test.go +++ b/github/github-iterators_test.go @@ -2247,6 +2247,150 @@ func TestEnterpriseService_ListAssignmentsIter(t *testing.T) { } } +func TestEnterpriseService_ListCodeSecurityConfigurationRepositoriesIter(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.Enterprise.ListCodeSecurityConfigurationRepositoriesIter(t.Context(), "", 0, 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.Enterprise.ListCodeSecurityConfigurationRepositoriesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListCodeSecurityConfigurationRepositoriesOptions{} + iter = client.Enterprise.ListCodeSecurityConfigurationRepositoriesIter(t.Context(), "", 0, 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.Enterprise.ListCodeSecurityConfigurationRepositoriesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Enterprise.ListCodeSecurityConfigurationRepositoriesIter(t.Context(), "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Enterprise.ListCodeSecurityConfigurationRepositoriesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Enterprise.ListCodeSecurityConfigurationRepositoriesIter(t.Context(), "", 0, nil) + gotItems = 0 + iter(func(item *RepositoryAttachment, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Enterprise.ListCodeSecurityConfigurationRepositoriesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + +func TestEnterpriseService_ListCodeSecurityConfigurationsIter(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.Enterprise.ListCodeSecurityConfigurationsIter(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.Enterprise.ListCodeSecurityConfigurationsIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListEnterpriseCodeSecurityConfigurationOptions{} + iter = client.Enterprise.ListCodeSecurityConfigurationsIter(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.Enterprise.ListCodeSecurityConfigurationsIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Enterprise.ListCodeSecurityConfigurationsIter(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.Enterprise.ListCodeSecurityConfigurationsIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Enterprise.ListCodeSecurityConfigurationsIter(t.Context(), "", nil) + gotItems = 0 + iter(func(item *CodeSecurityConfiguration, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Enterprise.ListCodeSecurityConfigurationsIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + func TestEnterpriseService_ListOrganizationCustomPropertyValuesIter(t *testing.T) { t.Parallel() client, mux, _ := setup(t) @@ -4407,6 +4551,78 @@ func TestOrganizationsService_ListBlockedUsersIter(t *testing.T) { } } +func TestOrganizationsService_ListCodeSecurityConfigurationRepositoriesIter(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.Organizations.ListCodeSecurityConfigurationRepositoriesIter(t.Context(), "", 0, 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.Organizations.ListCodeSecurityConfigurationRepositoriesIter call 1 got %v items; want %v", gotItems, want) + } + + opts := &ListCodeSecurityConfigurationRepositoriesOptions{} + iter = client.Organizations.ListCodeSecurityConfigurationRepositoriesIter(t.Context(), "", 0, 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.Organizations.ListCodeSecurityConfigurationRepositoriesIter call 2 got %v items; want %v", gotItems, want) + } + + iter = client.Organizations.ListCodeSecurityConfigurationRepositoriesIter(t.Context(), "", 0, nil) + gotItems = 0 + for _, err := range iter { + gotItems++ + if err == nil { + t.Error("expected error; got nil") + } + } + if gotItems != 1 { + t.Errorf("client.Organizations.ListCodeSecurityConfigurationRepositoriesIter call 3 got %v items; want 1 (an error)", gotItems) + } + + iter = client.Organizations.ListCodeSecurityConfigurationRepositoriesIter(t.Context(), "", 0, nil) + gotItems = 0 + iter(func(item *RepositoryAttachment, err error) bool { + gotItems++ + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + return false + }) + if gotItems != 1 { + t.Errorf("client.Organizations.ListCodeSecurityConfigurationRepositoriesIter call 4 got %v items; want 1 (an error)", gotItems) + } +} + func TestOrganizationsService_ListCredentialAuthorizationsIter(t *testing.T) { t.Parallel() client, mux, _ := setup(t) diff --git a/github/orgs_codesecurity_configurations.go b/github/orgs_codesecurity_configurations.go index 6bacf0ba079..69b72f8fe46 100644 --- a/github/orgs_codesecurity_configurations.go +++ b/github/orgs_codesecurity_configurations.go @@ -125,14 +125,7 @@ type ListOrgCodeSecurityConfigurationOptions struct { // supplied GitHub API will return an error. PerPage controls the number of items // per page (max 100 per GitHub API docs). type ListCodeSecurityConfigurationRepositoriesOptions struct { - // A cursor, as given in the Link header. If specified, the query only searches for repositories before this cursor. - Before string `url:"before,omitempty"` - - // A cursor, as given in the Link header. If specified, the query only searches for repositories after this cursor. - After string `url:"after,omitempty"` - - // For paginated result sets, the number of results to include per page. - PerPage int `url:"per_page,omitempty"` + ListCursorOptions // A comma-separated list of statuses. If specified, only repositories with these attachment statuses will be returned. // diff --git a/github/orgs_codesecurity_configurations_test.go b/github/orgs_codesecurity_configurations_test.go index 708fe2a0c41..d9641696f9f 100644 --- a/github/orgs_codesecurity_configurations_test.go +++ b/github/orgs_codesecurity_configurations_test.go @@ -533,7 +533,14 @@ func TestOrganizationsService_SetDefaultCodeSecurityConfiguration(t *testing.T) func TestOrganizationsService_ListCodeSecurityConfigurationRepositories(t *testing.T) { t.Parallel() - opts := &ListCodeSecurityConfigurationRepositoriesOptions{Before: "1", After: "2", PerPage: 30, Status: "attached"} + opts := &ListCodeSecurityConfigurationRepositoriesOptions{ + ListCursorOptions: ListCursorOptions{ + Before: "1", + After: "2", + PerPage: 30, + }, + Status: "attached", + } ctx := t.Context() client, mux, _ := setup(t)