diff --git a/github/github-accessors.go b/github/github-accessors.go index 073a3b9cea9..6e118bdec14 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -3612,6 +3612,14 @@ func (i *Invitation) GetTeamCount() int { return *i.TeamCount } +// GetActiveLockReason returns the ActiveLockReason field if it's non-nil, zero value otherwise. +func (i *Issue) GetActiveLockReason() string { + if i == nil || i.ActiveLockReason == nil { + return "" + } + return *i.ActiveLockReason +} + // GetAssignee returns the Assignee field. func (i *Issue) GetAssignee() *User { if i == nil { @@ -4012,6 +4020,14 @@ func (i *IssueEvent) GetLabel() *Label { return i.Label } +// GetLockReason returns the LockReason field if it's non-nil, zero value otherwise. +func (i *IssueEvent) GetLockReason() string { + if i == nil || i.LockReason == nil { + return "" + } + return *i.LockReason +} + // GetMilestone returns the Milestone field. func (i *IssueEvent) GetMilestone() *Milestone { if i == nil { @@ -6636,6 +6652,14 @@ func (p *PublicEvent) GetSender() *User { return p.Sender } +// GetActiveLockReason returns the ActiveLockReason field if it's non-nil, zero value otherwise. +func (p *PullRequest) GetActiveLockReason() string { + if p == nil || p.ActiveLockReason == nil { + return "" + } + return *p.ActiveLockReason +} + // GetAdditions returns the Additions field if it's non-nil, zero value otherwise. func (p *PullRequest) GetAdditions() int { if p == nil || p.Additions == nil { diff --git a/github/github.go b/github/github.go index 99b928c28c1..1244e3ec1ab 100644 --- a/github/github.go +++ b/github/github.go @@ -111,6 +111,9 @@ const ( // https://developer.github.com/changes/2018-02-07-team-discussions-api/ mediaTypeTeamDiscussionsPreview = "application/vnd.github.echo-preview+json" + // https://developer.github.com/changes/2018-01-10-lock-reason-api-preview/ + mediaTypeLockReasonPreview = "application/vnd.github.sailor-v-preview+json" + // https://developer.github.com/changes/2018-05-07-new-checks-api-public-beta/ mediaTypeCheckRunsPreview = "application/vnd.github.antiope-preview+json" diff --git a/github/issues.go b/github/issues.go index 07d6ec143e6..47537544a42 100644 --- a/github/issues.go +++ b/github/issues.go @@ -56,6 +56,10 @@ type Issue struct { // TextMatches is only populated from search results that request text matches // See: search.go and https://developer.github.com/v3/search/#text-match-metadata TextMatches []TextMatch `json:"text_matches,omitempty"` + + // ActiveLockReason is populated only when LockReason is provided while locking the issue. + // Possible values are: "off-topic", "too heated", "resolved", and "spam". + ActiveLockReason *string `json:"active_lock_reason,omitempty"` } func (i Issue) String() string { @@ -156,7 +160,7 @@ func (s *IssuesService) listIssues(ctx context.Context, u string, opt *IssueList } // TODO: remove custom Accept headers when APIs fully launch. - acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview} + acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview} req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) var issues []*Issue @@ -224,7 +228,7 @@ func (s *IssuesService) ListByRepo(ctx context.Context, owner string, repo strin } // TODO: remove custom Accept headers when APIs fully launch. - acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview} + acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview} req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) var issues []*Issue @@ -247,7 +251,7 @@ func (s *IssuesService) Get(ctx context.Context, owner string, repo string, numb } // TODO: remove custom Accept headers when APIs fully launch. - acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview} + acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview} req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) issue := new(Issue) @@ -303,16 +307,29 @@ func (s *IssuesService) Edit(ctx context.Context, owner string, repo string, num return i, resp, nil } +// LockIssueOptions specifies the optional parameters to the +// IssuesService.Lock method. +type LockIssueOptions struct { + // LockReason specifies the reason to lock this issue. + // Providing a lock reason can help make it clearer to contributors why an issue + // was locked. Possible values are: "off-topic", "too heated", "resolved", and "spam". + LockReason string `json:"lock_reason,omitempty"` +} + // Lock an issue's conversation. // // GitHub API docs: https://developer.github.com/v3/issues/#lock-an-issue -func (s *IssuesService) Lock(ctx context.Context, owner string, repo string, number int) (*Response, error) { +func (s *IssuesService) Lock(ctx context.Context, owner string, repo string, number int, opt *LockIssueOptions) (*Response, error) { u := fmt.Sprintf("repos/%v/%v/issues/%d/lock", owner, repo, number) - req, err := s.client.NewRequest("PUT", u, nil) + req, err := s.client.NewRequest("PUT", u, opt) if err != nil { return nil, err } + if opt != nil { + req.Header.Set("Accept", mediaTypeLockReasonPreview) + } + return s.client.Do(ctx, req, nil) } diff --git a/github/issues_events.go b/github/issues_events.go index 55e6d431b30..f71e46361c7 100644 --- a/github/issues_events.go +++ b/github/issues_events.go @@ -34,9 +34,13 @@ type IssueEvent struct { // The Actor committed to master a commit mentioning the issue in its commit message. // CommitID holds the SHA1 of the commit. // - // reopened, locked, unlocked + // reopened, unlocked // The Actor did that to the issue. // + // locked + // The Actor locked the issue. + // LockReason holds the reason of locking the issue (if provided while locking). + // // renamed // The Actor changed the issue title from Rename.From to Rename.To. // @@ -64,12 +68,13 @@ type IssueEvent struct { Issue *Issue `json:"issue,omitempty"` // Only present on certain events; see above. - Assignee *User `json:"assignee,omitempty"` - Assigner *User `json:"assigner,omitempty"` - CommitID *string `json:"commit_id,omitempty"` - Milestone *Milestone `json:"milestone,omitempty"` - Label *Label `json:"label,omitempty"` - Rename *Rename `json:"rename,omitempty"` + Assignee *User `json:"assignee,omitempty"` + Assigner *User `json:"assigner,omitempty"` + CommitID *string `json:"commit_id,omitempty"` + Milestone *Milestone `json:"milestone,omitempty"` + Label *Label `json:"label,omitempty"` + Rename *Rename `json:"rename,omitempty"` + LockReason *string `json:"lock_reason,omitempty"` } // ListIssueEvents lists events for the specified issue. @@ -87,6 +92,8 @@ func (s *IssuesService) ListIssueEvents(ctx context.Context, owner, repo string, return nil, nil, err } + req.Header.Set("Accept", mediaTypeLockReasonPreview) + var events []*IssueEvent resp, err := s.client.Do(ctx, req, &events) if err != nil { diff --git a/github/issues_test.go b/github/issues_test.go index e42f9750b14..d6fe9f3f919 100644 --- a/github/issues_test.go +++ b/github/issues_test.go @@ -20,7 +20,7 @@ func TestIssuesService_List_all(t *testing.T) { client, mux, _, teardown := setup() defer teardown() - acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview} + acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview} mux.HandleFunc("/issues", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", ")) @@ -57,7 +57,7 @@ func TestIssuesService_List_owned(t *testing.T) { client, mux, _, teardown := setup() defer teardown() - acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview} + acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview} mux.HandleFunc("/user/issues", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", ")) @@ -79,7 +79,7 @@ func TestIssuesService_ListByOrg(t *testing.T) { client, mux, _, teardown := setup() defer teardown() - acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview} + acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview} mux.HandleFunc("/orgs/o/issues", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", ")) @@ -109,7 +109,7 @@ func TestIssuesService_ListByRepo(t *testing.T) { client, mux, _, teardown := setup() defer teardown() - acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview} + acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview} mux.HandleFunc("/repos/o/r/issues", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", ")) @@ -155,7 +155,7 @@ func TestIssuesService_Get(t *testing.T) { client, mux, _, teardown := setup() defer teardown() - acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview} + acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview} mux.HandleFunc("/repos/o/r/issues/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", ")) @@ -279,7 +279,24 @@ func TestIssuesService_Lock(t *testing.T) { w.WriteHeader(http.StatusNoContent) }) - if _, err := client.Issues.Lock(context.Background(), "o", "r", 1); err != nil { + if _, err := client.Issues.Lock(context.Background(), "o", "r", 1, nil); err != nil { + t.Errorf("Issues.Lock returned error: %v", err) + } +} + +func TestIssuesService_LockWithReason(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/issues/1/lock", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + testHeader(t, r, "Accept", mediaTypeLockReasonPreview) + w.WriteHeader(http.StatusNoContent) + }) + + opt := &LockIssueOptions{LockReason: "off-topic"} + + if _, err := client.Issues.Lock(context.Background(), "o", "r", 1, opt); err != nil { t.Errorf("Issues.Lock returned error: %v", err) } } diff --git a/github/pulls.go b/github/pulls.go index 78252f596af..a123ec56863 100644 --- a/github/pulls.go +++ b/github/pulls.go @@ -9,6 +9,7 @@ import ( "bytes" "context" "fmt" + "strings" "time" ) @@ -61,6 +62,10 @@ type PullRequest struct { Head *PullRequestBranch `json:"head,omitempty"` Base *PullRequestBranch `json:"base,omitempty"` + + // ActiveLockReason is populated only when LockReason is provided while locking the pull request. + // Possible values are: "off-topic", "too heated", "resolved", and "spam". + ActiveLockReason *string `json:"active_lock_reason,omitempty"` } func (p PullRequest) String() string { @@ -118,7 +123,8 @@ func (s *PullRequestsService) List(ctx context.Context, owner string, repo strin } // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview) + acceptHeaders := []string{mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview} + req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) var pulls []*PullRequest resp, err := s.client.Do(ctx, req, &pulls) @@ -140,7 +146,8 @@ func (s *PullRequestsService) Get(ctx context.Context, owner string, repo string } // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview) + acceptHeaders := []string{mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview} + req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) pull := new(PullRequest) resp, err := s.client.Do(ctx, req, pull) @@ -247,7 +254,8 @@ func (s *PullRequestsService) Edit(ctx context.Context, owner string, repo strin } // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview) + acceptHeaders := []string{mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview} + req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) p := new(PullRequest) resp, err := s.client.Do(ctx, req, p) diff --git a/github/pulls_test.go b/github/pulls_test.go index c09d115ba31..3b2449fe0f5 100644 --- a/github/pulls_test.go +++ b/github/pulls_test.go @@ -20,9 +20,10 @@ func TestPullRequestsService_List(t *testing.T) { client, mux, _, teardown := setup() defer teardown() + acceptHeaders := []string{mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview} mux.HandleFunc("/repos/o/r/pulls", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeLabelDescriptionSearchPreview) + testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", ")) testFormValues(t, r, values{ "state": "closed", "head": "h", @@ -58,9 +59,10 @@ func TestPullRequestsService_Get(t *testing.T) { client, mux, _, teardown := setup() defer teardown() + acceptHeaders := []string{mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview} mux.HandleFunc("/repos/o/r/pulls/1", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeLabelDescriptionSearchPreview) + testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", ")) fmt.Fprint(w, `{"number":1}`) }) @@ -278,9 +280,10 @@ func TestPullRequestsService_Edit(t *testing.T) { for i, tt := range tests { madeRequest := false + acceptHeaders := []string{mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview} mux.HandleFunc(fmt.Sprintf("/repos/o/r/pulls/%v", i), func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PATCH") - testHeader(t, r, "Accept", mediaTypeLabelDescriptionSearchPreview) + testHeader(t, r, "Accept", strings.Join(acceptHeaders, ", ")) testBody(t, r, tt.wantUpdate+"\n") io.WriteString(w, tt.sendResponse) madeRequest = true