From 8eac70f648ac7538924c026bc2104cc6a51eaf76 Mon Sep 17 00:00:00 2001 From: Christoph Jerolimov Date: Wed, 1 Feb 2023 03:51:43 +0100 Subject: [PATCH 1/5] Fix parse token expiration (#2650) Fixes: #2649 . --- github/github.go | 16 +++++++++++----- github/github_test.go | 12 +++++++++++- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/github/github.go b/github/github.go index 6b37f082cf..a24d39dee4 100644 --- a/github/github.go +++ b/github/github.go @@ -571,7 +571,8 @@ type Response struct { // propagate to Response. Rate Rate - // token's expiration date + // token's expiration date. Timestamp is 0001-01-01 when token doesn't expire. + // So it is valid for TokenExpiration.Equal(Timestamp{}) or TokenExpiration.Time.After(time.Now()) TokenExpiration Timestamp } @@ -672,14 +673,19 @@ func parseRate(r *http.Response) Rate { } // parseTokenExpiration parses the TokenExpiration related headers. +// Returns 0001-01-01 if the header is not defined or could not be parsed. func parseTokenExpiration(r *http.Response) Timestamp { - var exp Timestamp if v := r.Header.Get(headerTokenExpiration); v != "" { - if t, err := time.Parse("2006-01-02 03:04:05 MST", v); err == nil { - exp = Timestamp{t.Local()} + if t, err := time.Parse("2006-01-02 15:04:05 MST", v); err == nil { + return Timestamp{t.Local()} + } + // Some tokens include the timezone offset instead of the timezone. + // https://github.com/google/go-github/issues/2649 + if t, err := time.Parse("2006-01-02 15:04:05 -0700", v); err == nil { + return Timestamp{t.Local()} } } - return exp + return Timestamp{} // 0001-01-01 00:00:00 } type requestContext uint8 diff --git a/github/github_test.go b/github/github_test.go index 7342e2dfd7..0c96940434 100644 --- a/github/github_test.go +++ b/github/github_test.go @@ -2860,6 +2860,16 @@ func TestParseTokenExpiration(t *testing.T) { header: "2021-09-03 02:34:04 UTC", want: Timestamp{time.Date(2021, time.September, 3, 2, 34, 4, 0, time.UTC)}, }, + { + header: "2021-09-03 14:34:04 UTC", + want: Timestamp{time.Date(2021, time.September, 3, 14, 34, 4, 0, time.UTC)}, + }, + // Some tokens include the timezone offset instead of the timezone. + // https://github.com/google/go-github/issues/2649 + { + header: "2023-04-26 20:23:26 +0200", + want: Timestamp{time.Date(2023, time.April, 26, 18, 23, 26, 0, time.UTC)}, + }, } for _, tt := range tests { @@ -2871,7 +2881,7 @@ func TestParseTokenExpiration(t *testing.T) { res.Header.Set(headerTokenExpiration, tt.header) exp := parseTokenExpiration(res) if !exp.Equal(tt.want) { - t.Errorf("parseTokenExpiration returned %#v, want %#v", exp, tt.want) + t.Errorf("parseTokenExpiration of %q\nreturned %#v\n want %#v", tt.header, exp, tt.want) } } } From 72dd8ad734d4c10072d88321bfa91a6dea76181c Mon Sep 17 00:00:00 2001 From: Federico Di Pierro Date: Wed, 1 Feb 2023 16:15:28 +0100 Subject: [PATCH 2/5] Add support for actions variables (#2652) Fixes: #2624. --- github/actions_variables.go | 293 ++++++++++++++ github/actions_variables_test.go | 659 +++++++++++++++++++++++++++++++ github/github-accessors.go | 40 ++ github/github-accessors_test.go | 47 +++ 4 files changed, 1039 insertions(+) create mode 100644 github/actions_variables.go create mode 100644 github/actions_variables_test.go diff --git a/github/actions_variables.go b/github/actions_variables.go new file mode 100644 index 0000000000..29445edd04 --- /dev/null +++ b/github/actions_variables.go @@ -0,0 +1,293 @@ +// Copyright 2023 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. + +package github + +import ( + "context" + "fmt" +) + +// ActionsVariable represents a repository action variable. +type ActionsVariable struct { + Name string `json:"name"` + Value string `json:"value"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + Visibility *string `json:"visibility,omitempty"` + // Used by ListOrgVariables and GetOrgVariables + SelectedRepositoriesURL *string `json:"selected_repositories_url,omitempty"` + // Used by UpdateOrgVariable and CreateOrgVariable + SelectedRepositoryIDs *SelectedRepoIDs `json:"selected_repository_ids,omitempty"` +} + +// ActionsVariables represents one item from the ListVariables response. +type ActionsVariables struct { + TotalCount int `json:"total_count"` + Variables []*ActionsVariable `json:"variables"` +} + +func (s *ActionsService) listVariables(ctx context.Context, url string, opts *ListOptions) (*ActionsVariables, *Response, error) { + u, err := addOptions(url, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + variables := new(ActionsVariables) + resp, err := s.client.Do(ctx, req, &variables) + if err != nil { + return nil, resp, err + } + + return variables, resp, nil +} + +// ListRepoVariables lists all variables available in a repository. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/variables#list-repository-variables +func (s *ActionsService) ListRepoVariables(ctx context.Context, owner, repo string, opts *ListOptions) (*ActionsVariables, *Response, error) { + url := fmt.Sprintf("repos/%v/%v/actions/variables", owner, repo) + return s.listVariables(ctx, url, opts) +} + +// ListOrgVariables lists all variables available in an organization. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/variables#list-organization-variables +func (s *ActionsService) ListOrgVariables(ctx context.Context, org string, opts *ListOptions) (*ActionsVariables, *Response, error) { + url := fmt.Sprintf("orgs/%v/actions/variables", org) + return s.listVariables(ctx, url, opts) +} + +// ListEnvVariables lists all variables available in an environment. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/variables#list-environment-variables +func (s *ActionsService) ListEnvVariables(ctx context.Context, repoID int, env string, opts *ListOptions) (*ActionsVariables, *Response, error) { + url := fmt.Sprintf("repositories/%v/environments/%v/variables", repoID, env) + return s.listVariables(ctx, url, opts) +} + +func (s *ActionsService) getVariable(ctx context.Context, url string) (*ActionsVariable, *Response, error) { + req, err := s.client.NewRequest("GET", url, nil) + if err != nil { + return nil, nil, err + } + + variable := new(ActionsVariable) + resp, err := s.client.Do(ctx, req, variable) + if err != nil { + return nil, resp, err + } + + return variable, resp, nil +} + +// GetRepoVariable gets a single repository variable. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/variables#get-a-repository-variable +func (s *ActionsService) GetRepoVariable(ctx context.Context, owner, repo, name string) (*ActionsVariable, *Response, error) { + url := fmt.Sprintf("repos/%v/%v/actions/variables/%v", owner, repo, name) + return s.getVariable(ctx, url) +} + +// GetOrgVariable gets a single organization variable. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/variables#get-an-organization-variable +func (s *ActionsService) GetOrgVariable(ctx context.Context, org, name string) (*ActionsVariable, *Response, error) { + url := fmt.Sprintf("orgs/%v/actions/variables/%v", org, name) + return s.getVariable(ctx, url) +} + +// GetEnvVariable gets a single environment variable. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/variables#get-an-environment-variable +func (s *ActionsService) GetEnvVariable(ctx context.Context, repoID int, env, variableName string) (*ActionsVariable, *Response, error) { + url := fmt.Sprintf("repositories/%v/environments/%v/variables/%v", repoID, env, variableName) + return s.getVariable(ctx, url) +} + +func (s *ActionsService) postVariable(ctx context.Context, url string, variable *ActionsVariable) (*Response, error) { + req, err := s.client.NewRequest("POST", url, variable) + if err != nil { + return nil, err + } + return s.client.Do(ctx, req, nil) +} + +// CreateRepoVariable creates a repository variable. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/variables#create-a-repository-variable +func (s *ActionsService) CreateRepoVariable(ctx context.Context, owner, repo string, variable *ActionsVariable) (*Response, error) { + url := fmt.Sprintf("repos/%v/%v/actions/variables", owner, repo) + return s.postVariable(ctx, url, variable) +} + +// CreateOrgVariable creates an organization variable. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/variables#create-an-organization-variable +func (s *ActionsService) CreateOrgVariable(ctx context.Context, org string, variable *ActionsVariable) (*Response, error) { + url := fmt.Sprintf("orgs/%v/actions/variables", org) + return s.postVariable(ctx, url, variable) +} + +// CreateEnvVariable creates an environment variable. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/variables#create-an-environment-variable +func (s *ActionsService) CreateEnvVariable(ctx context.Context, repoID int, env string, variable *ActionsVariable) (*Response, error) { + url := fmt.Sprintf("repositories/%v/environments/%v/variables", repoID, env) + return s.postVariable(ctx, url, variable) +} + +func (s *ActionsService) patchVariable(ctx context.Context, url string, variable *ActionsVariable) (*Response, error) { + req, err := s.client.NewRequest("PATCH", url, variable) + if err != nil { + return nil, err + } + return s.client.Do(ctx, req, nil) +} + +// UpdateRepoVariable updates a repository variable. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/variables#update-a-repository-variable +func (s *ActionsService) UpdateRepoVariable(ctx context.Context, owner, repo string, variable *ActionsVariable) (*Response, error) { + url := fmt.Sprintf("repos/%v/%v/actions/variables/%v", owner, repo, variable.Name) + return s.patchVariable(ctx, url, variable) +} + +// UpdateOrgVariable updates an organization variable. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/variables#update-an-organization-variable +func (s *ActionsService) UpdateOrgVariable(ctx context.Context, org string, variable *ActionsVariable) (*Response, error) { + url := fmt.Sprintf("orgs/%v/actions/variables/%v", org, variable.Name) + return s.patchVariable(ctx, url, variable) +} + +// UpdateEnvVariable updates an environment variable. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/variables#create-an-environment-variable +func (s *ActionsService) UpdateEnvVariable(ctx context.Context, repoID int, env string, variable *ActionsVariable) (*Response, error) { + url := fmt.Sprintf("repositories/%v/environments/%v/variables/%v", repoID, env, variable.Name) + return s.patchVariable(ctx, url, variable) +} + +func (s *ActionsService) deleteVariable(ctx context.Context, url string) (*Response, error) { + req, err := s.client.NewRequest("DELETE", url, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// DeleteRepoVariable deletes a variable in a repository. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/variables#delete-a-repository-variable +func (s *ActionsService) DeleteRepoVariable(ctx context.Context, owner, repo, name string) (*Response, error) { + url := fmt.Sprintf("repos/%v/%v/actions/variables/%v", owner, repo, name) + return s.deleteVariable(ctx, url) +} + +// DeleteOrgVariable deletes a variable in an organization. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/variables#delete-an-organization-variable +func (s *ActionsService) DeleteOrgVariable(ctx context.Context, org, name string) (*Response, error) { + url := fmt.Sprintf("orgs/%v/actions/variables/%v", org, name) + return s.deleteVariable(ctx, url) +} + +// DeleteEnvVariable deletes a variable in an environment. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/variables#delete-an-environment-variable +func (s *ActionsService) DeleteEnvVariable(ctx context.Context, repoID int, env, variableName string) (*Response, error) { + url := fmt.Sprintf("repositories/%v/environments/%v/variables/%v", repoID, env, variableName) + return s.deleteVariable(ctx, url) +} + +func (s *ActionsService) listSelectedReposForVariable(ctx context.Context, url string, opts *ListOptions) (*SelectedReposList, *Response, error) { + u, err := addOptions(url, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + result := new(SelectedReposList) + resp, err := s.client.Do(ctx, req, result) + if err != nil { + return nil, resp, err + } + + return result, resp, nil +} + +// ListSelectedReposForOrgVariable lists all repositories that have access to a variable. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/variables#list-selected-repositories-for-an-organization-variable +func (s *ActionsService) ListSelectedReposForOrgVariable(ctx context.Context, org, name string, opts *ListOptions) (*SelectedReposList, *Response, error) { + url := fmt.Sprintf("orgs/%v/actions/variables/%v/repositories", org, name) + return s.listSelectedReposForVariable(ctx, url, opts) +} + +func (s *ActionsService) setSelectedReposForVariable(ctx context.Context, url string, ids SelectedRepoIDs) (*Response, error) { + type repoIDs struct { + SelectedIDs SelectedRepoIDs `json:"selected_repository_ids"` + } + + req, err := s.client.NewRequest("PUT", url, repoIDs{SelectedIDs: ids}) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// SetSelectedReposForOrgVariable sets the repositories that have access to a variable. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/variables#set-selected-repositories-for-an-organization-variable +func (s *ActionsService) SetSelectedReposForOrgVariable(ctx context.Context, org, name string, ids SelectedRepoIDs) (*Response, error) { + url := fmt.Sprintf("orgs/%v/actions/variables/%v/repositories", org, name) + return s.setSelectedReposForVariable(ctx, url, ids) +} + +func (s *ActionsService) addSelectedRepoToVariable(ctx context.Context, url string) (*Response, error) { + req, err := s.client.NewRequest("PUT", url, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// AddSelectedRepoToOrgVariable adds a repository to an organization variable. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/variables#add-selected-repository-to-an-organization-variable +func (s *ActionsService) AddSelectedRepoToOrgVariable(ctx context.Context, org, name string, repo *Repository) (*Response, error) { + url := fmt.Sprintf("orgs/%v/actions/variables/%v/repositories/%v", org, name, *repo.ID) + return s.addSelectedRepoToVariable(ctx, url) +} + +func (s *ActionsService) removeSelectedRepoFromVariable(ctx context.Context, url string) (*Response, error) { + req, err := s.client.NewRequest("DELETE", url, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// RemoveSelectedRepoFromOrgVariable removes a repository from an organization variable. +// +// GitHub API docs: https://docs.github.com/en/rest/actions/variables#remove-selected-repository-from-an-organization-variable +func (s *ActionsService) RemoveSelectedRepoFromOrgVariable(ctx context.Context, org, name string, repo *Repository) (*Response, error) { + url := fmt.Sprintf("orgs/%v/actions/variables/%v/repositories/%v", org, name, *repo.ID) + return s.removeSelectedRepoFromVariable(ctx, url) +} diff --git a/github/actions_variables_test.go b/github/actions_variables_test.go new file mode 100644 index 0000000000..646da92b1e --- /dev/null +++ b/github/actions_variables_test.go @@ -0,0 +1,659 @@ +// Copyright 2023 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. + +package github + +import ( + "context" + "fmt" + "net/http" + "testing" + "time" + + "github.com/google/go-cmp/cmp" +) + +func TestActionsService_ListRepoVariables(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/actions/variables", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":4,"variables":[{"name":"A","value":"AA","created_at":"2019-01-02T15:04:05Z","updated_at":"2020-01-02T15:04:05Z"},{"name":"B","value":"BB","created_at":"2019-01-02T15:04:05Z","updated_at":"2020-01-02T15:04:05Z"}]}`) + }) + + opts := &ListOptions{Page: 2, PerPage: 2} + ctx := context.Background() + variables, _, err := client.Actions.ListRepoVariables(ctx, "o", "r", opts) + if err != nil { + t.Errorf("Actions.ListRepoVariables returned error: %v", err) + } + + want := &ActionsVariables{ + TotalCount: 4, + Variables: []*ActionsVariable{ + {Name: "A", Value: "AA", CreatedAt: &Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}, UpdatedAt: &Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)}}, + {Name: "B", Value: "BB", CreatedAt: &Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}, UpdatedAt: &Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)}}, + }, + } + if !cmp.Equal(variables, want) { + t.Errorf("Actions.ListRepoVariables returned %+v, want %+v", variables, want) + } + + const methodName = "ListRepoVariables" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListRepoVariables(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListRepoVariables(ctx, "o", "r", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetRepoVariable(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/actions/variables/NAME", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"name":"NAME","value":"VALUE","created_at":"2019-01-02T15:04:05Z","updated_at":"2020-01-02T15:04:05Z"}`) + }) + + ctx := context.Background() + variable, _, err := client.Actions.GetRepoVariable(ctx, "o", "r", "NAME") + if err != nil { + t.Errorf("Actions.GetRepoVariable returned error: %v", err) + } + + want := &ActionsVariable{ + Name: "NAME", + Value: "VALUE", + CreatedAt: &Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}, + UpdatedAt: &Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)}, + } + if !cmp.Equal(variable, want) { + t.Errorf("Actions.GetRepoVariable returned %+v, want %+v", variable, want) + } + + const methodName = "GetRepoVariable" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetRepoVariable(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetRepoVariable(ctx, "o", "r", "NAME") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_CreateRepoVariable(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/actions/variables", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Content-Type", "application/json") + testBody(t, r, `{"name":"NAME","value":"VALUE"}`+"\n") + w.WriteHeader(http.StatusCreated) + }) + + input := &ActionsVariable{ + Name: "NAME", + Value: "VALUE", + } + ctx := context.Background() + _, err := client.Actions.CreateRepoVariable(ctx, "o", "r", input) + if err != nil { + t.Errorf("Actions.CreateRepoVariable returned error: %v", err) + } + + const methodName = "CreateRepoVariable" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.CreateRepoVariable(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.CreateRepoVariable(ctx, "o", "r", input) + }) +} + +func TestActionsService_UpdateRepoVariable(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/actions/variables/NAME", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testHeader(t, r, "Content-Type", "application/json") + testBody(t, r, `{"name":"NAME","value":"VALUE"}`+"\n") + w.WriteHeader(http.StatusNoContent) + }) + + input := &ActionsVariable{ + Name: "NAME", + Value: "VALUE", + } + ctx := context.Background() + _, err := client.Actions.UpdateRepoVariable(ctx, "o", "r", input) + if err != nil { + t.Errorf("Actions.UpdateRepoVariable returned error: %v", err) + } + + const methodName = "UpdateRepoVariable" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.UpdateRepoVariable(ctx, "\n", "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.UpdateRepoVariable(ctx, "o", "r", input) + }) +} + +func TestActionsService_DeleteRepoVariable(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/actions/variables/NAME", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := context.Background() + _, err := client.Actions.DeleteRepoVariable(ctx, "o", "r", "NAME") + if err != nil { + t.Errorf("Actions.( returned error: %v", err) + } + + const methodName = "DeleteRepoVariable" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.DeleteRepoVariable(ctx, "\n", "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.DeleteRepoVariable(ctx, "o", "r", "NAME") + }) +} + +func TestActionsService_ListOrgVariables(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/actions/variables", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":3,"variables":[{"name":"A","value":"AA","created_at":"2019-08-10T14:59:22Z","updated_at":"2020-01-10T14:59:22Z","visibility":"private"},{"name":"B","value":"BB","created_at":"2019-08-10T14:59:22Z","updated_at":"2020-01-10T14:59:22Z","visibility":"all"},{"name":"C","value":"CC","created_at":"2019-08-10T14:59:22Z","updated_at":"2020-01-10T14:59:22Z","visibility":"selected","selected_repositories_url":"https://api.github.com/orgs/octo-org/actions/variables/VAR/repositories"}]}`) + }) + + opts := &ListOptions{Page: 2, PerPage: 2} + ctx := context.Background() + variables, _, err := client.Actions.ListOrgVariables(ctx, "o", opts) + if err != nil { + t.Errorf("Actions.ListOrgVariables returned error: %v", err) + } + + want := &ActionsVariables{ + TotalCount: 3, + Variables: []*ActionsVariable{ + {Name: "A", Value: "AA", CreatedAt: &Timestamp{time.Date(2019, time.August, 10, 14, 59, 22, 0, time.UTC)}, UpdatedAt: &Timestamp{time.Date(2020, time.January, 10, 14, 59, 22, 0, time.UTC)}, Visibility: String("private")}, + {Name: "B", Value: "BB", CreatedAt: &Timestamp{time.Date(2019, time.August, 10, 14, 59, 22, 0, time.UTC)}, UpdatedAt: &Timestamp{time.Date(2020, time.January, 10, 14, 59, 22, 0, time.UTC)}, Visibility: String("all")}, + {Name: "C", Value: "CC", CreatedAt: &Timestamp{time.Date(2019, time.August, 10, 14, 59, 22, 0, time.UTC)}, UpdatedAt: &Timestamp{time.Date(2020, time.January, 10, 14, 59, 22, 0, time.UTC)}, Visibility: String("selected"), SelectedRepositoriesURL: String("https://api.github.com/orgs/octo-org/actions/variables/VAR/repositories")}, + }, + } + if !cmp.Equal(variables, want) { + t.Errorf("Actions.ListOrgVariables returned %+v, want %+v", variables, want) + } + + const methodName = "ListOrgVariables" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListOrgVariables(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListOrgVariables(ctx, "o", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetOrgVariable(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/actions/variables/NAME", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"name":"NAME","value":"VALUE","created_at":"2019-01-02T15:04:05Z","updated_at":"2020-01-02T15:04:05Z","visibility":"selected","selected_repositories_url":"https://api.github.com/orgs/octo-org/actions/variables/VAR/repositories"}`) + }) + + ctx := context.Background() + variable, _, err := client.Actions.GetOrgVariable(ctx, "o", "NAME") + if err != nil { + t.Errorf("Actions.GetOrgVariable returned error: %v", err) + } + + want := &ActionsVariable{ + Name: "NAME", + Value: "VALUE", + CreatedAt: &Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}, + UpdatedAt: &Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)}, + Visibility: String("selected"), + SelectedRepositoriesURL: String("https://api.github.com/orgs/octo-org/actions/variables/VAR/repositories"), + } + if !cmp.Equal(variable, want) { + t.Errorf("Actions.GetOrgVariable returned %+v, want %+v", variable, want) + } + + const methodName = "GetOrgVariable" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetOrgVariable(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetOrgVariable(ctx, "o", "NAME") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_CreateOrgVariable(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/actions/variables", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Content-Type", "application/json") + testBody(t, r, `{"name":"NAME","value":"VALUE","visibility":"selected","selected_repository_ids":[1296269,1269280]}`+"\n") + w.WriteHeader(http.StatusCreated) + }) + + input := &ActionsVariable{ + Name: "NAME", + Value: "VALUE", + Visibility: String("selected"), + SelectedRepositoryIDs: &SelectedRepoIDs{1296269, 1269280}, + } + ctx := context.Background() + _, err := client.Actions.CreateOrgVariable(ctx, "o", input) + if err != nil { + t.Errorf("Actions.CreateOrgVariable returned error: %v", err) + } + + const methodName = "CreateOrgVariable" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.CreateOrgVariable(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.CreateOrgVariable(ctx, "o", input) + }) +} + +func TestActionsService_UpdateOrgVariable(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/actions/variables/NAME", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testHeader(t, r, "Content-Type", "application/json") + testBody(t, r, `{"name":"NAME","value":"VALUE","visibility":"selected","selected_repository_ids":[1296269,1269280]}`+"\n") + w.WriteHeader(http.StatusNoContent) + }) + + input := &ActionsVariable{ + Name: "NAME", + Value: "VALUE", + Visibility: String("selected"), + SelectedRepositoryIDs: &SelectedRepoIDs{1296269, 1269280}, + } + ctx := context.Background() + _, err := client.Actions.UpdateOrgVariable(ctx, "o", input) + if err != nil { + t.Errorf("Actions.UpdateOrgVariable returned error: %v", err) + } + + const methodName = "UpdateOrgVariable" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.UpdateOrgVariable(ctx, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.UpdateOrgVariable(ctx, "o", input) + }) +} + +func TestActionsService_ListSelectedReposForOrgVariable(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/actions/variables/NAME/repositories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprintf(w, `{"total_count":1,"repositories":[{"id":1}]}`) + }) + + opts := &ListOptions{Page: 2, PerPage: 2} + ctx := context.Background() + repos, _, err := client.Actions.ListSelectedReposForOrgVariable(ctx, "o", "NAME", opts) + if err != nil { + t.Errorf("Actions.( returned error: %v", err) + } + + want := &SelectedReposList{ + TotalCount: Int(1), + Repositories: []*Repository{ + {ID: Int64(1)}, + }, + } + if !cmp.Equal(repos, want) { + t.Errorf("Actions.( returned %+v, want %+v", repos, want) + } + + const methodName = "ListSelectedReposForOrgVariable" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListSelectedReposForOrgVariable(ctx, "\n", "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListSelectedReposForOrgVariable(ctx, "o", "NAME", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_SetSelectedReposForOrgSVariable(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/actions/variables/NAME/repositories", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Content-Type", "application/json") + testBody(t, r, `{"selected_repository_ids":[64780797]}`+"\n") + }) + + ctx := context.Background() + _, err := client.Actions.SetSelectedReposForOrgVariable(ctx, "o", "NAME", SelectedRepoIDs{64780797}) + if err != nil { + t.Errorf("Actions.( returned error: %v", err) + } + + const methodName = "SetSelectedReposForOrgVariable" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.SetSelectedReposForOrgVariable(ctx, "\n", "\n", SelectedRepoIDs{64780797}) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.SetSelectedReposForOrgVariable(ctx, "o", "NAME", SelectedRepoIDs{64780797}) + }) +} + +func TestActionsService_AddSelectedRepoToOrgVariable(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/actions/variables/NAME/repositories/1234", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + }) + + repo := &Repository{ID: Int64(1234)} + ctx := context.Background() + _, err := client.Actions.AddSelectedRepoToOrgVariable(ctx, "o", "NAME", repo) + if err != nil { + t.Errorf("Actions.AddSelectedRepoToOrgVariable returned error: %v", err) + } + + const methodName = "AddSelectedRepoToOrgVariable" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.AddSelectedRepoToOrgVariable(ctx, "\n", "\n", repo) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.AddSelectedRepoToOrgVariable(ctx, "o", "NAME", repo) + }) +} + +func TestActionsService_RemoveSelectedRepoFromOrgVariable(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/actions/variables/NAME/repositories/1234", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + repo := &Repository{ID: Int64(1234)} + ctx := context.Background() + _, err := client.Actions.RemoveSelectedRepoFromOrgVariable(ctx, "o", "NAME", repo) + if err != nil { + t.Errorf("Actions.RemoveSelectedRepoFromOrgVariable returned error: %v", err) + } + + const methodName = "RemoveSelectedRepoFromOrgVariable" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.RemoveSelectedRepoFromOrgVariable(ctx, "\n", "\n", repo) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.RemoveSelectedRepoFromOrgVariable(ctx, "o", "NAME", repo) + }) +} + +func TestActionsService_DeleteOrgVariable(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/actions/variables/NAME", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := context.Background() + _, err := client.Actions.DeleteOrgVariable(ctx, "o", "NAME") + if err != nil { + t.Errorf("Actions.DeleteOrgVariable returned error: %v", err) + } + + const methodName = "DeleteOrgVariable" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.DeleteOrgVariable(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.DeleteOrgVariable(ctx, "o", "NAME") + }) +} + +func TestActionsService_ListEnvVariables(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repositories/1/environments/e/variables", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"per_page": "2", "page": "2"}) + fmt.Fprint(w, `{"total_count":4,"variables":[{"name":"A","value":"AA","created_at":"2019-01-02T15:04:05Z","updated_at":"2020-01-02T15:04:05Z"},{"name":"B","value":"BB","created_at":"2019-01-02T15:04:05Z","updated_at":"2020-01-02T15:04:05Z"}]}`) + }) + + opts := &ListOptions{Page: 2, PerPage: 2} + ctx := context.Background() + variables, _, err := client.Actions.ListEnvVariables(ctx, 1, "e", opts) + if err != nil { + t.Errorf("Actions.ListEnvVariables returned error: %v", err) + } + + want := &ActionsVariables{ + TotalCount: 4, + Variables: []*ActionsVariable{ + {Name: "A", Value: "AA", CreatedAt: &Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}, UpdatedAt: &Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)}}, + {Name: "B", Value: "BB", CreatedAt: &Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}, UpdatedAt: &Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)}}, + }, + } + if !cmp.Equal(variables, want) { + t.Errorf("Actions.ListEnvVariables returned %+v, want %+v", variables, want) + } + + const methodName = "ListEnvVariables" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.ListEnvVariables(ctx, 0.0, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.ListEnvVariables(ctx, 1, "e", opts) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_GetEnvVariable(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repositories/1/environments/e/variables/variable", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{"name":"variable","value":"VAR","created_at":"2019-01-02T15:04:05Z","updated_at":"2020-01-02T15:04:05Z"}`) + }) + + ctx := context.Background() + variable, _, err := client.Actions.GetEnvVariable(ctx, 1, "e", "variable") + if err != nil { + t.Errorf("Actions.GetEnvVariable returned error: %v", err) + } + + want := &ActionsVariable{ + Name: "variable", + Value: "VAR", + CreatedAt: &Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}, + UpdatedAt: &Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)}, + } + if !cmp.Equal(variable, want) { + t.Errorf("Actions.GetEnvVariable returned %+v, want %+v", variable, want) + } + + const methodName = "GetEnvVariable" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Actions.GetEnvVariable(ctx, 0.0, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Actions.GetEnvVariable(ctx, 1, "e", "variable") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestActionsService_CreateEnvVariable(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repositories/1/environments/e/variables", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + testHeader(t, r, "Content-Type", "application/json") + testBody(t, r, `{"name":"variable","value":"VAR"}`+"\n") + w.WriteHeader(http.StatusCreated) + }) + + input := &ActionsVariable{ + Name: "variable", + Value: "VAR", + } + ctx := context.Background() + _, err := client.Actions.CreateEnvVariable(ctx, 1, "e", input) + if err != nil { + t.Errorf("Actions.CreateEnvVariable returned error: %v", err) + } + + const methodName = "CreateEnvVariable" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.CreateEnvVariable(ctx, 0.0, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.CreateEnvVariable(ctx, 1, "e", input) + }) +} + +func TestActionsService_UpdateEnvVariable(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repositories/1/environments/e/variables/variable", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PATCH") + testHeader(t, r, "Content-Type", "application/json") + testBody(t, r, `{"name":"variable","value":"VAR"}`+"\n") + w.WriteHeader(http.StatusNoContent) + }) + + input := &ActionsVariable{ + Name: "variable", + Value: "VAR", + } + ctx := context.Background() + _, err := client.Actions.UpdateEnvVariable(ctx, 1, "e", input) + if err != nil { + t.Errorf("Actions.UpdateEnvVariable returned error: %v", err) + } + + const methodName = "UpdateEnvVariable" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.UpdateEnvVariable(ctx, 0.0, "\n", input) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.UpdateEnvVariable(ctx, 1, "e", input) + }) +} + +func TestActionsService_DeleteEnvVariable(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repositories/1/environments/e/variables/variable", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := context.Background() + _, err := client.Actions.DeleteEnvVariable(ctx, 1, "e", "variable") + if err != nil { + t.Errorf("Actions.DeleteEnvVariable returned error: %v", err) + } + + const methodName = "DeleteEnvVariable" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Actions.DeleteEnvVariable(ctx, 0.0, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Actions.DeleteEnvVariable(ctx, 1, "r", "variable") + }) +} diff --git a/github/github-accessors.go b/github/github-accessors.go index e2d0d7f835..3110d8132d 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -174,6 +174,46 @@ func (a *ActionsPermissionsRepository) GetSelectedActionsURL() string { return *a.SelectedActionsURL } +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (a *ActionsVariable) GetCreatedAt() Timestamp { + if a == nil || a.CreatedAt == nil { + return Timestamp{} + } + return *a.CreatedAt +} + +// GetSelectedRepositoriesURL returns the SelectedRepositoriesURL field if it's non-nil, zero value otherwise. +func (a *ActionsVariable) GetSelectedRepositoriesURL() string { + if a == nil || a.SelectedRepositoriesURL == nil { + return "" + } + return *a.SelectedRepositoriesURL +} + +// GetSelectedRepositoryIDs returns the SelectedRepositoryIDs field. +func (a *ActionsVariable) GetSelectedRepositoryIDs() *SelectedRepoIDs { + if a == nil { + return nil + } + return a.SelectedRepositoryIDs +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (a *ActionsVariable) GetUpdatedAt() Timestamp { + if a == nil || a.UpdatedAt == nil { + return Timestamp{} + } + return *a.UpdatedAt +} + +// GetVisibility returns the Visibility field if it's non-nil, zero value otherwise. +func (a *ActionsVariable) GetVisibility() string { + if a == nil || a.Visibility == nil { + return "" + } + return *a.Visibility +} + // GetFrom returns the From field if it's non-nil, zero value otherwise. func (a *AdminEnforcedChanges) GetFrom() bool { if a == nil || a.From == nil { diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index 24e0a75826..9457df769e 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -215,6 +215,53 @@ func TestActionsPermissionsRepository_GetSelectedActionsURL(tt *testing.T) { a.GetSelectedActionsURL() } +func TestActionsVariable_GetCreatedAt(tt *testing.T) { + var zeroValue Timestamp + a := &ActionsVariable{CreatedAt: &zeroValue} + a.GetCreatedAt() + a = &ActionsVariable{} + a.GetCreatedAt() + a = nil + a.GetCreatedAt() +} + +func TestActionsVariable_GetSelectedRepositoriesURL(tt *testing.T) { + var zeroValue string + a := &ActionsVariable{SelectedRepositoriesURL: &zeroValue} + a.GetSelectedRepositoriesURL() + a = &ActionsVariable{} + a.GetSelectedRepositoriesURL() + a = nil + a.GetSelectedRepositoriesURL() +} + +func TestActionsVariable_GetSelectedRepositoryIDs(tt *testing.T) { + a := &ActionsVariable{} + a.GetSelectedRepositoryIDs() + a = nil + a.GetSelectedRepositoryIDs() +} + +func TestActionsVariable_GetUpdatedAt(tt *testing.T) { + var zeroValue Timestamp + a := &ActionsVariable{UpdatedAt: &zeroValue} + a.GetUpdatedAt() + a = &ActionsVariable{} + a.GetUpdatedAt() + a = nil + a.GetUpdatedAt() +} + +func TestActionsVariable_GetVisibility(tt *testing.T) { + var zeroValue string + a := &ActionsVariable{Visibility: &zeroValue} + a.GetVisibility() + a = &ActionsVariable{} + a.GetVisibility() + a = nil + a.GetVisibility() +} + func TestAdminEnforcedChanges_GetFrom(tt *testing.T) { var zeroValue bool a := &AdminEnforcedChanges{From: &zeroValue} From ef6c7a6dc8a3c9820b4702355f7e2a05e7abfaca Mon Sep 17 00:00:00 2001 From: Glenn Lewis <6598971+gmlewis@users.noreply.github.com> Date: Thu, 2 Feb 2023 06:57:20 -0500 Subject: [PATCH 3/5] Remove failing unit tests for Go 1.20 (#2656) Fixes: #2654. --- github/actions_artifacts_test.go | 17 ----------------- github/repos_contents_test.go | 17 ----------------- 2 files changed, 34 deletions(-) diff --git a/github/actions_artifacts_test.go b/github/actions_artifacts_test.go index 345a087534..e35a873bc0 100644 --- a/github/actions_artifacts_test.go +++ b/github/actions_artifacts_test.go @@ -368,23 +368,6 @@ func TestActionsService_DownloadArtifact_StatusMovedPermanently_followRedirects( } } -func TestActionsService_DownloadArtifact_invalidLocationHeader(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - mux.HandleFunc("/repos/o/r/actions/artifacts/1/zip", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - ctlChar := 0x7f - badURL := "https://google.com" + string(byte(ctlChar)) - w.Header().Add("Location", badURL) - w.WriteHeader(http.StatusFound) - }) - - ctx := context.Background() - _, _, err := client.Actions.DownloadArtifact(ctx, "o", "r", 1, false) - testURLParseError(t, err) -} - func TestActionsService_DeleteArtifact(t *testing.T) { client, mux, _, teardown := setup() defer teardown() diff --git a/github/repos_contents_test.go b/github/repos_contents_test.go index db3efb91c6..6616e5af0c 100644 --- a/github/repos_contents_test.go +++ b/github/repos_contents_test.go @@ -744,23 +744,6 @@ func TestRepositoriesService_GetArchiveLink_StatusMovedPermanently_followRedirec } } -func TestRepositoriesService_GetArchiveLink_invalidLocationHeader(t *testing.T) { - client, mux, _, teardown := setup() - defer teardown() - - mux.HandleFunc("/repos/o/r/tarball", func(w http.ResponseWriter, r *http.Request) { - testMethod(t, r, "GET") - ctlChar := 0x7f - badURL := "https://google.com" + string(byte(ctlChar)) - w.Header().Add("Location", badURL) - w.WriteHeader(http.StatusFound) - }) - - ctx := context.Background() - _, _, err := client.Repositories.GetArchiveLink(ctx, "o", "r", Tarball, &RepositoryContentGetOptions{}, false) - testURLParseError(t, err) -} - func TestRepositoriesService_GetContents_NoTrailingSlashInDirectoryApiPath(t *testing.T) { client, mux, _, teardown := setup() defer teardown() From b2fef5dfd6376b7c99d8dc88cecf7098e5bf4427 Mon Sep 17 00:00:00 2001 From: Sairaviteja27 <44663543+Sairaviteja27@users.noreply.github.com> Date: Thu, 2 Feb 2023 17:33:05 +0530 Subject: [PATCH 4/5] Add permission filter for list collaborators (#2653) Fixes #2651. --- github/repos_collaborators.go | 7 ++++++ github/repos_collaborators_test.go | 40 ++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/github/repos_collaborators.go b/github/repos_collaborators.go index abc4161c3b..c2396872f2 100644 --- a/github/repos_collaborators.go +++ b/github/repos_collaborators.go @@ -23,6 +23,13 @@ type ListCollaboratorsOptions struct { // Default value is "all". Affiliation string `url:"affiliation,omitempty"` + // Permission specifies how collaborators should be filtered by the permissions they have on the repository. + // Possible values are: + // "pull", "triage", "push", "maintain", "admin" + // + // If not specified, all collaborators will be returned. + Permission string `url:"permission,omitempty"` + ListOptions } diff --git a/github/repos_collaborators_test.go b/github/repos_collaborators_test.go index 8bb6c15cf1..8953d36bcc 100644 --- a/github/repos_collaborators_test.go +++ b/github/repos_collaborators_test.go @@ -94,6 +94,46 @@ func TestRepositoriesService_ListCollaborators_withAffiliation(t *testing.T) { }) } +func TestRepositoriesService_ListCollaborators_withPermission(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/collaborators", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"permission": "pull", "page": "2"}) + fmt.Fprintf(w, `[{"id":1}, {"id":2}]`) + }) + + opt := &ListCollaboratorsOptions{ + ListOptions: ListOptions{Page: 2}, + Permission: "pull", + } + ctx := context.Background() + users, _, err := client.Repositories.ListCollaborators(ctx, "o", "r", opt) + if err != nil { + t.Errorf("Repositories.ListCollaborators returned error: %v", err) + } + + want := []*User{{ID: Int64(1)}, {ID: Int64(2)}} + if !cmp.Equal(users, want) { + t.Errorf("Repositories.ListCollaborators returned %+v, want %+v", users, want) + } + + const methodName = "ListCollaborators" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListCollaborators(ctx, "\n", "\n", opt) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListCollaborators(ctx, "o", "r", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + func TestRepositoriesService_ListCollaborators_invalidOwner(t *testing.T) { client, _, _, teardown := setup() defer teardown() From 2094f991592201d301feec818d413c136537732c Mon Sep 17 00:00:00 2001 From: Glenn Lewis <6598971+gmlewis@users.noreply.github.com> Date: Thu, 2 Feb 2023 07:18:53 -0500 Subject: [PATCH 5/5] Update workflow to use Go 1.20 and 1.19 (#2657) --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d4a197746b..c35dbc1b43 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -21,7 +21,7 @@ jobs: test: strategy: matrix: - go-version: [1.x, 1.18.x] + go-version: [1.x, 1.19.x] platform: [ubuntu-latest] include: # include windows, but only with the latest Go version, since there