This includes most API methods on pull requests (with the exception of listing commits and the merge functions), as well as all of the methods for pull request comments. (Fixes #13) This also fixes a few oversights in the issues API.
| @ -0,0 +1,238 @@ | |||
| // Copyright 2013 Google. All rights reserved. | |||
| // | |||
| // Use of this source code is governed by a BSD-style | |||
| // license that can be found in the LICENSE file or at | |||
| // https://developers.google.com/open-source/licenses/bsd | |||
| package github | |||
| import ( | |||
| "fmt" | |||
| "net/url" | |||
| "time" | |||
| ) | |||
| // PullRequestsService handles communication with the pull request related | |||
| // methods of the GitHub API. | |||
| // | |||
| // GitHub API docs: http://developer.github.com/v3/pulls/ | |||
| type PullRequestsService struct { | |||
| client *Client | |||
| } | |||
| // PullRequest represents a GitHub pull request on a repository. | |||
| type PullRequest struct { | |||
| Number int `json:"number,omitempty"` | |||
| State string `json:"state,omitempty"` | |||
| Title string `json:"title,omitempty"` | |||
| Body string `json:"body,omitempty"` | |||
| CreatedAt *time.Time `json:"created_at,omitempty"` | |||
| UpdatedAt *time.Time `json:"updated_at,omitempty"` | |||
| ClosedAt *time.Time `json:"closed_at,omitempty"` | |||
| MergedAt *time.Time `json:"merged_at,omitempty"` | |||
| User *User `json:"user,omitempty"` | |||
| Merged bool `json:"merged,omitempty"` | |||
| Mergeable bool `json:"mergeable,omitempty"` | |||
| MergedBy *User `json:"merged_by,omitempty"` | |||
| Comments int `json:"comments,omitempty"` | |||
| Commits int `json:"commits,omitempty"` | |||
| Additions int `json:"additions,omitempty"` | |||
| Deletions int `json:"deletions,omitempty"` | |||
| ChangedFiles int `json:"changed_files,omitempty"` | |||
| // TODO(willnorris): add head and base once we have a Commit struct defined somewhere | |||
| } | |||
| // PullRequestComment represents a comment left on a pull request. | |||
| type PullRequestComment struct { | |||
| ID int `json:"id,omitempty"` | |||
| Body string `json:"body,omitempty"` | |||
| Path string `json:"path,omitempty"` | |||
| Position int `json:"position,omitempty"` | |||
| CommitID string `json:"commit_id,omitempty"` | |||
| User *User `json:"user,omitempty"` | |||
| CreatedAt *time.Time `json:"created_at,omitempty"` | |||
| UpdatedAt *time.Time `json:"updated_at,omitempty"` | |||
| } | |||
| // PullRequestListOptions specifies the optional parameters to the | |||
| // PullRequestsService.List method. | |||
| type PullRequestListOptions struct { | |||
| // State filters pull requests based on their state. Possible values are: | |||
| // open, closed. Default is "open". | |||
| State string | |||
| // Head filters pull requests by head user and branch name in the format of: | |||
| // "user:ref-name". | |||
| Head string | |||
| // Base filters pull requests by base branch name. | |||
| Base string | |||
| } | |||
| // List the pull requests for the specified repository. | |||
| // | |||
| // GitHub API docs: http://developer.github.com/v3/pulls/#list-pull-requests | |||
| func (s *PullRequestsService) List(owner string, repo string, opt *PullRequestListOptions) ([]PullRequest, error) { | |||
| u := fmt.Sprintf("repos/%v/%v/pulls", owner, repo) | |||
| if opt != nil { | |||
| params := url.Values{ | |||
| "state": {opt.State}, | |||
| "head": {opt.Head}, | |||
| "base": {opt.Base}, | |||
| } | |||
| u += "?" + params.Encode() | |||
| } | |||
| req, err := s.client.NewRequest("GET", u, nil) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| pulls := new([]PullRequest) | |||
| _, err = s.client.Do(req, pulls) | |||
| return *pulls, err | |||
| } | |||
| // Get a single pull request. | |||
| // | |||
| // GitHub API docs: https://developer.github.com/v3/pulls/#get-a-single-pull-request | |||
| func (s *PullRequestsService) Get(owner string, repo string, number int) (*PullRequest, error) { | |||
| u := fmt.Sprintf("repos/%v/%v/pulls/%d", owner, repo, number) | |||
| req, err := s.client.NewRequest("GET", u, nil) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| pull := new(PullRequest) | |||
| _, err = s.client.Do(req, pull) | |||
| return pull, err | |||
| } | |||
| // Create a new pull request on the specified repository. | |||
| // | |||
| // GitHub API docs: https://developer.github.com/v3/pulls/#create-a-pull-request | |||
| func (s *PullRequestsService) Create(owner string, repo string, pull *PullRequest) (*PullRequest, error) { | |||
| u := fmt.Sprintf("repos/%v/%v/pulls", owner, repo) | |||
| req, err := s.client.NewRequest("POST", u, pull) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| p := new(PullRequest) | |||
| _, err = s.client.Do(req, p) | |||
| return p, err | |||
| } | |||
| // Edit a pull request. | |||
| // | |||
| // GitHub API docs: https://developer.github.com/v3/pulls/#update-a-pull-request | |||
| func (s *PullRequestsService) Edit(owner string, repo string, number int, pull *PullRequest) (*PullRequest, error) { | |||
| u := fmt.Sprintf("repos/%v/%v/pulls/%d", owner, repo, number) | |||
| req, err := s.client.NewRequest("PATCH", u, pull) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| p := new(PullRequest) | |||
| _, err = s.client.Do(req, p) | |||
| return p, err | |||
| } | |||
| // PullRequestListCommentsOptions specifies the optional parameters to the | |||
| // PullRequestsService.ListComments method. | |||
| type PullRequestListCommentsOptions struct { | |||
| // Sort specifies how to sort comments. Possible values are: created, updated. | |||
| Sort string | |||
| // Direction in which to sort comments. Possible values are: asc, desc. | |||
| Direction string | |||
| // Since filters comments by time. | |||
| Since time.Time | |||
| } | |||
| // ListComments lists all comments on the specified pull request. Specifying a | |||
| // pull request number of 0 will return all comments on all pull requests for | |||
| // the repository. | |||
| // | |||
| // GitHub API docs: https://developer.github.com/v3/pulls/comments/#list-comments-on-a-pull-request | |||
| func (s *PullRequestsService) ListComments(owner string, repo string, number int, opt *PullRequestListCommentsOptions) ([]PullRequestComment, error) { | |||
| var u string | |||
| if number == 0 { | |||
| u = fmt.Sprintf("repos/%v/%v/pulls/comments", owner, repo) | |||
| } else { | |||
| u = fmt.Sprintf("repos/%v/%v/pulls/%d/comments", owner, repo, number) | |||
| } | |||
| if opt != nil { | |||
| params := url.Values{ | |||
| "sort": {opt.Sort}, | |||
| "direction": {opt.Direction}, | |||
| } | |||
| if !opt.Since.IsZero() { | |||
| params.Add("since", opt.Since.Format(time.RFC3339)) | |||
| } | |||
| u += "?" + params.Encode() | |||
| } | |||
| req, err := s.client.NewRequest("GET", u, nil) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| comments := new([]PullRequestComment) | |||
| _, err = s.client.Do(req, comments) | |||
| return *comments, err | |||
| } | |||
| // GetComment fetches the specified pull request comment. | |||
| // | |||
| // GitHub API docs: https://developer.github.com/v3/pulls/comments/#get-a-single-comment | |||
| func (s *PullRequestsService) GetComment(owner string, repo string, number int) (*PullRequestComment, error) { | |||
| u := fmt.Sprintf("repos/%v/%v/pulls/comments/%d", owner, repo, number) | |||
| req, err := s.client.NewRequest("GET", u, nil) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| comment := new(PullRequestComment) | |||
| _, err = s.client.Do(req, comment) | |||
| return comment, err | |||
| } | |||
| // CreateComment creates a new comment on the specified pull request. | |||
| // | |||
| // GitHub API docs: https://developer.github.com/v3/pulls/comments/#get-a-single-comment | |||
| func (s *PullRequestsService) CreateComment(owner string, repo string, number int, comment *PullRequestComment) (*PullRequestComment, error) { | |||
| u := fmt.Sprintf("repos/%v/%v/pulls/%d/comments", owner, repo, number) | |||
| req, err := s.client.NewRequest("POST", u, comment) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| c := new(PullRequestComment) | |||
| _, err = s.client.Do(req, c) | |||
| return c, err | |||
| } | |||
| // EditComment updates a pull request comment. | |||
| // | |||
| // GitHub API docs: https://developer.github.com/v3/pulls/comments/#edit-a-comment | |||
| func (s *PullRequestsService) EditComment(owner string, repo string, number int, comment *PullRequestComment) (*PullRequestComment, error) { | |||
| u := fmt.Sprintf("repos/%v/%v/pulls/comments/%d", owner, repo, number) | |||
| req, err := s.client.NewRequest("PATCH", u, comment) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| c := new(PullRequestComment) | |||
| _, err = s.client.Do(req, c) | |||
| return c, err | |||
| } | |||
| // DeleteComment deletes a pull request comment. | |||
| // | |||
| // GitHub API docs: https://developer.github.com/v3/pulls/comments/#delete-a-comment | |||
| func (s *PullRequestsService) DeleteComment(owner string, repo string, number int) error { | |||
| u := fmt.Sprintf("repos/%v/%v/pulls/comments/%d", owner, repo, number) | |||
| req, err := s.client.NewRequest("DELETE", u, nil) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| _, err = s.client.Do(req, nil) | |||
| return err | |||
| } | |||
| @ -0,0 +1,312 @@ | |||
| // Copyright 2013 Google. All rights reserved. | |||
| // | |||
| // Use of this source code is governed by a BSD-style | |||
| // license that can be found in the LICENSE file or at | |||
| // https://developers.google.com/open-source/licenses/bsd | |||
| package github | |||
| import ( | |||
| "encoding/json" | |||
| "fmt" | |||
| "net/http" | |||
| "reflect" | |||
| "testing" | |||
| "time" | |||
| ) | |||
| func TestPullRequestsService_List(t *testing.T) { | |||
| setup() | |||
| defer teardown() | |||
| mux.HandleFunc("/repos/o/r/pulls", func(w http.ResponseWriter, r *http.Request) { | |||
| testMethod(t, r, "GET") | |||
| testFormValues(t, r, values{ | |||
| "state": "closed", | |||
| "head": "h", | |||
| "base": "b", | |||
| }) | |||
| fmt.Fprint(w, `[{"number":1}]`) | |||
| }) | |||
| opt := &PullRequestListOptions{"closed", "h", "b"} | |||
| pulls, err := client.PullRequests.List("o", "r", opt) | |||
| if err != nil { | |||
| t.Errorf("PullRequests.List returned error: %v", err) | |||
| } | |||
| want := []PullRequest{PullRequest{Number: 1}} | |||
| if !reflect.DeepEqual(pulls, want) { | |||
| t.Errorf("PullRequests.List returned %+v, want %+v", pulls, want) | |||
| } | |||
| } | |||
| func TestPullRequestsService_List_invalidOwner(t *testing.T) { | |||
| _, err := client.PullRequests.List("%", "r", nil) | |||
| testURLParseError(t, err) | |||
| } | |||
| func TestPullRequestsService_Get(t *testing.T) { | |||
| setup() | |||
| defer teardown() | |||
| mux.HandleFunc("/repos/o/r/pulls/1", func(w http.ResponseWriter, r *http.Request) { | |||
| testMethod(t, r, "GET") | |||
| fmt.Fprint(w, `{"number":1}`) | |||
| }) | |||
| pull, err := client.PullRequests.Get("o", "r", 1) | |||
| if err != nil { | |||
| t.Errorf("PullRequests.Get returned error: %v", err) | |||
| } | |||
| want := &PullRequest{Number: 1} | |||
| if !reflect.DeepEqual(pull, want) { | |||
| t.Errorf("PullRequests.Get returned %+v, want %+v", pull, want) | |||
| } | |||
| } | |||
| func TestPullRequestsService_Get_invalidOwner(t *testing.T) { | |||
| _, err := client.PullRequests.Get("%", "r", 1) | |||
| testURLParseError(t, err) | |||
| } | |||
| func TestPullRequestsService_Create(t *testing.T) { | |||
| setup() | |||
| defer teardown() | |||
| input := &PullRequest{Title: "t"} | |||
| mux.HandleFunc("/repos/o/r/pulls", func(w http.ResponseWriter, r *http.Request) { | |||
| v := new(PullRequest) | |||
| json.NewDecoder(r.Body).Decode(v) | |||
| testMethod(t, r, "POST") | |||
| if !reflect.DeepEqual(v, input) { | |||
| t.Errorf("Request body = %+v, want %+v", v, input) | |||
| } | |||
| fmt.Fprint(w, `{"number":1}`) | |||
| }) | |||
| pull, err := client.PullRequests.Create("o", "r", input) | |||
| if err != nil { | |||
| t.Errorf("PullRequests.Create returned error: %v", err) | |||
| } | |||
| want := &PullRequest{Number: 1} | |||
| if !reflect.DeepEqual(pull, want) { | |||
| t.Errorf("PullRequests.Create returned %+v, want %+v", pull, want) | |||
| } | |||
| } | |||
| func TestPullRequestsService_Create_invalidOwner(t *testing.T) { | |||
| _, err := client.PullRequests.Create("%", "r", nil) | |||
| testURLParseError(t, err) | |||
| } | |||
| func TestPullRequestsService_Edit(t *testing.T) { | |||
| setup() | |||
| defer teardown() | |||
| input := &PullRequest{Title: "t"} | |||
| mux.HandleFunc("/repos/o/r/pulls/1", func(w http.ResponseWriter, r *http.Request) { | |||
| v := new(PullRequest) | |||
| json.NewDecoder(r.Body).Decode(v) | |||
| testMethod(t, r, "PATCH") | |||
| if !reflect.DeepEqual(v, input) { | |||
| t.Errorf("Request body = %+v, want %+v", v, input) | |||
| } | |||
| fmt.Fprint(w, `{"number":1}`) | |||
| }) | |||
| pull, err := client.PullRequests.Edit("o", "r", 1, input) | |||
| if err != nil { | |||
| t.Errorf("PullRequests.Edit returned error: %v", err) | |||
| } | |||
| want := &PullRequest{Number: 1} | |||
| if !reflect.DeepEqual(pull, want) { | |||
| t.Errorf("PullRequests.Edit returned %+v, want %+v", pull, want) | |||
| } | |||
| } | |||
| func TestPullRequestsService_Edit_invalidOwner(t *testing.T) { | |||
| _, err := client.PullRequests.Edit("%", "r", 1, nil) | |||
| testURLParseError(t, err) | |||
| } | |||
| func TestPullRequestsService_ListComments_allPulls(t *testing.T) { | |||
| setup() | |||
| defer teardown() | |||
| mux.HandleFunc("/repos/o/r/pulls/comments", func(w http.ResponseWriter, r *http.Request) { | |||
| testMethod(t, r, "GET") | |||
| testFormValues(t, r, values{ | |||
| "sort": "updated", | |||
| "direction": "desc", | |||
| "since": "2002-02-10T15:30:00Z", | |||
| }) | |||
| fmt.Fprint(w, `[{"id":1}]`) | |||
| }) | |||
| opt := &PullRequestListCommentsOptions{"updated", "desc", | |||
| time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC), | |||
| } | |||
| pulls, err := client.PullRequests.ListComments("o", "r", 0, opt) | |||
| if err != nil { | |||
| t.Errorf("PullRequests.ListComments returned error: %v", err) | |||
| } | |||
| want := []PullRequestComment{PullRequestComment{ID: 1}} | |||
| if !reflect.DeepEqual(pulls, want) { | |||
| t.Errorf("PullRequests.ListComments returned %+v, want %+v", pulls, want) | |||
| } | |||
| } | |||
| func TestPullRequestsService_ListComments_specificPull(t *testing.T) { | |||
| setup() | |||
| defer teardown() | |||
| mux.HandleFunc("/repos/o/r/pulls/1/comments", func(w http.ResponseWriter, r *http.Request) { | |||
| testMethod(t, r, "GET") | |||
| fmt.Fprint(w, `[{"id":1}]`) | |||
| }) | |||
| pulls, err := client.PullRequests.ListComments("o", "r", 1, nil) | |||
| if err != nil { | |||
| t.Errorf("PullRequests.ListComments returned error: %v", err) | |||
| } | |||
| want := []PullRequestComment{PullRequestComment{ID: 1}} | |||
| if !reflect.DeepEqual(pulls, want) { | |||
| t.Errorf("PullRequests.ListComments returned %+v, want %+v", pulls, want) | |||
| } | |||
| } | |||
| func TestPullRequestsService_ListComments_invalidOwner(t *testing.T) { | |||
| _, err := client.PullRequests.ListComments("%", "r", 1, nil) | |||
| testURLParseError(t, err) | |||
| } | |||
| func TestPullRequestsService_GetComment(t *testing.T) { | |||
| setup() | |||
| defer teardown() | |||
| mux.HandleFunc("/repos/o/r/pulls/comments/1", func(w http.ResponseWriter, r *http.Request) { | |||
| testMethod(t, r, "GET") | |||
| fmt.Fprint(w, `{"id":1}`) | |||
| }) | |||
| comment, err := client.PullRequests.GetComment("o", "r", 1) | |||
| if err != nil { | |||
| t.Errorf("PullRequests.GetComment returned error: %v", err) | |||
| } | |||
| want := &PullRequestComment{ID: 1} | |||
| if !reflect.DeepEqual(comment, want) { | |||
| t.Errorf("PullRequests.GetComment returned %+v, want %+v", comment, want) | |||
| } | |||
| } | |||
| func TestPullRequestsService_GetComment_invalidOwner(t *testing.T) { | |||
| _, err := client.PullRequests.GetComment("%", "r", 1) | |||
| testURLParseError(t, err) | |||
| } | |||
| func TestPullRequestsService_CreateComment(t *testing.T) { | |||
| setup() | |||
| defer teardown() | |||
| input := &PullRequestComment{Body: "b"} | |||
| mux.HandleFunc("/repos/o/r/pulls/1/comments", func(w http.ResponseWriter, r *http.Request) { | |||
| v := new(PullRequestComment) | |||
| json.NewDecoder(r.Body).Decode(v) | |||
| testMethod(t, r, "POST") | |||
| if !reflect.DeepEqual(v, input) { | |||
| t.Errorf("Request body = %+v, want %+v", v, input) | |||
| } | |||
| fmt.Fprint(w, `{"id":1}`) | |||
| }) | |||
| comment, err := client.PullRequests.CreateComment("o", "r", 1, input) | |||
| if err != nil { | |||
| t.Errorf("PullRequests.CreateComment returned error: %v", err) | |||
| } | |||
| want := &PullRequestComment{ID: 1} | |||
| if !reflect.DeepEqual(comment, want) { | |||
| t.Errorf("PullRequests.CreateComment returned %+v, want %+v", comment, want) | |||
| } | |||
| } | |||
| func TestPullRequestsService_CreateComment_invalidOwner(t *testing.T) { | |||
| _, err := client.PullRequests.CreateComment("%", "r", 1, nil) | |||
| testURLParseError(t, err) | |||
| } | |||
| func TestPullRequestsService_EditComment(t *testing.T) { | |||
| setup() | |||
| defer teardown() | |||
| input := &PullRequestComment{Body: "b"} | |||
| mux.HandleFunc("/repos/o/r/pulls/comments/1", func(w http.ResponseWriter, r *http.Request) { | |||
| v := new(PullRequestComment) | |||
| json.NewDecoder(r.Body).Decode(v) | |||
| testMethod(t, r, "PATCH") | |||
| if !reflect.DeepEqual(v, input) { | |||
| t.Errorf("Request body = %+v, want %+v", v, input) | |||
| } | |||
| fmt.Fprint(w, `{"id":1}`) | |||
| }) | |||
| comment, err := client.PullRequests.EditComment("o", "r", 1, input) | |||
| if err != nil { | |||
| t.Errorf("PullRequests.EditComment returned error: %v", err) | |||
| } | |||
| want := &PullRequestComment{ID: 1} | |||
| if !reflect.DeepEqual(comment, want) { | |||
| t.Errorf("PullRequests.EditComment returned %+v, want %+v", comment, want) | |||
| } | |||
| } | |||
| func TestPullRequestsService_EditComment_invalidOwner(t *testing.T) { | |||
| _, err := client.PullRequests.EditComment("%", "r", 1, nil) | |||
| testURLParseError(t, err) | |||
| } | |||
| func TestPullRequestsService_DeleteComment(t *testing.T) { | |||
| setup() | |||
| defer teardown() | |||
| mux.HandleFunc("/repos/o/r/pulls/comments/1", func(w http.ResponseWriter, r *http.Request) { | |||
| testMethod(t, r, "DELETE") | |||
| }) | |||
| err := client.PullRequests.DeleteComment("o", "r", 1) | |||
| if err != nil { | |||
| t.Errorf("PullRequests.DeleteComment returned error: %v", err) | |||
| } | |||
| } | |||
| func TestPullRequestsService_DeleteComment_invalidOwner(t *testing.T) { | |||
| err := client.PullRequests.DeleteComment("%", "r", 1) | |||
| testURLParseError(t, err) | |||
| } | |||