mirror of https://github.com/matrix-org/go-neb.git
Kegan Dougal
9 years ago
123 changed files with 23713 additions and 0 deletions
-
14vendor/manifest
-
67vendor/src/github.com/google/go-github/github/activity.go
-
287vendor/src/github.com/google/go-github/github/activity_events.go
-
305vendor/src/github.com/google/go-github/github/activity_events_test.go
-
222vendor/src/github.com/google/go-github/github/activity_notifications.go
-
203vendor/src/github.com/google/go-github/github/activity_notifications_test.go
-
132vendor/src/github.com/google/go-github/github/activity_star.go
-
171vendor/src/github.com/google/go-github/github/activity_star_test.go
-
128vendor/src/github.com/google/go-github/github/activity_test.go
-
136vendor/src/github.com/google/go-github/github/activity_watching.go
-
183vendor/src/github.com/google/go-github/github/activity_watching_test.go
-
436vendor/src/github.com/google/go-github/github/authorizations.go
-
343vendor/src/github.com/google/go-github/github/authorizations_test.go
-
145vendor/src/github.com/google/go-github/github/doc.go
-
467vendor/src/github.com/google/go-github/github/event_types.go
-
75vendor/src/github.com/google/go-github/github/examples_test.go
-
343vendor/src/github.com/google/go-github/github/gists.go
-
118vendor/src/github.com/google/go-github/github/gists_comments.go
-
153vendor/src/github.com/google/go-github/github/gists_comments_test.go
-
488vendor/src/github.com/google/go-github/github/gists_test.go
-
12vendor/src/github.com/google/go-github/github/git.go
-
47vendor/src/github.com/google/go-github/github/git_blobs.go
-
97vendor/src/github.com/google/go-github/github/git_blobs_test.go
-
127vendor/src/github.com/google/go-github/github/git_commits.go
-
83vendor/src/github.com/google/go-github/github/git_commits_test.go
-
162vendor/src/github.com/google/go-github/github/git_refs.go
-
280vendor/src/github.com/google/go-github/github/git_refs_test.go
-
77vendor/src/github.com/google/go-github/github/git_tags.go
-
68vendor/src/github.com/google/go-github/github/git_tags_test.go
-
89vendor/src/github.com/google/go-github/github/git_trees.go
-
189vendor/src/github.com/google/go-github/github/git_trees_test.go
-
807vendor/src/github.com/google/go-github/github/github.go
-
865vendor/src/github.com/google/go-github/github/github_test.go
-
61vendor/src/github.com/google/go-github/github/gitignore.go
-
58vendor/src/github.com/google/go-github/github/gitignore_test.go
-
299vendor/src/github.com/google/go-github/github/issues.go
-
82vendor/src/github.com/google/go-github/github/issues_assignees.go
-
157vendor/src/github.com/google/go-github/github/issues_assignees_test.go
-
147vendor/src/github.com/google/go-github/github/issues_comments.go
-
187vendor/src/github.com/google/go-github/github/issues_comments_test.go
-
149vendor/src/github.com/google/go-github/github/issues_events.go
-
83vendor/src/github.com/google/go-github/github/issues_events_test.go
-
222vendor/src/github.com/google/go-github/github/issues_labels.go
-
313vendor/src/github.com/google/go-github/github/issues_labels_test.go
-
146vendor/src/github.com/google/go-github/github/issues_milestones.go
-
158vendor/src/github.com/google/go-github/github/issues_milestones_test.go
-
276vendor/src/github.com/google/go-github/github/issues_test.go
-
148vendor/src/github.com/google/go-github/github/issues_timeline.go
-
39vendor/src/github.com/google/go-github/github/issues_timeline_test.go
-
79vendor/src/github.com/google/go-github/github/licenses.go
-
64vendor/src/github.com/google/go-github/github/licenses_test.go
-
119vendor/src/github.com/google/go-github/github/messages.go
-
81vendor/src/github.com/google/go-github/github/messages_test.go
-
223vendor/src/github.com/google/go-github/github/migrations.go
-
326vendor/src/github.com/google/go-github/github/migrations_source_import.go
-
225vendor/src/github.com/google/go-github/github/migrations_source_import_test.go
-
177vendor/src/github.com/google/go-github/github/migrations_test.go
-
197vendor/src/github.com/google/go-github/github/misc.go
-
170vendor/src/github.com/google/go-github/github/misc_test.go
-
170vendor/src/github.com/google/go-github/github/orgs.go
-
104vendor/src/github.com/google/go-github/github/orgs_hooks.go
-
134vendor/src/github.com/google/go-github/github/orgs_hooks_test.go
-
272vendor/src/github.com/google/go-github/github/orgs_members.go
-
355vendor/src/github.com/google/go-github/github/orgs_members_test.go
-
379vendor/src/github.com/google/go-github/github/orgs_teams.go
-
501vendor/src/github.com/google/go-github/github/orgs_teams_test.go
-
143vendor/src/github.com/google/go-github/github/orgs_test.go
-
285vendor/src/github.com/google/go-github/github/pulls.go
-
156vendor/src/github.com/google/go-github/github/pulls_comments.go
-
187vendor/src/github.com/google/go-github/github/pulls_comments_test.go
-
363vendor/src/github.com/google/go-github/github/pulls_test.go
-
270vendor/src/github.com/google/go-github/github/reactions.go
-
200vendor/src/github.com/google/go-github/github/reactions_test.go
-
597vendor/src/github.com/google/go-github/github/repos.go
-
92vendor/src/github.com/google/go-github/github/repos_collaborators.go
-
134vendor/src/github.com/google/go-github/github/repos_collaborators_test.go
-
160vendor/src/github.com/google/go-github/github/repos_comments.go
-
183vendor/src/github.com/google/go-github/github/repos_comments_test.go
-
198vendor/src/github.com/google/go-github/github/repos_commits.go
-
233vendor/src/github.com/google/go-github/github/repos_commits_test.go
-
275vendor/src/github.com/google/go-github/github/repos_contents.go
-
412vendor/src/github.com/google/go-github/github/repos_contents_test.go
-
179vendor/src/github.com/google/go-github/github/repos_deployments.go
-
118vendor/src/github.com/google/go-github/github/repos_deployments_test.go
-
73vendor/src/github.com/google/go-github/github/repos_forks.go
-
73vendor/src/github.com/google/go-github/github/repos_forks_test.go
-
196vendor/src/github.com/google/go-github/github/repos_hooks.go
-
187vendor/src/github.com/google/go-github/github/repos_hooks_test.go
-
91vendor/src/github.com/google/go-github/github/repos_invitations.go
-
73vendor/src/github.com/google/go-github/github/repos_invitations_test.go
-
108vendor/src/github.com/google/go-github/github/repos_keys.go
-
153vendor/src/github.com/google/go-github/github/repos_keys_test.go
-
37vendor/src/github.com/google/go-github/github/repos_merging.go
-
47vendor/src/github.com/google/go-github/github/repos_merging_test.go
-
116vendor/src/github.com/google/go-github/github/repos_pages.go
-
95vendor/src/github.com/google/go-github/github/repos_pages_test.go
-
325vendor/src/github.com/google/go-github/github/repos_releases.go
-
352vendor/src/github.com/google/go-github/github/repos_releases_test.go
-
214vendor/src/github.com/google/go-github/github/repos_stats.go
-
210vendor/src/github.com/google/go-github/github/repos_stats_test.go
@ -0,0 +1,67 @@ |
|||
// Copyright 2013 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 |
|||
|
|||
// ActivityService handles communication with the activity related
|
|||
// methods of the GitHub API.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/activity/
|
|||
type ActivityService service |
|||
|
|||
// FeedLink represents a link to a related resource.
|
|||
type FeedLink struct { |
|||
HRef *string `json:"href,omitempty"` |
|||
Type *string `json:"type,omitempty"` |
|||
} |
|||
|
|||
// Feeds represents timeline resources in Atom format.
|
|||
type Feeds struct { |
|||
TimelineURL *string `json:"timeline_url,omitempty"` |
|||
UserURL *string `json:"user_url,omitempty"` |
|||
CurrentUserPublicURL *string `json:"current_user_public_url,omitempty"` |
|||
CurrentUserURL *string `json:"current_user_url,omitempty"` |
|||
CurrentUserActorURL *string `json:"current_user_actor_url,omitempty"` |
|||
CurrentUserOrganizationURL *string `json:"current_user_organization_url,omitempty"` |
|||
CurrentUserOrganizationURLs []string `json:"current_user_organization_urls,omitempty"` |
|||
Links *struct { |
|||
Timeline *FeedLink `json:"timeline,omitempty"` |
|||
User *FeedLink `json:"user,omitempty"` |
|||
CurrentUserPublic *FeedLink `json:"current_user_public,omitempty"` |
|||
CurrentUser *FeedLink `json:"current_user,omitempty"` |
|||
CurrentUserActor *FeedLink `json:"current_user_actor,omitempty"` |
|||
CurrentUserOrganization *FeedLink `json:"current_user_organization,omitempty"` |
|||
CurrentUserOrganizations []FeedLink `json:"current_user_organizations,omitempty"` |
|||
} `json:"_links,omitempty"` |
|||
} |
|||
|
|||
// ListFeeds lists all the feeds available to the authenticated user.
|
|||
//
|
|||
// GitHub provides several timeline resources in Atom format:
|
|||
// Timeline: The GitHub global public timeline
|
|||
// User: The public timeline for any user, using URI template
|
|||
// Current user public: The public timeline for the authenticated user
|
|||
// Current user: The private timeline for the authenticated user
|
|||
// Current user actor: The private timeline for activity created by the
|
|||
// authenticated user
|
|||
// Current user organizations: The private timeline for the organizations
|
|||
// the authenticated user is a member of.
|
|||
//
|
|||
// Note: Private feeds are only returned when authenticating via Basic Auth
|
|||
// since current feed URIs use the older, non revocable auth tokens.
|
|||
func (s *ActivityService) ListFeeds() (*Feeds, *Response, error) { |
|||
req, err := s.client.NewRequest("GET", "feeds", nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
f := &Feeds{} |
|||
resp, err := s.client.Do(req, f) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return f, resp, nil |
|||
} |
@ -0,0 +1,287 @@ |
|||
// Copyright 2013 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"time" |
|||
) |
|||
|
|||
// Event represents a GitHub event.
|
|||
type Event struct { |
|||
Type *string `json:"type,omitempty"` |
|||
Public *bool `json:"public"` |
|||
RawPayload *json.RawMessage `json:"payload,omitempty"` |
|||
Repo *Repository `json:"repo,omitempty"` |
|||
Actor *User `json:"actor,omitempty"` |
|||
Org *Organization `json:"org,omitempty"` |
|||
CreatedAt *time.Time `json:"created_at,omitempty"` |
|||
ID *string `json:"id,omitempty"` |
|||
} |
|||
|
|||
func (e Event) String() string { |
|||
return Stringify(e) |
|||
} |
|||
|
|||
// Payload returns the parsed event payload. For recognized event types,
|
|||
// a value of the corresponding struct type will be returned.
|
|||
func (e *Event) Payload() (payload interface{}) { |
|||
switch *e.Type { |
|||
case "CommitCommentEvent": |
|||
payload = &CommitCommentEvent{} |
|||
case "CreateEvent": |
|||
payload = &CreateEvent{} |
|||
case "DeleteEvent": |
|||
payload = &DeleteEvent{} |
|||
case "DeploymentEvent": |
|||
payload = &DeploymentEvent{} |
|||
case "DeploymentStatusEvent": |
|||
payload = &DeploymentStatusEvent{} |
|||
case "ForkEvent": |
|||
payload = &ForkEvent{} |
|||
case "GollumEvent": |
|||
payload = &GollumEvent{} |
|||
case "IssueActivityEvent": |
|||
payload = &IssueActivityEvent{} |
|||
case "IssueCommentEvent": |
|||
payload = &IssueCommentEvent{} |
|||
case "IssuesEvent": |
|||
payload = &IssuesEvent{} |
|||
case "MemberEvent": |
|||
payload = &MemberEvent{} |
|||
case "MembershipEvent": |
|||
payload = &MembershipEvent{} |
|||
case "PageBuildEvent": |
|||
payload = &PageBuildEvent{} |
|||
case "PublicEvent": |
|||
payload = &PublicEvent{} |
|||
case "PullRequestEvent": |
|||
payload = &PullRequestEvent{} |
|||
case "PullRequestReviewCommentEvent": |
|||
payload = &PullRequestReviewCommentEvent{} |
|||
case "PushEvent": |
|||
payload = &PushEvent{} |
|||
case "ReleaseEvent": |
|||
payload = &ReleaseEvent{} |
|||
case "RepositoryEvent": |
|||
payload = &RepositoryEvent{} |
|||
case "StatusEvent": |
|||
payload = &StatusEvent{} |
|||
case "TeamAddEvent": |
|||
payload = &TeamAddEvent{} |
|||
case "WatchEvent": |
|||
payload = &WatchEvent{} |
|||
} |
|||
if err := json.Unmarshal(*e.RawPayload, &payload); err != nil { |
|||
panic(err.Error()) |
|||
} |
|||
return payload |
|||
} |
|||
|
|||
// ListEvents drinks from the firehose of all public events across GitHub.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/activity/events/#list-public-events
|
|||
func (s *ActivityService) ListEvents(opt *ListOptions) ([]*Event, *Response, error) { |
|||
u, err := addOptions("events", opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
events := new([]*Event) |
|||
resp, err := s.client.Do(req, events) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *events, resp, err |
|||
} |
|||
|
|||
// ListRepositoryEvents lists events for a repository.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/activity/events/#list-repository-events
|
|||
func (s *ActivityService) ListRepositoryEvents(owner, repo string, opt *ListOptions) ([]*Event, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/events", owner, repo) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
events := new([]*Event) |
|||
resp, err := s.client.Do(req, events) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *events, resp, err |
|||
} |
|||
|
|||
// ListIssueEventsForRepository lists issue events for a repository.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/activity/events/#list-issue-events-for-a-repository
|
|||
func (s *ActivityService) ListIssueEventsForRepository(owner, repo string, opt *ListOptions) ([]*Event, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/issues/events", owner, repo) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
events := new([]*Event) |
|||
resp, err := s.client.Do(req, events) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *events, resp, err |
|||
} |
|||
|
|||
// ListEventsForRepoNetwork lists public events for a network of repositories.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/activity/events/#list-public-events-for-a-network-of-repositories
|
|||
func (s *ActivityService) ListEventsForRepoNetwork(owner, repo string, opt *ListOptions) ([]*Event, *Response, error) { |
|||
u := fmt.Sprintf("networks/%v/%v/events", owner, repo) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
events := new([]*Event) |
|||
resp, err := s.client.Do(req, events) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *events, resp, err |
|||
} |
|||
|
|||
// ListEventsForOrganization lists public events for an organization.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/activity/events/#list-public-events-for-an-organization
|
|||
func (s *ActivityService) ListEventsForOrganization(org string, opt *ListOptions) ([]*Event, *Response, error) { |
|||
u := fmt.Sprintf("orgs/%v/events", org) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
events := new([]*Event) |
|||
resp, err := s.client.Do(req, events) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *events, resp, err |
|||
} |
|||
|
|||
// ListEventsPerformedByUser lists the events performed by a user. If publicOnly is
|
|||
// true, only public events will be returned.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/activity/events/#list-events-performed-by-a-user
|
|||
func (s *ActivityService) ListEventsPerformedByUser(user string, publicOnly bool, opt *ListOptions) ([]*Event, *Response, error) { |
|||
var u string |
|||
if publicOnly { |
|||
u = fmt.Sprintf("users/%v/events/public", user) |
|||
} else { |
|||
u = fmt.Sprintf("users/%v/events", user) |
|||
} |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
events := new([]*Event) |
|||
resp, err := s.client.Do(req, events) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *events, resp, err |
|||
} |
|||
|
|||
// ListEventsReceivedByUser lists the events received by a user. If publicOnly is
|
|||
// true, only public events will be returned.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/activity/events/#list-events-that-a-user-has-received
|
|||
func (s *ActivityService) ListEventsReceivedByUser(user string, publicOnly bool, opt *ListOptions) ([]*Event, *Response, error) { |
|||
var u string |
|||
if publicOnly { |
|||
u = fmt.Sprintf("users/%v/received_events/public", user) |
|||
} else { |
|||
u = fmt.Sprintf("users/%v/received_events", user) |
|||
} |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
events := new([]*Event) |
|||
resp, err := s.client.Do(req, events) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *events, resp, err |
|||
} |
|||
|
|||
// ListUserEventsForOrganization provides the user’s organization dashboard. You
|
|||
// must be authenticated as the user to view this.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/activity/events/#list-events-for-an-organization
|
|||
func (s *ActivityService) ListUserEventsForOrganization(org, user string, opt *ListOptions) ([]*Event, *Response, error) { |
|||
u := fmt.Sprintf("users/%v/events/orgs/%v", user, org) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
events := new([]*Event) |
|||
resp, err := s.client.Do(req, events) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *events, resp, err |
|||
} |
@ -0,0 +1,305 @@ |
|||
// Copyright 2013 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestActivityService_ListEvents(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/events", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{ |
|||
"page": "2", |
|||
}) |
|||
fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 2} |
|||
events, _, err := client.Activity.ListEvents(opt) |
|||
if err != nil { |
|||
t.Errorf("Activities.ListEvents returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Event{{ID: String("1")}, {ID: String("2")}} |
|||
if !reflect.DeepEqual(events, want) { |
|||
t.Errorf("Activities.ListEvents returned %+v, want %+v", events, want) |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_ListRepositoryEvents(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/events", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{ |
|||
"page": "2", |
|||
}) |
|||
fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 2} |
|||
events, _, err := client.Activity.ListRepositoryEvents("o", "r", opt) |
|||
if err != nil { |
|||
t.Errorf("Activities.ListRepositoryEvents returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Event{{ID: String("1")}, {ID: String("2")}} |
|||
if !reflect.DeepEqual(events, want) { |
|||
t.Errorf("Activities.ListRepositoryEvents returned %+v, want %+v", events, want) |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_ListRepositoryEvents_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Activity.ListRepositoryEvents("%", "%", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestActivityService_ListIssueEventsForRepository(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/issues/events", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{ |
|||
"page": "2", |
|||
}) |
|||
fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 2} |
|||
events, _, err := client.Activity.ListIssueEventsForRepository("o", "r", opt) |
|||
if err != nil { |
|||
t.Errorf("Activities.ListIssueEventsForRepository returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Event{{ID: String("1")}, {ID: String("2")}} |
|||
if !reflect.DeepEqual(events, want) { |
|||
t.Errorf("Activities.ListIssueEventsForRepository returned %+v, want %+v", events, want) |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_ListIssueEventsForRepository_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Activity.ListIssueEventsForRepository("%", "%", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestActivityService_ListEventsForRepoNetwork(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/networks/o/r/events", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{ |
|||
"page": "2", |
|||
}) |
|||
fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 2} |
|||
events, _, err := client.Activity.ListEventsForRepoNetwork("o", "r", opt) |
|||
if err != nil { |
|||
t.Errorf("Activities.ListEventsForRepoNetwork returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Event{{ID: String("1")}, {ID: String("2")}} |
|||
if !reflect.DeepEqual(events, want) { |
|||
t.Errorf("Activities.ListEventsForRepoNetwork returned %+v, want %+v", events, want) |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_ListEventsForRepoNetwork_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Activity.ListEventsForRepoNetwork("%", "%", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestActivityService_ListEventsForOrganization(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/orgs/o/events", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{ |
|||
"page": "2", |
|||
}) |
|||
fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 2} |
|||
events, _, err := client.Activity.ListEventsForOrganization("o", opt) |
|||
if err != nil { |
|||
t.Errorf("Activities.ListEventsForOrganization returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Event{{ID: String("1")}, {ID: String("2")}} |
|||
if !reflect.DeepEqual(events, want) { |
|||
t.Errorf("Activities.ListEventsForOrganization returned %+v, want %+v", events, want) |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_ListEventsForOrganization_invalidOrg(t *testing.T) { |
|||
_, _, err := client.Activity.ListEventsForOrganization("%", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestActivityService_ListEventsPerformedByUser_all(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/users/u/events", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{ |
|||
"page": "2", |
|||
}) |
|||
fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 2} |
|||
events, _, err := client.Activity.ListEventsPerformedByUser("u", false, opt) |
|||
if err != nil { |
|||
t.Errorf("Events.ListPerformedByUser returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Event{{ID: String("1")}, {ID: String("2")}} |
|||
if !reflect.DeepEqual(events, want) { |
|||
t.Errorf("Events.ListPerformedByUser returned %+v, want %+v", events, want) |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_ListEventsPerformedByUser_publicOnly(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/users/u/events/public", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) |
|||
}) |
|||
|
|||
events, _, err := client.Activity.ListEventsPerformedByUser("u", true, nil) |
|||
if err != nil { |
|||
t.Errorf("Events.ListPerformedByUser returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Event{{ID: String("1")}, {ID: String("2")}} |
|||
if !reflect.DeepEqual(events, want) { |
|||
t.Errorf("Events.ListPerformedByUser returned %+v, want %+v", events, want) |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_ListEventsPerformedByUser_invalidUser(t *testing.T) { |
|||
_, _, err := client.Activity.ListEventsPerformedByUser("%", false, nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestActivityService_ListEventsReceivedByUser_all(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/users/u/received_events", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{ |
|||
"page": "2", |
|||
}) |
|||
fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 2} |
|||
events, _, err := client.Activity.ListEventsReceivedByUser("u", false, opt) |
|||
if err != nil { |
|||
t.Errorf("Events.ListReceivedByUser returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Event{{ID: String("1")}, {ID: String("2")}} |
|||
if !reflect.DeepEqual(events, want) { |
|||
t.Errorf("Events.ListReceivedUser returned %+v, want %+v", events, want) |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_ListEventsReceivedByUser_publicOnly(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/users/u/received_events/public", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) |
|||
}) |
|||
|
|||
events, _, err := client.Activity.ListEventsReceivedByUser("u", true, nil) |
|||
if err != nil { |
|||
t.Errorf("Events.ListReceivedByUser returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Event{{ID: String("1")}, {ID: String("2")}} |
|||
if !reflect.DeepEqual(events, want) { |
|||
t.Errorf("Events.ListReceivedByUser returned %+v, want %+v", events, want) |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_ListEventsReceivedByUser_invalidUser(t *testing.T) { |
|||
_, _, err := client.Activity.ListEventsReceivedByUser("%", false, nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestActivityService_ListUserEventsForOrganization(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/users/u/events/orgs/o", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{ |
|||
"page": "2", |
|||
}) |
|||
fmt.Fprint(w, `[{"id":"1"},{"id":"2"}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 2} |
|||
events, _, err := client.Activity.ListUserEventsForOrganization("o", "u", opt) |
|||
if err != nil { |
|||
t.Errorf("Activities.ListUserEventsForOrganization returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Event{{ID: String("1")}, {ID: String("2")}} |
|||
if !reflect.DeepEqual(events, want) { |
|||
t.Errorf("Activities.ListUserEventsForOrganization returned %+v, want %+v", events, want) |
|||
} |
|||
} |
|||
|
|||
func TestActivity_EventPayload_typed(t *testing.T) { |
|||
raw := []byte(`{"type": "PushEvent","payload":{"push_id": 1}}`) |
|||
var event *Event |
|||
if err := json.Unmarshal(raw, &event); err != nil { |
|||
t.Fatalf("Unmarshal Event returned error: %v", err) |
|||
} |
|||
|
|||
want := &PushEvent{PushID: Int(1)} |
|||
if !reflect.DeepEqual(event.Payload(), want) { |
|||
t.Errorf("Event Payload returned %+v, want %+v", event.Payload(), want) |
|||
} |
|||
} |
|||
|
|||
// TestEvent_Payload_untyped checks that unrecognized events are parsed to an
|
|||
// interface{} value (instead of being discarded or throwing an error), for
|
|||
// forward compatibility with new event types.
|
|||
func TestActivity_EventPayload_untyped(t *testing.T) { |
|||
raw := []byte(`{"type": "UnrecognizedEvent","payload":{"field": "val"}}`) |
|||
var event *Event |
|||
if err := json.Unmarshal(raw, &event); err != nil { |
|||
t.Fatalf("Unmarshal Event returned error: %v", err) |
|||
} |
|||
|
|||
want := map[string]interface{}{"field": "val"} |
|||
if !reflect.DeepEqual(event.Payload(), want) { |
|||
t.Errorf("Event Payload returned %+v, want %+v", event.Payload(), want) |
|||
} |
|||
} |
@ -0,0 +1,222 @@ |
|||
// Copyright 2014 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 ( |
|||
"fmt" |
|||
"time" |
|||
) |
|||
|
|||
// Notification identifies a GitHub notification for a user.
|
|||
type Notification struct { |
|||
ID *string `json:"id,omitempty"` |
|||
Repository *Repository `json:"repository,omitempty"` |
|||
Subject *NotificationSubject `json:"subject,omitempty"` |
|||
|
|||
// Reason identifies the event that triggered the notification.
|
|||
//
|
|||
// GitHub API Docs: https://developer.github.com/v3/activity/notifications/#notification-reasons
|
|||
Reason *string `json:"reason,omitempty"` |
|||
|
|||
Unread *bool `json:"unread,omitempty"` |
|||
UpdatedAt *time.Time `json:"updated_at,omitempty"` |
|||
LastReadAt *time.Time `json:"last_read_at,omitempty"` |
|||
URL *string `json:"url,omitempty"` |
|||
} |
|||
|
|||
// NotificationSubject identifies the subject of a notification.
|
|||
type NotificationSubject struct { |
|||
Title *string `json:"title,omitempty"` |
|||
URL *string `json:"url,omitempty"` |
|||
LatestCommentURL *string `json:"latest_comment_url,omitempty"` |
|||
Type *string `json:"type,omitempty"` |
|||
} |
|||
|
|||
// NotificationListOptions specifies the optional parameters to the
|
|||
// ActivityService.ListNotifications method.
|
|||
type NotificationListOptions struct { |
|||
All bool `url:"all,omitempty"` |
|||
Participating bool `url:"participating,omitempty"` |
|||
Since time.Time `url:"since,omitempty"` |
|||
Before time.Time `url:"before,omitempty"` |
|||
|
|||
ListOptions |
|||
} |
|||
|
|||
// ListNotifications lists all notifications for the authenticated user.
|
|||
//
|
|||
// GitHub API Docs: https://developer.github.com/v3/activity/notifications/#list-your-notifications
|
|||
func (s *ActivityService) ListNotifications(opt *NotificationListOptions) ([]*Notification, *Response, error) { |
|||
u := fmt.Sprintf("notifications") |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
var notifications []*Notification |
|||
resp, err := s.client.Do(req, ¬ifications) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return notifications, resp, err |
|||
} |
|||
|
|||
// ListRepositoryNotifications lists all notifications in a given repository
|
|||
// for the authenticated user.
|
|||
//
|
|||
// GitHub API Docs: https://developer.github.com/v3/activity/notifications/#list-your-notifications-in-a-repository
|
|||
func (s *ActivityService) ListRepositoryNotifications(owner, repo string, opt *NotificationListOptions) ([]*Notification, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/notifications", owner, repo) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
var notifications []*Notification |
|||
resp, err := s.client.Do(req, ¬ifications) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return notifications, resp, err |
|||
} |
|||
|
|||
type markReadOptions struct { |
|||
LastReadAt time.Time `json:"last_read_at,omitempty"` |
|||
} |
|||
|
|||
// MarkNotificationsRead marks all notifications up to lastRead as read.
|
|||
//
|
|||
// GitHub API Docs: https://developer.github.com/v3/activity/notifications/#mark-as-read
|
|||
func (s *ActivityService) MarkNotificationsRead(lastRead time.Time) (*Response, error) { |
|||
opts := &markReadOptions{ |
|||
LastReadAt: lastRead, |
|||
} |
|||
req, err := s.client.NewRequest("PUT", "notifications", opts) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
return s.client.Do(req, nil) |
|||
} |
|||
|
|||
// MarkRepositoryNotificationsRead marks all notifications up to lastRead in
|
|||
// the specified repository as read.
|
|||
//
|
|||
// GitHub API Docs: https://developer.github.com/v3/activity/notifications/#mark-notifications-as-read-in-a-repository
|
|||
func (s *ActivityService) MarkRepositoryNotificationsRead(owner, repo string, lastRead time.Time) (*Response, error) { |
|||
opts := &markReadOptions{ |
|||
LastReadAt: lastRead, |
|||
} |
|||
u := fmt.Sprintf("repos/%v/%v/notifications", owner, repo) |
|||
req, err := s.client.NewRequest("PUT", u, opts) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
return s.client.Do(req, nil) |
|||
} |
|||
|
|||
// GetThread gets the specified notification thread.
|
|||
//
|
|||
// GitHub API Docs: https://developer.github.com/v3/activity/notifications/#view-a-single-thread
|
|||
func (s *ActivityService) GetThread(id string) (*Notification, *Response, error) { |
|||
u := fmt.Sprintf("notifications/threads/%v", id) |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
notification := new(Notification) |
|||
resp, err := s.client.Do(req, notification) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return notification, resp, err |
|||
} |
|||
|
|||
// MarkThreadRead marks the specified thread as read.
|
|||
//
|
|||
// GitHub API Docs: https://developer.github.com/v3/activity/notifications/#mark-a-thread-as-read
|
|||
func (s *ActivityService) MarkThreadRead(id string) (*Response, error) { |
|||
u := fmt.Sprintf("notifications/threads/%v", id) |
|||
|
|||
req, err := s.client.NewRequest("PATCH", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
return s.client.Do(req, nil) |
|||
} |
|||
|
|||
// GetThreadSubscription checks to see if the authenticated user is subscribed
|
|||
// to a thread.
|
|||
//
|
|||
// GitHub API Docs: https://developer.github.com/v3/activity/notifications/#get-a-thread-subscription
|
|||
func (s *ActivityService) GetThreadSubscription(id string) (*Subscription, *Response, error) { |
|||
u := fmt.Sprintf("notifications/threads/%v/subscription", id) |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
sub := new(Subscription) |
|||
resp, err := s.client.Do(req, sub) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return sub, resp, err |
|||
} |
|||
|
|||
// SetThreadSubscription sets the subscription for the specified thread for the
|
|||
// authenticated user.
|
|||
//
|
|||
// GitHub API Docs: https://developer.github.com/v3/activity/notifications/#set-a-thread-subscription
|
|||
func (s *ActivityService) SetThreadSubscription(id string, subscription *Subscription) (*Subscription, *Response, error) { |
|||
u := fmt.Sprintf("notifications/threads/%v/subscription", id) |
|||
|
|||
req, err := s.client.NewRequest("PUT", u, subscription) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
sub := new(Subscription) |
|||
resp, err := s.client.Do(req, sub) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return sub, resp, err |
|||
} |
|||
|
|||
// DeleteThreadSubscription deletes the subscription for the specified thread
|
|||
// for the authenticated user.
|
|||
//
|
|||
// GitHub API Docs: https://developer.github.com/v3/activity/notifications/#delete-a-thread-subscription
|
|||
func (s *ActivityService) DeleteThreadSubscription(id string) (*Response, error) { |
|||
u := fmt.Sprintf("notifications/threads/%v/subscription", id) |
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
return s.client.Do(req, nil) |
|||
} |
@ -0,0 +1,203 @@ |
|||
// Copyright 2014 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
"time" |
|||
) |
|||
|
|||
func TestActivityService_ListNotification(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/notifications", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{ |
|||
"all": "true", |
|||
"participating": "true", |
|||
"since": "2006-01-02T15:04:05Z", |
|||
"before": "2007-03-04T15:04:05Z", |
|||
}) |
|||
|
|||
fmt.Fprint(w, `[{"id":"1", "subject":{"title":"t"}}]`) |
|||
}) |
|||
|
|||
opt := &NotificationListOptions{ |
|||
All: true, |
|||
Participating: true, |
|||
Since: time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC), |
|||
Before: time.Date(2007, 03, 04, 15, 04, 05, 0, time.UTC), |
|||
} |
|||
notifications, _, err := client.Activity.ListNotifications(opt) |
|||
if err != nil { |
|||
t.Errorf("Activity.ListNotifications returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Notification{{ID: String("1"), Subject: &NotificationSubject{Title: String("t")}}} |
|||
if !reflect.DeepEqual(notifications, want) { |
|||
t.Errorf("Activity.ListNotifications returned %+v, want %+v", notifications, want) |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_ListRepositoryNotification(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/notifications", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `[{"id":"1"}]`) |
|||
}) |
|||
|
|||
notifications, _, err := client.Activity.ListRepositoryNotifications("o", "r", nil) |
|||
if err != nil { |
|||
t.Errorf("Activity.ListRepositoryNotifications returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Notification{{ID: String("1")}} |
|||
if !reflect.DeepEqual(notifications, want) { |
|||
t.Errorf("Activity.ListRepositoryNotifications returned %+v, want %+v", notifications, want) |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_MarkNotificationsRead(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/notifications", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "PUT") |
|||
testHeader(t, r, "Content-Type", "application/json") |
|||
testBody(t, r, `{"last_read_at":"2006-01-02T15:04:05Z"}`+"\n") |
|||
|
|||
w.WriteHeader(http.StatusResetContent) |
|||
}) |
|||
|
|||
_, err := client.Activity.MarkNotificationsRead(time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC)) |
|||
if err != nil { |
|||
t.Errorf("Activity.MarkNotificationsRead returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_MarkRepositoryNotificationsRead(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/notifications", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "PUT") |
|||
testHeader(t, r, "Content-Type", "application/json") |
|||
testBody(t, r, `{"last_read_at":"2006-01-02T15:04:05Z"}`+"\n") |
|||
|
|||
w.WriteHeader(http.StatusResetContent) |
|||
}) |
|||
|
|||
_, err := client.Activity.MarkRepositoryNotificationsRead("o", "r", time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC)) |
|||
if err != nil { |
|||
t.Errorf("Activity.MarkRepositoryNotificationsRead returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_GetThread(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/notifications/threads/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{"id":"1"}`) |
|||
}) |
|||
|
|||
notification, _, err := client.Activity.GetThread("1") |
|||
if err != nil { |
|||
t.Errorf("Activity.GetThread returned error: %v", err) |
|||
} |
|||
|
|||
want := &Notification{ID: String("1")} |
|||
if !reflect.DeepEqual(notification, want) { |
|||
t.Errorf("Activity.GetThread returned %+v, want %+v", notification, want) |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_MarkThreadRead(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/notifications/threads/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "PATCH") |
|||
w.WriteHeader(http.StatusResetContent) |
|||
}) |
|||
|
|||
_, err := client.Activity.MarkThreadRead("1") |
|||
if err != nil { |
|||
t.Errorf("Activity.MarkThreadRead returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_GetThreadSubscription(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/notifications/threads/1/subscription", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{"subscribed":true}`) |
|||
}) |
|||
|
|||
sub, _, err := client.Activity.GetThreadSubscription("1") |
|||
if err != nil { |
|||
t.Errorf("Activity.GetThreadSubscription returned error: %v", err) |
|||
} |
|||
|
|||
want := &Subscription{Subscribed: Bool(true)} |
|||
if !reflect.DeepEqual(sub, want) { |
|||
t.Errorf("Activity.GetThreadSubscription returned %+v, want %+v", sub, want) |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_SetThreadSubscription(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &Subscription{Subscribed: Bool(true)} |
|||
|
|||
mux.HandleFunc("/notifications/threads/1/subscription", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(Subscription) |
|||
json.NewDecoder(r.Body).Decode(v) |
|||
|
|||
testMethod(t, r, "PUT") |
|||
if !reflect.DeepEqual(v, input) { |
|||
t.Errorf("Request body = %+v, want %+v", v, input) |
|||
} |
|||
|
|||
fmt.Fprint(w, `{"ignored":true}`) |
|||
}) |
|||
|
|||
sub, _, err := client.Activity.SetThreadSubscription("1", input) |
|||
if err != nil { |
|||
t.Errorf("Activity.SetThreadSubscription returned error: %v", err) |
|||
} |
|||
|
|||
want := &Subscription{Ignored: Bool(true)} |
|||
if !reflect.DeepEqual(sub, want) { |
|||
t.Errorf("Activity.SetThreadSubscription returned %+v, want %+v", sub, want) |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_DeleteThreadSubscription(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/notifications/threads/1/subscription", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
w.WriteHeader(http.StatusNoContent) |
|||
}) |
|||
|
|||
_, err := client.Activity.DeleteThreadSubscription("1") |
|||
if err != nil { |
|||
t.Errorf("Activity.DeleteThreadSubscription returned error: %v", err) |
|||
} |
|||
} |
@ -0,0 +1,132 @@ |
|||
// Copyright 2013 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 "fmt" |
|||
|
|||
// StarredRepository is returned by ListStarred.
|
|||
type StarredRepository struct { |
|||
StarredAt *Timestamp `json:"starred_at,omitempty"` |
|||
Repository *Repository `json:"repo,omitempty"` |
|||
} |
|||
|
|||
// Stargazer represents a user that has starred a repository.
|
|||
type Stargazer struct { |
|||
StarredAt *Timestamp `json:"starred_at,omitempty"` |
|||
User *User `json:"user,omitempty"` |
|||
} |
|||
|
|||
// ListStargazers lists people who have starred the specified repo.
|
|||
//
|
|||
// GitHub API Docs: https://developer.github.com/v3/activity/starring/#list-stargazers
|
|||
func (s *ActivityService) ListStargazers(owner, repo string, opt *ListOptions) ([]*Stargazer, *Response, error) { |
|||
u := fmt.Sprintf("repos/%s/%s/stargazers", owner, repo) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches
|
|||
req.Header.Set("Accept", mediaTypeStarringPreview) |
|||
|
|||
stargazers := new([]*Stargazer) |
|||
resp, err := s.client.Do(req, stargazers) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *stargazers, resp, err |
|||
} |
|||
|
|||
// ActivityListStarredOptions specifies the optional parameters to the
|
|||
// ActivityService.ListStarred method.
|
|||
type ActivityListStarredOptions struct { |
|||
// How to sort the repository list. Possible values are: created, updated,
|
|||
// pushed, full_name. Default is "full_name".
|
|||
Sort string `url:"sort,omitempty"` |
|||
|
|||
// Direction in which to sort repositories. Possible values are: asc, desc.
|
|||
// Default is "asc" when sort is "full_name", otherwise default is "desc".
|
|||
Direction string `url:"direction,omitempty"` |
|||
|
|||
ListOptions |
|||
} |
|||
|
|||
// ListStarred lists all the repos starred by a user. Passing the empty string
|
|||
// will list the starred repositories for the authenticated user.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/activity/starring/#list-repositories-being-starred
|
|||
func (s *ActivityService) ListStarred(user string, opt *ActivityListStarredOptions) ([]*StarredRepository, *Response, error) { |
|||
var u string |
|||
if user != "" { |
|||
u = fmt.Sprintf("users/%v/starred", user) |
|||
} else { |
|||
u = "user/starred" |
|||
} |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches
|
|||
req.Header.Set("Accept", mediaTypeStarringPreview) |
|||
|
|||
repos := new([]*StarredRepository) |
|||
resp, err := s.client.Do(req, repos) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *repos, resp, err |
|||
} |
|||
|
|||
// IsStarred checks if a repository is starred by authenticated user.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/activity/starring/#check-if-you-are-starring-a-repository
|
|||
func (s *ActivityService) IsStarred(owner, repo string) (bool, *Response, error) { |
|||
u := fmt.Sprintf("user/starred/%v/%v", owner, repo) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return false, nil, err |
|||
} |
|||
resp, err := s.client.Do(req, nil) |
|||
starred, err := parseBoolResponse(err) |
|||
return starred, resp, err |
|||
} |
|||
|
|||
// Star a repository as the authenticated user.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/activity/starring/#star-a-repository
|
|||
func (s *ActivityService) Star(owner, repo string) (*Response, error) { |
|||
u := fmt.Sprintf("user/starred/%v/%v", owner, repo) |
|||
req, err := s.client.NewRequest("PUT", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return s.client.Do(req, nil) |
|||
} |
|||
|
|||
// Unstar a repository as the authenticated user.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/activity/starring/#unstar-a-repository
|
|||
func (s *ActivityService) Unstar(owner, repo string) (*Response, error) { |
|||
u := fmt.Sprintf("user/starred/%v/%v", owner, repo) |
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return s.client.Do(req, nil) |
|||
} |
@ -0,0 +1,171 @@ |
|||
// Copyright 2013 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 ( |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
"time" |
|||
) |
|||
|
|||
func TestActivityService_ListStargazers(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/stargazers", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeStarringPreview) |
|||
testFormValues(t, r, values{ |
|||
"page": "2", |
|||
}) |
|||
|
|||
fmt.Fprint(w, `[{"starred_at":"2002-02-10T15:30:00Z","user":{"id":1}}]`) |
|||
}) |
|||
|
|||
stargazers, _, err := client.Activity.ListStargazers("o", "r", &ListOptions{Page: 2}) |
|||
if err != nil { |
|||
t.Errorf("Activity.ListStargazers returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Stargazer{{StarredAt: &Timestamp{time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC)}, User: &User{ID: Int(1)}}} |
|||
if !reflect.DeepEqual(stargazers, want) { |
|||
t.Errorf("Activity.ListStargazers returned %+v, want %+v", stargazers, want) |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_ListStarred_authenticatedUser(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/user/starred", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeStarringPreview) |
|||
fmt.Fprint(w, `[{"starred_at":"2002-02-10T15:30:00Z","repo":{"id":1}}]`) |
|||
}) |
|||
|
|||
repos, _, err := client.Activity.ListStarred("", nil) |
|||
if err != nil { |
|||
t.Errorf("Activity.ListStarred returned error: %v", err) |
|||
} |
|||
|
|||
want := []*StarredRepository{{StarredAt: &Timestamp{time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC)}, Repository: &Repository{ID: Int(1)}}} |
|||
if !reflect.DeepEqual(repos, want) { |
|||
t.Errorf("Activity.ListStarred returned %+v, want %+v", repos, want) |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_ListStarred_specifiedUser(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/users/u/starred", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeStarringPreview) |
|||
testFormValues(t, r, values{ |
|||
"sort": "created", |
|||
"direction": "asc", |
|||
"page": "2", |
|||
}) |
|||
fmt.Fprint(w, `[{"starred_at":"2002-02-10T15:30:00Z","repo":{"id":2}}]`) |
|||
}) |
|||
|
|||
opt := &ActivityListStarredOptions{"created", "asc", ListOptions{Page: 2}} |
|||
repos, _, err := client.Activity.ListStarred("u", opt) |
|||
if err != nil { |
|||
t.Errorf("Activity.ListStarred returned error: %v", err) |
|||
} |
|||
|
|||
want := []*StarredRepository{{StarredAt: &Timestamp{time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC)}, Repository: &Repository{ID: Int(2)}}} |
|||
if !reflect.DeepEqual(repos, want) { |
|||
t.Errorf("Activity.ListStarred returned %+v, want %+v", repos, want) |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_ListStarred_invalidUser(t *testing.T) { |
|||
_, _, err := client.Activity.ListStarred("%", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestActivityService_IsStarred_hasStar(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
w.WriteHeader(http.StatusNoContent) |
|||
}) |
|||
|
|||
star, _, err := client.Activity.IsStarred("o", "r") |
|||
if err != nil { |
|||
t.Errorf("Activity.IsStarred returned error: %v", err) |
|||
} |
|||
if want := true; star != want { |
|||
t.Errorf("Activity.IsStarred returned %+v, want %+v", star, want) |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_IsStarred_noStar(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
w.WriteHeader(http.StatusNotFound) |
|||
}) |
|||
|
|||
star, _, err := client.Activity.IsStarred("o", "r") |
|||
if err != nil { |
|||
t.Errorf("Activity.IsStarred returned error: %v", err) |
|||
} |
|||
if want := false; star != want { |
|||
t.Errorf("Activity.IsStarred returned %+v, want %+v", star, want) |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_IsStarred_invalidID(t *testing.T) { |
|||
_, _, err := client.Activity.IsStarred("%", "%") |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestActivityService_Star(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "PUT") |
|||
}) |
|||
|
|||
_, err := client.Activity.Star("o", "r") |
|||
if err != nil { |
|||
t.Errorf("Activity.Star returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_Star_invalidID(t *testing.T) { |
|||
_, err := client.Activity.Star("%", "%") |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestActivityService_Unstar(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
}) |
|||
|
|||
_, err := client.Activity.Unstar("o", "r") |
|||
if err != nil { |
|||
t.Errorf("Activity.Unstar returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_Unstar_invalidID(t *testing.T) { |
|||
_, err := client.Activity.Unstar("%", "%") |
|||
testURLParseError(t, err) |
|||
} |
@ -0,0 +1,128 @@ |
|||
// Copyright 2016 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 ( |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestActivityService_List(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/feeds", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
|
|||
w.WriteHeader(http.StatusOK) |
|||
w.Write(feedsJSON) |
|||
}) |
|||
|
|||
got, _, err := client.Activity.ListFeeds() |
|||
if err != nil { |
|||
t.Errorf("Activity.ListFeeds returned error: %v", err) |
|||
} |
|||
if want := wantFeeds; !reflect.DeepEqual(got, want) { |
|||
t.Errorf("Activity.ListFeeds = %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
var feedsJSON = []byte(`{ |
|||
"timeline_url": "https://github.com/timeline", |
|||
"user_url": "https://github.com/{user}", |
|||
"current_user_public_url": "https://github.com/defunkt", |
|||
"current_user_url": "https://github.com/defunkt.private?token=abc123", |
|||
"current_user_actor_url": "https://github.com/defunkt.private.actor?token=abc123", |
|||
"current_user_organization_url": "", |
|||
"current_user_organization_urls": [ |
|||
"https://github.com/organizations/github/defunkt.private.atom?token=abc123" |
|||
], |
|||
"_links": { |
|||
"timeline": { |
|||
"href": "https://github.com/timeline", |
|||
"type": "application/atom+xml" |
|||
}, |
|||
"user": { |
|||
"href": "https://github.com/{user}", |
|||
"type": "application/atom+xml" |
|||
}, |
|||
"current_user_public": { |
|||
"href": "https://github.com/defunkt", |
|||
"type": "application/atom+xml" |
|||
}, |
|||
"current_user": { |
|||
"href": "https://github.com/defunkt.private?token=abc123", |
|||
"type": "application/atom+xml" |
|||
}, |
|||
"current_user_actor": { |
|||
"href": "https://github.com/defunkt.private.actor?token=abc123", |
|||
"type": "application/atom+xml" |
|||
}, |
|||
"current_user_organization": { |
|||
"href": "", |
|||
"type": "" |
|||
}, |
|||
"current_user_organizations": [ |
|||
{ |
|||
"href": "https://github.com/organizations/github/defunkt.private.atom?token=abc123", |
|||
"type": "application/atom+xml" |
|||
} |
|||
] |
|||
} |
|||
}`) |
|||
|
|||
var wantFeeds = &Feeds{ |
|||
TimelineURL: String("https://github.com/timeline"), |
|||
UserURL: String("https://github.com/{user}"), |
|||
CurrentUserPublicURL: String("https://github.com/defunkt"), |
|||
CurrentUserURL: String("https://github.com/defunkt.private?token=abc123"), |
|||
CurrentUserActorURL: String("https://github.com/defunkt.private.actor?token=abc123"), |
|||
CurrentUserOrganizationURL: String(""), |
|||
CurrentUserOrganizationURLs: []string{ |
|||
"https://github.com/organizations/github/defunkt.private.atom?token=abc123", |
|||
}, |
|||
Links: &struct { |
|||
Timeline *FeedLink `json:"timeline,omitempty"` |
|||
User *FeedLink `json:"user,omitempty"` |
|||
CurrentUserPublic *FeedLink `json:"current_user_public,omitempty"` |
|||
CurrentUser *FeedLink `json:"current_user,omitempty"` |
|||
CurrentUserActor *FeedLink `json:"current_user_actor,omitempty"` |
|||
CurrentUserOrganization *FeedLink `json:"current_user_organization,omitempty"` |
|||
CurrentUserOrganizations []FeedLink `json:"current_user_organizations,omitempty"` |
|||
}{ |
|||
Timeline: &FeedLink{ |
|||
HRef: String("https://github.com/timeline"), |
|||
Type: String("application/atom+xml"), |
|||
}, |
|||
User: &FeedLink{ |
|||
HRef: String("https://github.com/{user}"), |
|||
Type: String("application/atom+xml"), |
|||
}, |
|||
CurrentUserPublic: &FeedLink{ |
|||
HRef: String("https://github.com/defunkt"), |
|||
Type: String("application/atom+xml"), |
|||
}, |
|||
CurrentUser: &FeedLink{ |
|||
HRef: String("https://github.com/defunkt.private?token=abc123"), |
|||
Type: String("application/atom+xml"), |
|||
}, |
|||
CurrentUserActor: &FeedLink{ |
|||
HRef: String("https://github.com/defunkt.private.actor?token=abc123"), |
|||
Type: String("application/atom+xml"), |
|||
}, |
|||
CurrentUserOrganization: &FeedLink{ |
|||
HRef: String(""), |
|||
Type: String(""), |
|||
}, |
|||
CurrentUserOrganizations: []FeedLink{ |
|||
{ |
|||
HRef: String("https://github.com/organizations/github/defunkt.private.atom?token=abc123"), |
|||
Type: String("application/atom+xml"), |
|||
}, |
|||
}, |
|||
}, |
|||
} |
@ -0,0 +1,136 @@ |
|||
// Copyright 2014 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 "fmt" |
|||
|
|||
// Subscription identifies a repository or thread subscription.
|
|||
type Subscription struct { |
|||
Subscribed *bool `json:"subscribed,omitempty"` |
|||
Ignored *bool `json:"ignored,omitempty"` |
|||
Reason *string `json:"reason,omitempty"` |
|||
CreatedAt *Timestamp `json:"created_at,omitempty"` |
|||
URL *string `json:"url,omitempty"` |
|||
|
|||
// only populated for repository subscriptions
|
|||
RepositoryURL *string `json:"repository_url,omitempty"` |
|||
|
|||
// only populated for thread subscriptions
|
|||
ThreadURL *string `json:"thread_url,omitempty"` |
|||
} |
|||
|
|||
// ListWatchers lists watchers of a particular repo.
|
|||
//
|
|||
// GitHub API Docs: http://developer.github.com/v3/activity/watching/#list-watchers
|
|||
func (s *ActivityService) ListWatchers(owner, repo string, opt *ListOptions) ([]*User, *Response, error) { |
|||
u := fmt.Sprintf("repos/%s/%s/subscribers", owner, repo) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
watchers := new([]*User) |
|||
resp, err := s.client.Do(req, watchers) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *watchers, resp, err |
|||
} |
|||
|
|||
// ListWatched lists the repositories the specified user is watching. Passing
|
|||
// the empty string will fetch watched repos for the authenticated user.
|
|||
//
|
|||
// GitHub API Docs: https://developer.github.com/v3/activity/watching/#list-repositories-being-watched
|
|||
func (s *ActivityService) ListWatched(user string, opt *ListOptions) ([]*Repository, *Response, error) { |
|||
var u string |
|||
if user != "" { |
|||
u = fmt.Sprintf("users/%v/subscriptions", user) |
|||
} else { |
|||
u = "user/subscriptions" |
|||
} |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
watched := new([]*Repository) |
|||
resp, err := s.client.Do(req, watched) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *watched, resp, err |
|||
} |
|||
|
|||
// GetRepositorySubscription returns the subscription for the specified
|
|||
// repository for the authenticated user. If the authenticated user is not
|
|||
// watching the repository, a nil Subscription is returned.
|
|||
//
|
|||
// GitHub API Docs: https://developer.github.com/v3/activity/watching/#get-a-repository-subscription
|
|||
func (s *ActivityService) GetRepositorySubscription(owner, repo string) (*Subscription, *Response, error) { |
|||
u := fmt.Sprintf("repos/%s/%s/subscription", owner, repo) |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
sub := new(Subscription) |
|||
resp, err := s.client.Do(req, sub) |
|||
if err != nil { |
|||
// if it's just a 404, don't return that as an error
|
|||
_, err = parseBoolResponse(err) |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return sub, resp, err |
|||
} |
|||
|
|||
// SetRepositorySubscription sets the subscription for the specified repository
|
|||
// for the authenticated user.
|
|||
//
|
|||
// GitHub API Docs: https://developer.github.com/v3/activity/watching/#set-a-repository-subscription
|
|||
func (s *ActivityService) SetRepositorySubscription(owner, repo string, subscription *Subscription) (*Subscription, *Response, error) { |
|||
u := fmt.Sprintf("repos/%s/%s/subscription", owner, repo) |
|||
|
|||
req, err := s.client.NewRequest("PUT", u, subscription) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
sub := new(Subscription) |
|||
resp, err := s.client.Do(req, sub) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return sub, resp, err |
|||
} |
|||
|
|||
// DeleteRepositorySubscription deletes the subscription for the specified
|
|||
// repository for the authenticated user.
|
|||
//
|
|||
// GitHub API Docs: https://developer.github.com/v3/activity/watching/#delete-a-repository-subscription
|
|||
func (s *ActivityService) DeleteRepositorySubscription(owner, repo string) (*Response, error) { |
|||
u := fmt.Sprintf("repos/%s/%s/subscription", owner, repo) |
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
return s.client.Do(req, nil) |
|||
} |
@ -0,0 +1,183 @@ |
|||
// Copyright 2014 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestActivityService_ListWatchers(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/subscribers", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{ |
|||
"page": "2", |
|||
}) |
|||
|
|||
fmt.Fprint(w, `[{"id":1}]`) |
|||
}) |
|||
|
|||
watchers, _, err := client.Activity.ListWatchers("o", "r", &ListOptions{Page: 2}) |
|||
if err != nil { |
|||
t.Errorf("Activity.ListWatchers returned error: %v", err) |
|||
} |
|||
|
|||
want := []*User{{ID: Int(1)}} |
|||
if !reflect.DeepEqual(watchers, want) { |
|||
t.Errorf("Activity.ListWatchers returned %+v, want %+v", watchers, want) |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_ListWatched_authenticatedUser(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/user/subscriptions", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{ |
|||
"page": "2", |
|||
}) |
|||
fmt.Fprint(w, `[{"id":1}]`) |
|||
}) |
|||
|
|||
watched, _, err := client.Activity.ListWatched("", &ListOptions{Page: 2}) |
|||
if err != nil { |
|||
t.Errorf("Activity.ListWatched returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Repository{{ID: Int(1)}} |
|||
if !reflect.DeepEqual(watched, want) { |
|||
t.Errorf("Activity.ListWatched returned %+v, want %+v", watched, want) |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_ListWatched_specifiedUser(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/users/u/subscriptions", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{ |
|||
"page": "2", |
|||
}) |
|||
fmt.Fprint(w, `[{"id":1}]`) |
|||
}) |
|||
|
|||
watched, _, err := client.Activity.ListWatched("u", &ListOptions{Page: 2}) |
|||
if err != nil { |
|||
t.Errorf("Activity.ListWatched returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Repository{{ID: Int(1)}} |
|||
if !reflect.DeepEqual(watched, want) { |
|||
t.Errorf("Activity.ListWatched returned %+v, want %+v", watched, want) |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_GetRepositorySubscription_true(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/subscription", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{"subscribed":true}`) |
|||
}) |
|||
|
|||
sub, _, err := client.Activity.GetRepositorySubscription("o", "r") |
|||
if err != nil { |
|||
t.Errorf("Activity.GetRepositorySubscription returned error: %v", err) |
|||
} |
|||
|
|||
want := &Subscription{Subscribed: Bool(true)} |
|||
if !reflect.DeepEqual(sub, want) { |
|||
t.Errorf("Activity.GetRepositorySubscription returned %+v, want %+v", sub, want) |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_GetRepositorySubscription_false(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/subscription", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
w.WriteHeader(http.StatusNotFound) |
|||
}) |
|||
|
|||
sub, _, err := client.Activity.GetRepositorySubscription("o", "r") |
|||
if err != nil { |
|||
t.Errorf("Activity.GetRepositorySubscription returned error: %v", err) |
|||
} |
|||
|
|||
var want *Subscription |
|||
if !reflect.DeepEqual(sub, want) { |
|||
t.Errorf("Activity.GetRepositorySubscription returned %+v, want %+v", sub, want) |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_GetRepositorySubscription_error(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/subscription", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
w.WriteHeader(http.StatusBadRequest) |
|||
}) |
|||
|
|||
_, _, err := client.Activity.GetRepositorySubscription("o", "r") |
|||
if err == nil { |
|||
t.Errorf("Expected HTTP 400 response") |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_SetRepositorySubscription(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &Subscription{Subscribed: Bool(true)} |
|||
|
|||
mux.HandleFunc("/repos/o/r/subscription", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(Subscription) |
|||
json.NewDecoder(r.Body).Decode(v) |
|||
|
|||
testMethod(t, r, "PUT") |
|||
if !reflect.DeepEqual(v, input) { |
|||
t.Errorf("Request body = %+v, want %+v", v, input) |
|||
} |
|||
|
|||
fmt.Fprint(w, `{"ignored":true}`) |
|||
}) |
|||
|
|||
sub, _, err := client.Activity.SetRepositorySubscription("o", "r", input) |
|||
if err != nil { |
|||
t.Errorf("Activity.SetRepositorySubscription returned error: %v", err) |
|||
} |
|||
|
|||
want := &Subscription{Ignored: Bool(true)} |
|||
if !reflect.DeepEqual(sub, want) { |
|||
t.Errorf("Activity.SetRepositorySubscription returned %+v, want %+v", sub, want) |
|||
} |
|||
} |
|||
|
|||
func TestActivityService_DeleteRepositorySubscription(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/subscription", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
w.WriteHeader(http.StatusNoContent) |
|||
}) |
|||
|
|||
_, err := client.Activity.DeleteRepositorySubscription("o", "r") |
|||
if err != nil { |
|||
t.Errorf("Activity.DeleteRepositorySubscription returned error: %v", err) |
|||
} |
|||
} |
@ -0,0 +1,436 @@ |
|||
// Copyright 2015 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 "fmt" |
|||
|
|||
// Scope models a GitHub authorization scope.
|
|||
//
|
|||
// GitHub API docs:https://developer.github.com/v3/oauth/#scopes
|
|||
type Scope string |
|||
|
|||
// This is the set of scopes for GitHub API V3
|
|||
const ( |
|||
ScopeNone Scope = "(no scope)" // REVISIT: is this actually returned, or just a documentation artifact?
|
|||
ScopeUser Scope = "user" |
|||
ScopeUserEmail Scope = "user:email" |
|||
ScopeUserFollow Scope = "user:follow" |
|||
ScopePublicRepo Scope = "public_repo" |
|||
ScopeRepo Scope = "repo" |
|||
ScopeRepoDeployment Scope = "repo_deployment" |
|||
ScopeRepoStatus Scope = "repo:status" |
|||
ScopeDeleteRepo Scope = "delete_repo" |
|||
ScopeNotifications Scope = "notifications" |
|||
ScopeGist Scope = "gist" |
|||
ScopeReadRepoHook Scope = "read:repo_hook" |
|||
ScopeWriteRepoHook Scope = "write:repo_hook" |
|||
ScopeAdminRepoHook Scope = "admin:repo_hook" |
|||
ScopeAdminOrgHook Scope = "admin:org_hook" |
|||
ScopeReadOrg Scope = "read:org" |
|||
ScopeWriteOrg Scope = "write:org" |
|||
ScopeAdminOrg Scope = "admin:org" |
|||
ScopeReadPublicKey Scope = "read:public_key" |
|||
ScopeWritePublicKey Scope = "write:public_key" |
|||
ScopeAdminPublicKey Scope = "admin:public_key" |
|||
ScopeReadGPGKey Scope = "read:gpg_key" |
|||
ScopeWriteGPGKey Scope = "write:gpg_key" |
|||
ScopeAdminGPGKey Scope = "admin:gpg_key" |
|||
) |
|||
|
|||
// AuthorizationsService handles communication with the authorization related
|
|||
// methods of the GitHub API.
|
|||
//
|
|||
// This service requires HTTP Basic Authentication; it cannot be accessed using
|
|||
// an OAuth token.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/
|
|||
type AuthorizationsService service |
|||
|
|||
// Authorization represents an individual GitHub authorization.
|
|||
type Authorization struct { |
|||
ID *int `json:"id,omitempty"` |
|||
URL *string `json:"url,omitempty"` |
|||
Scopes []Scope `json:"scopes,omitempty"` |
|||
Token *string `json:"token,omitempty"` |
|||
TokenLastEight *string `json:"token_last_eight,omitempty"` |
|||
HashedToken *string `json:"hashed_token,omitempty"` |
|||
App *AuthorizationApp `json:"app,omitempty"` |
|||
Note *string `json:"note,omitempty"` |
|||
NoteURL *string `json:"note_url,omitempty"` |
|||
UpdateAt *Timestamp `json:"updated_at,omitempty"` |
|||
CreatedAt *Timestamp `json:"created_at,omitempty"` |
|||
Fingerprint *string `json:"fingerprint,omitempty"` |
|||
|
|||
// User is only populated by the Check and Reset methods.
|
|||
User *User `json:"user,omitempty"` |
|||
} |
|||
|
|||
func (a Authorization) String() string { |
|||
return Stringify(a) |
|||
} |
|||
|
|||
// AuthorizationApp represents an individual GitHub app (in the context of authorization).
|
|||
type AuthorizationApp struct { |
|||
URL *string `json:"url,omitempty"` |
|||
Name *string `json:"name,omitempty"` |
|||
ClientID *string `json:"client_id,omitempty"` |
|||
} |
|||
|
|||
func (a AuthorizationApp) String() string { |
|||
return Stringify(a) |
|||
} |
|||
|
|||
// Grant represents an OAuth application that has been granted access to an account.
|
|||
type Grant struct { |
|||
ID *int `json:"id,omitempty"` |
|||
URL *string `json:"url,omitempty"` |
|||
App *AuthorizationApp `json:"app,omitempty"` |
|||
CreatedAt *Timestamp `json:"created_at,omitempty"` |
|||
UpdatedAt *Timestamp `json:"updated_at,omitempty"` |
|||
Scopes []string `json:"scopes,omitempty"` |
|||
} |
|||
|
|||
func (g Grant) String() string { |
|||
return Stringify(g) |
|||
} |
|||
|
|||
// AuthorizationRequest represents a request to create an authorization.
|
|||
type AuthorizationRequest struct { |
|||
Scopes []Scope `json:"scopes,omitempty"` |
|||
Note *string `json:"note,omitempty"` |
|||
NoteURL *string `json:"note_url,omitempty"` |
|||
ClientID *string `json:"client_id,omitempty"` |
|||
ClientSecret *string `json:"client_secret,omitempty"` |
|||
Fingerprint *string `json:"fingerprint,omitempty"` |
|||
} |
|||
|
|||
func (a AuthorizationRequest) String() string { |
|||
return Stringify(a) |
|||
} |
|||
|
|||
// AuthorizationUpdateRequest represents a request to update an authorization.
|
|||
//
|
|||
// Note that for any one update, you must only provide one of the "scopes"
|
|||
// fields. That is, you may provide only one of "Scopes", or "AddScopes", or
|
|||
// "RemoveScopes".
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#update-an-existing-authorization
|
|||
type AuthorizationUpdateRequest struct { |
|||
Scopes []string `json:"scopes,omitempty"` |
|||
AddScopes []string `json:"add_scopes,omitempty"` |
|||
RemoveScopes []string `json:"remove_scopes,omitempty"` |
|||
Note *string `json:"note,omitempty"` |
|||
NoteURL *string `json:"note_url,omitempty"` |
|||
Fingerprint *string `json:"fingerprint,omitempty"` |
|||
} |
|||
|
|||
func (a AuthorizationUpdateRequest) String() string { |
|||
return Stringify(a) |
|||
} |
|||
|
|||
// List the authorizations for the authenticated user.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#list-your-authorizations
|
|||
func (s *AuthorizationsService) List(opt *ListOptions) ([]*Authorization, *Response, error) { |
|||
u := "authorizations" |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
auths := new([]*Authorization) |
|||
resp, err := s.client.Do(req, auths) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
return *auths, resp, err |
|||
} |
|||
|
|||
// Get a single authorization.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#get-a-single-authorization
|
|||
func (s *AuthorizationsService) Get(id int) (*Authorization, *Response, error) { |
|||
u := fmt.Sprintf("authorizations/%d", id) |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
a := new(Authorization) |
|||
resp, err := s.client.Do(req, a) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
return a, resp, err |
|||
} |
|||
|
|||
// Create a new authorization for the specified OAuth application.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#create-a-new-authorization
|
|||
func (s *AuthorizationsService) Create(auth *AuthorizationRequest) (*Authorization, *Response, error) { |
|||
u := "authorizations" |
|||
|
|||
req, err := s.client.NewRequest("POST", u, auth) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
a := new(Authorization) |
|||
resp, err := s.client.Do(req, a) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
return a, resp, err |
|||
} |
|||
|
|||
// GetOrCreateForApp creates a new authorization for the specified OAuth
|
|||
// application, only if an authorization for that application doesn’t already
|
|||
// exist for the user.
|
|||
//
|
|||
// If a new token is created, the HTTP status code will be "201 Created", and
|
|||
// the returned Authorization.Token field will be populated. If an existing
|
|||
// token is returned, the status code will be "200 OK" and the
|
|||
// Authorization.Token field will be empty.
|
|||
//
|
|||
// clientID is the OAuth Client ID with which to create the token.
|
|||
//
|
|||
// GitHub API docs:
|
|||
// - https://developer.github.com/v3/oauth_authorizations/#get-or-create-an-authorization-for-a-specific-app
|
|||
// - https://developer.github.com/v3/oauth_authorizations/#get-or-create-an-authorization-for-a-specific-app-and-fingerprint
|
|||
func (s *AuthorizationsService) GetOrCreateForApp(clientID string, auth *AuthorizationRequest) (*Authorization, *Response, error) { |
|||
var u string |
|||
if auth.Fingerprint == nil || *auth.Fingerprint == "" { |
|||
u = fmt.Sprintf("authorizations/clients/%v", clientID) |
|||
} else { |
|||
u = fmt.Sprintf("authorizations/clients/%v/%v", clientID, *auth.Fingerprint) |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("PUT", u, auth) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
a := new(Authorization) |
|||
resp, err := s.client.Do(req, a) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return a, resp, err |
|||
} |
|||
|
|||
// Edit a single authorization.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#update-an-existing-authorization
|
|||
func (s *AuthorizationsService) Edit(id int, auth *AuthorizationUpdateRequest) (*Authorization, *Response, error) { |
|||
u := fmt.Sprintf("authorizations/%d", id) |
|||
|
|||
req, err := s.client.NewRequest("PATCH", u, auth) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
a := new(Authorization) |
|||
resp, err := s.client.Do(req, a) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return a, resp, err |
|||
} |
|||
|
|||
// Delete a single authorization.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#delete-an-authorization
|
|||
func (s *AuthorizationsService) Delete(id int) (*Response, error) { |
|||
u := fmt.Sprintf("authorizations/%d", id) |
|||
|
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
return s.client.Do(req, nil) |
|||
} |
|||
|
|||
// Check if an OAuth token is valid for a specific app.
|
|||
//
|
|||
// Note that this operation requires the use of BasicAuth, but where the
|
|||
// username is the OAuth application clientID, and the password is its
|
|||
// clientSecret. Invalid tokens will return a 404 Not Found.
|
|||
//
|
|||
// The returned Authorization.User field will be populated.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#check-an-authorization
|
|||
func (s *AuthorizationsService) Check(clientID string, token string) (*Authorization, *Response, error) { |
|||
u := fmt.Sprintf("applications/%v/tokens/%v", clientID, token) |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
a := new(Authorization) |
|||
resp, err := s.client.Do(req, a) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return a, resp, err |
|||
} |
|||
|
|||
// Reset is used to reset a valid OAuth token without end user involvement.
|
|||
// Applications must save the "token" property in the response, because changes
|
|||
// take effect immediately.
|
|||
//
|
|||
// Note that this operation requires the use of BasicAuth, but where the
|
|||
// username is the OAuth application clientID, and the password is its
|
|||
// clientSecret. Invalid tokens will return a 404 Not Found.
|
|||
//
|
|||
// The returned Authorization.User field will be populated.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#reset-an-authorization
|
|||
func (s *AuthorizationsService) Reset(clientID string, token string) (*Authorization, *Response, error) { |
|||
u := fmt.Sprintf("applications/%v/tokens/%v", clientID, token) |
|||
|
|||
req, err := s.client.NewRequest("POST", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
a := new(Authorization) |
|||
resp, err := s.client.Do(req, a) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return a, resp, err |
|||
} |
|||
|
|||
// Revoke an authorization for an application.
|
|||
//
|
|||
// Note that this operation requires the use of BasicAuth, but where the
|
|||
// username is the OAuth application clientID, and the password is its
|
|||
// clientSecret. Invalid tokens will return a 404 Not Found.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#revoke-an-authorization-for-an-application
|
|||
func (s *AuthorizationsService) Revoke(clientID string, token string) (*Response, error) { |
|||
u := fmt.Sprintf("applications/%v/tokens/%v", clientID, token) |
|||
|
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
return s.client.Do(req, nil) |
|||
} |
|||
|
|||
// ListGrants lists the set of OAuth applications that have been granted
|
|||
// access to a user's account. This will return one entry for each application
|
|||
// that has been granted access to the account, regardless of the number of
|
|||
// tokens an application has generated for the user.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#list-your-grants
|
|||
func (s *AuthorizationsService) ListGrants() ([]*Grant, *Response, error) { |
|||
req, err := s.client.NewRequest("GET", "applications/grants", nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeOAuthGrantAuthorizationsPreview) |
|||
|
|||
grants := []*Grant{} |
|||
resp, err := s.client.Do(req, &grants) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return grants, resp, err |
|||
} |
|||
|
|||
// GetGrant gets a single OAuth application grant.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#get-a-single-grant
|
|||
func (s *AuthorizationsService) GetGrant(id int) (*Grant, *Response, error) { |
|||
u := fmt.Sprintf("applications/grants/%d", id) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeOAuthGrantAuthorizationsPreview) |
|||
|
|||
grant := new(Grant) |
|||
resp, err := s.client.Do(req, grant) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return grant, resp, err |
|||
} |
|||
|
|||
// DeleteGrant deletes an OAuth application grant. Deleting an application's
|
|||
// grant will also delete all OAuth tokens associated with the application for
|
|||
// the user.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#delete-a-grant
|
|||
func (s *AuthorizationsService) DeleteGrant(id int) (*Response, error) { |
|||
u := fmt.Sprintf("applications/grants/%d", id) |
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeOAuthGrantAuthorizationsPreview) |
|||
|
|||
return s.client.Do(req, nil) |
|||
} |
|||
|
|||
// Create an impersonation OAuth token.
|
|||
//
|
|||
// This requires admin permissions. With the returned Authorization.Token
|
|||
// you can e.g. create or delete a user's public SSH key. NOTE: creating a
|
|||
// new token automatically revokes an existing one.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/enterprise/2.5/v3/users/administration/#create-an-impersonation-oauth-token
|
|||
func (s *AuthorizationsService) CreateImpersonation(username string, authReq *AuthorizationRequest) (*Authorization, *Response, error) { |
|||
u := fmt.Sprintf("admin/users/%v/authorizations", username) |
|||
req, err := s.client.NewRequest("POST", u, authReq) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
a := new(Authorization) |
|||
resp, err := s.client.Do(req, a) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
return a, resp, err |
|||
} |
|||
|
|||
// Delete an impersonation OAuth token.
|
|||
//
|
|||
// NOTE: there can be only one at a time.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/enterprise/2.5/v3/users/administration/#delete-an-impersonation-oauth-token
|
|||
func (s *AuthorizationsService) DeleteImpersonation(username string) (*Response, error) { |
|||
u := fmt.Sprintf("admin/users/%v/authorizations", username) |
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
return s.client.Do(req, nil) |
|||
} |
@ -0,0 +1,343 @@ |
|||
// Copyright 2015 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestAuthorizationsService_List(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/authorizations", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{"page": "1", "per_page": "2"}) |
|||
fmt.Fprint(w, `[{"id":1}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 1, PerPage: 2} |
|||
got, _, err := client.Authorizations.List(opt) |
|||
if err != nil { |
|||
t.Errorf("Authorizations.List returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Authorization{{ID: Int(1)}} |
|||
if !reflect.DeepEqual(got, want) { |
|||
t.Errorf("Authorizations.List returned %+v, want %+v", *got[0].ID, *want[0].ID) |
|||
} |
|||
} |
|||
|
|||
func TestAuthorizationsService_Get(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/authorizations/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{"id":1}`) |
|||
}) |
|||
|
|||
got, _, err := client.Authorizations.Get(1) |
|||
if err != nil { |
|||
t.Errorf("Authorizations.Get returned error: %v", err) |
|||
} |
|||
|
|||
want := &Authorization{ID: Int(1)} |
|||
if !reflect.DeepEqual(got, want) { |
|||
t.Errorf("Authorizations.Get returned auth %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestAuthorizationsService_Create(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &AuthorizationRequest{ |
|||
Note: String("test"), |
|||
} |
|||
|
|||
mux.HandleFunc("/authorizations", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(AuthorizationRequest) |
|||
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}`) |
|||
}) |
|||
|
|||
got, _, err := client.Authorizations.Create(input) |
|||
if err != nil { |
|||
t.Errorf("Authorizations.Create returned error: %v", err) |
|||
} |
|||
|
|||
want := &Authorization{ID: Int(1)} |
|||
if !reflect.DeepEqual(got, want) { |
|||
t.Errorf("Authorization.Create returned %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestAuthorizationsService_GetOrCreateForApp(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &AuthorizationRequest{ |
|||
Note: String("test"), |
|||
} |
|||
|
|||
mux.HandleFunc("/authorizations/clients/id", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(AuthorizationRequest) |
|||
json.NewDecoder(r.Body).Decode(v) |
|||
|
|||
testMethod(t, r, "PUT") |
|||
if !reflect.DeepEqual(v, input) { |
|||
t.Errorf("Request body = %+v, want %+v", v, input) |
|||
} |
|||
|
|||
fmt.Fprint(w, `{"ID":1}`) |
|||
}) |
|||
|
|||
got, _, err := client.Authorizations.GetOrCreateForApp("id", input) |
|||
if err != nil { |
|||
t.Errorf("Authorizations.GetOrCreateForApp returned error: %v", err) |
|||
} |
|||
|
|||
want := &Authorization{ID: Int(1)} |
|||
if !reflect.DeepEqual(got, want) { |
|||
t.Errorf("Authorization.GetOrCreateForApp returned %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestAuthorizationsService_GetOrCreateForApp_Fingerprint(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &AuthorizationRequest{ |
|||
Note: String("test"), |
|||
Fingerprint: String("fp"), |
|||
} |
|||
|
|||
mux.HandleFunc("/authorizations/clients/id/fp", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(AuthorizationRequest) |
|||
json.NewDecoder(r.Body).Decode(v) |
|||
|
|||
testMethod(t, r, "PUT") |
|||
if !reflect.DeepEqual(v, input) { |
|||
t.Errorf("Request body = %+v, want %+v", v, input) |
|||
} |
|||
|
|||
fmt.Fprint(w, `{"ID":1}`) |
|||
}) |
|||
|
|||
got, _, err := client.Authorizations.GetOrCreateForApp("id", input) |
|||
if err != nil { |
|||
t.Errorf("Authorizations.GetOrCreateForApp returned error: %v", err) |
|||
} |
|||
|
|||
want := &Authorization{ID: Int(1)} |
|||
if !reflect.DeepEqual(got, want) { |
|||
t.Errorf("Authorization.GetOrCreateForApp returned %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestAuthorizationsService_Edit(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &AuthorizationUpdateRequest{ |
|||
Note: String("test"), |
|||
} |
|||
|
|||
mux.HandleFunc("/authorizations/1", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(AuthorizationUpdateRequest) |
|||
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}`) |
|||
}) |
|||
|
|||
got, _, err := client.Authorizations.Edit(1, input) |
|||
if err != nil { |
|||
t.Errorf("Authorizations.Edit returned error: %v", err) |
|||
} |
|||
|
|||
want := &Authorization{ID: Int(1)} |
|||
if !reflect.DeepEqual(got, want) { |
|||
t.Errorf("Authorization.Update returned %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestAuthorizationsService_Delete(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/authorizations/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
w.WriteHeader(http.StatusNoContent) |
|||
}) |
|||
|
|||
_, err := client.Authorizations.Delete(1) |
|||
if err != nil { |
|||
t.Errorf("Authorizations.Delete returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestAuthorizationsService_Check(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/applications/id/tokens/t", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{"id":1}`) |
|||
}) |
|||
|
|||
got, _, err := client.Authorizations.Check("id", "t") |
|||
if err != nil { |
|||
t.Errorf("Authorizations.Check returned error: %v", err) |
|||
} |
|||
|
|||
want := &Authorization{ID: Int(1)} |
|||
if !reflect.DeepEqual(got, want) { |
|||
t.Errorf("Authorizations.Check returned auth %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestAuthorizationsService_Reset(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/applications/id/tokens/t", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "POST") |
|||
fmt.Fprint(w, `{"ID":1}`) |
|||
}) |
|||
|
|||
got, _, err := client.Authorizations.Reset("id", "t") |
|||
if err != nil { |
|||
t.Errorf("Authorizations.Reset returned error: %v", err) |
|||
} |
|||
|
|||
want := &Authorization{ID: Int(1)} |
|||
if !reflect.DeepEqual(got, want) { |
|||
t.Errorf("Authorizations.Reset returned auth %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestAuthorizationsService_Revoke(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/applications/id/tokens/t", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
w.WriteHeader(http.StatusNoContent) |
|||
}) |
|||
|
|||
_, err := client.Authorizations.Revoke("id", "t") |
|||
if err != nil { |
|||
t.Errorf("Authorizations.Revoke returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestListGrants(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/applications/grants", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeOAuthGrantAuthorizationsPreview) |
|||
fmt.Fprint(w, `[{"id": 1}]`) |
|||
}) |
|||
|
|||
got, _, err := client.Authorizations.ListGrants() |
|||
if err != nil { |
|||
t.Errorf("OAuthAuthorizations.ListGrants returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Grant{{ID: Int(1)}} |
|||
if !reflect.DeepEqual(got, want) { |
|||
t.Errorf("OAuthAuthorizations.ListGrants = %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestGetGrant(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/applications/grants/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeOAuthGrantAuthorizationsPreview) |
|||
fmt.Fprint(w, `{"id": 1}`) |
|||
}) |
|||
|
|||
got, _, err := client.Authorizations.GetGrant(1) |
|||
if err != nil { |
|||
t.Errorf("OAuthAuthorizations.GetGrant returned error: %v", err) |
|||
} |
|||
|
|||
want := &Grant{ID: Int(1)} |
|||
if !reflect.DeepEqual(got, want) { |
|||
t.Errorf("OAuthAuthorizations.GetGrant = %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestDeleteGrant(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/applications/grants/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
testHeader(t, r, "Accept", mediaTypeOAuthGrantAuthorizationsPreview) |
|||
}) |
|||
|
|||
_, err := client.Authorizations.DeleteGrant(1) |
|||
if err != nil { |
|||
t.Errorf("OAuthAuthorizations.DeleteGrant returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestAuthorizationsService_CreateImpersonation(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/admin/users/u/authorizations", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "POST") |
|||
fmt.Fprint(w, `{"id":1}`) |
|||
}) |
|||
|
|||
req := &AuthorizationRequest{Scopes: []Scope{ScopePublicRepo}} |
|||
got, _, err := client.Authorizations.CreateImpersonation("u", req) |
|||
if err != nil { |
|||
t.Errorf("Authorizations.CreateImpersonation returned error: %+v", err) |
|||
} |
|||
|
|||
want := &Authorization{ID: Int(1)} |
|||
if !reflect.DeepEqual(got, want) { |
|||
t.Errorf("Authorizations.CreateImpersonation returned %+v, want %+v", *got.ID, *want.ID) |
|||
} |
|||
} |
|||
|
|||
func TestAuthorizationsService_DeleteImpersonation(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/admin/users/u/authorizations", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
}) |
|||
|
|||
_, err := client.Authorizations.DeleteImpersonation("u") |
|||
if err != nil { |
|||
t.Errorf("Authorizations.DeleteImpersonation returned error: %+v", err) |
|||
} |
|||
} |
@ -0,0 +1,145 @@ |
|||
// Copyright 2013 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 provides a client for using the GitHub API. |
|||
|
|||
Usage: |
|||
|
|||
import "github.com/google/go-github/github" |
|||
|
|||
Construct a new GitHub client, then use the various services on the client to |
|||
access different parts of the GitHub API. For example: |
|||
|
|||
client := github.NewClient(nil) |
|||
|
|||
// list all organizations for user "willnorris"
|
|||
orgs, _, err := client.Organizations.List("willnorris", nil) |
|||
|
|||
Some API methods have optional parameters that can be passed. For example: |
|||
|
|||
client := github.NewClient(nil) |
|||
|
|||
// list recently updated repositories for org "github"
|
|||
opt := &github.RepositoryListByOrgOptions{Sort: "updated"} |
|||
repos, _, err := client.Repositories.ListByOrg("github", opt) |
|||
|
|||
The services of a client divide the API into logical chunks and correspond to |
|||
the structure of the GitHub API documentation at |
|||
http://developer.github.com/v3/.
|
|||
|
|||
Authentication |
|||
|
|||
The go-github library does not directly handle authentication. Instead, when |
|||
creating a new client, pass an http.Client that can handle authentication for |
|||
you. The easiest and recommended way to do this is using the golang.org/x/oauth2 |
|||
library, but you can always use any other library that provides an http.Client. |
|||
If you have an OAuth2 access token (for example, a personal API token), you can |
|||
use it with the oauth2 library using: |
|||
|
|||
import "golang.org/x/oauth2" |
|||
|
|||
func main() { |
|||
ts := oauth2.StaticTokenSource( |
|||
&oauth2.Token{AccessToken: "... your access token ..."}, |
|||
) |
|||
tc := oauth2.NewClient(oauth2.NoContext, ts) |
|||
|
|||
client := github.NewClient(tc) |
|||
|
|||
// list all repositories for the authenticated user
|
|||
repos, _, err := client.Repositories.List("", nil) |
|||
} |
|||
|
|||
Note that when using an authenticated Client, all calls made by the client will |
|||
include the specified OAuth token. Therefore, authenticated clients should |
|||
almost never be shared between different users. |
|||
|
|||
See the oauth2 docs for complete instructions on using that library. |
|||
|
|||
For API methods that require HTTP Basic Authentication, use the |
|||
BasicAuthTransport. |
|||
|
|||
Rate Limiting |
|||
|
|||
GitHub imposes a rate limit on all API clients. Unauthenticated clients are |
|||
limited to 60 requests per hour, while authenticated clients can make up to |
|||
5,000 requests per hour. To receive the higher rate limit when making calls |
|||
that are not issued on behalf of a user, use the |
|||
UnauthenticatedRateLimitedTransport. |
|||
|
|||
The Rate method on a client returns the rate limit information based on the most |
|||
recent API call. This is updated on every call, but may be out of date if it's |
|||
been some time since the last API call and other clients have made subsequent |
|||
requests since then. You can always call RateLimits() directly to get the most |
|||
up-to-date rate limit data for the client. |
|||
|
|||
To detect an API rate limit error, you can check if its type is *github.RateLimitError: |
|||
|
|||
repos, _, err := client.Repositories.List("", nil) |
|||
if _, ok := err.(*github.RateLimitError); ok { |
|||
log.Println("hit rate limit") |
|||
} |
|||
|
|||
Learn more about GitHub rate limiting at |
|||
http://developer.github.com/v3/#rate-limiting.
|
|||
|
|||
Conditional Requests |
|||
|
|||
The GitHub API has good support for conditional requests which will help |
|||
prevent you from burning through your rate limit, as well as help speed up your |
|||
application. go-github does not handle conditional requests directly, but is |
|||
instead designed to work with a caching http.Transport. We recommend using |
|||
https://github.com/gregjones/httpcache for that.
|
|||
|
|||
Learn more about GitHub conditional requests at |
|||
https://developer.github.com/v3/#conditional-requests.
|
|||
|
|||
Creating and Updating Resources |
|||
|
|||
All structs for GitHub resources use pointer values for all non-repeated fields. |
|||
This allows distinguishing between unset fields and those set to a zero-value. |
|||
Helper functions have been provided to easily create these pointers for string, |
|||
bool, and int values. For example: |
|||
|
|||
// create a new private repository named "foo"
|
|||
repo := &github.Repository{ |
|||
Name: github.String("foo"), |
|||
Private: github.Bool(true), |
|||
} |
|||
client.Repositories.Create("", repo) |
|||
|
|||
Users who have worked with protocol buffers should find this pattern familiar. |
|||
|
|||
Pagination |
|||
|
|||
All requests for resource collections (repos, pull requests, issues, etc.) |
|||
support pagination. Pagination options are described in the |
|||
github.ListOptions struct and passed to the list methods directly or as an |
|||
embedded type of a more specific list options struct (for example |
|||
github.PullRequestListOptions). Pages information is available via the |
|||
github.Response struct. |
|||
|
|||
client := github.NewClient(nil) |
|||
|
|||
opt := &github.RepositoryListByOrgOptions{ |
|||
ListOptions: github.ListOptions{PerPage: 10}, |
|||
} |
|||
// get all pages of results
|
|||
var allRepos []*github.Repository |
|||
for { |
|||
repos, resp, err := client.Repositories.ListByOrg("github", opt) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
allRepos = append(allRepos, repos...) |
|||
if resp.NextPage == 0 { |
|||
break |
|||
} |
|||
opt.ListOptions.Page = resp.NextPage |
|||
} |
|||
|
|||
*/ |
|||
package github |
@ -0,0 +1,467 @@ |
|||
// Copyright 2016 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.
|
|||
|
|||
// These event types are shared between the Events API and used as Webhook payloads.
|
|||
|
|||
package github |
|||
|
|||
// CommitCommentEvent is triggered when a commit comment is created.
|
|||
// The Webhook event name is "commit_comment".
|
|||
//
|
|||
// GitHub docs: https://developer.github.com/v3/activity/events/types/#commitcommentevent
|
|||
type CommitCommentEvent struct { |
|||
Comment *RepositoryComment `json:"comment,omitempty"` |
|||
|
|||
// The following fields are only populated by Webhook events.
|
|||
Action *string `json:"action,omitempty"` |
|||
Repo *Repository `json:"repository,omitempty"` |
|||
Sender *User `json:"sender,omitempty"` |
|||
} |
|||
|
|||
// CreateEvent represents a created repository, branch, or tag.
|
|||
// The Webhook event name is "create".
|
|||
//
|
|||
// Note: webhooks will not receive this event for created repositories.
|
|||
// Additionally, webhooks will not receive this event for tags if more
|
|||
// than three tags are pushed at once.
|
|||
//
|
|||
// GitHub docs: https://developer.github.com/v3/activity/events/types/#createevent
|
|||
type CreateEvent struct { |
|||
Ref *string `json:"ref,omitempty"` |
|||
// RefType is the object that was created. Possible values are: "repository", "branch", "tag".
|
|||
RefType *string `json:"ref_type,omitempty"` |
|||
MasterBranch *string `json:"master_branch,omitempty"` |
|||
Description *string `json:"description,omitempty"` |
|||
|
|||
// The following fields are only populated by Webhook events.
|
|||
PusherType *string `json:"pusher_type,omitempty"` |
|||
Repo *Repository `json:"repository,omitempty"` |
|||
Sender *User `json:"sender,omitempty"` |
|||
} |
|||
|
|||
// DeleteEvent represents a deleted branch or tag.
|
|||
// The Webhook event name is "delete".
|
|||
//
|
|||
// Note: webhooks will not receive this event for tags if more than three tags
|
|||
// are deleted at once.
|
|||
//
|
|||
// GitHub docs: https://developer.github.com/v3/activity/events/types/#deleteevent
|
|||
type DeleteEvent struct { |
|||
Ref *string `json:"ref,omitempty"` |
|||
// RefType is the object that was deleted. Possible values are: "branch", "tag".
|
|||
RefType *string `json:"ref_type,omitempty"` |
|||
|
|||
// The following fields are only populated by Webhook events.
|
|||
PusherType *string `json:"pusher_type,omitempty"` |
|||
Repo *Repository `json:"repository,omitempty"` |
|||
Sender *User `json:"sender,omitempty"` |
|||
} |
|||
|
|||
// DeploymentEvent represents a deployment.
|
|||
// The Webhook event name is "deployment".
|
|||
//
|
|||
// Events of this type are not visible in timelines, they are only used to trigger hooks.
|
|||
//
|
|||
// GitHub docs: https://developer.github.com/v3/activity/events/types/#deploymentevent
|
|||
type DeploymentEvent struct { |
|||
Deployment *Deployment `json:"deployment,omitempty"` |
|||
Repo *Repository `json:"repository,omitempty"` |
|||
|
|||
// The following fields are only populated by Webhook events.
|
|||
Sender *User `json:"sender,omitempty"` |
|||
} |
|||
|
|||
// DeploymentStatusEvent represents a deployment status.
|
|||
// The Webhook event name is "deployment_status".
|
|||
//
|
|||
// Events of this type are not visible in timelines, they are only used to trigger hooks.
|
|||
//
|
|||
// GitHub docs: https://developer.github.com/v3/activity/events/types/#deploymentstatusevent
|
|||
type DeploymentStatusEvent struct { |
|||
Deployment *Deployment `json:"deployment,omitempty"` |
|||
DeploymentStatus *DeploymentStatus `json:"deployment_status,omitempty"` |
|||
Repo *Repository `json:"repository,omitempty"` |
|||
|
|||
// The following fields are only populated by Webhook events.
|
|||
Sender *User `json:"sender,omitempty"` |
|||
} |
|||
|
|||
// ForkEvent is triggered when a user forks a repository.
|
|||
// The Webhook event name is "fork".
|
|||
//
|
|||
// GitHub docs: https://developer.github.com/v3/activity/events/types/#forkevent
|
|||
type ForkEvent struct { |
|||
// Forkee is the created repository.
|
|||
Forkee *Repository `json:"forkee,omitempty"` |
|||
|
|||
// The following fields are only populated by Webhook events.
|
|||
Repo *Repository `json:"repository,omitempty"` |
|||
Sender *User `json:"sender,omitempty"` |
|||
} |
|||
|
|||
// Page represents a single Wiki page.
|
|||
type Page struct { |
|||
PageName *string `json:"page_name,omitempty"` |
|||
Title *string `json:"title,omitempty"` |
|||
Summary *string `json:"summary,omitempty"` |
|||
Action *string `json:"action,omitempty"` |
|||
SHA *string `json:"sha,omitempty"` |
|||
HTMLURL *string `json:"html_url,omitempty"` |
|||
} |
|||
|
|||
// GollumEvent is triggered when a Wiki page is created or updated.
|
|||
// The Webhook event name is "gollum".
|
|||
//
|
|||
// GitHub docs: https://developer.github.com/v3/activity/events/types/#gollumevent
|
|||
type GollumEvent struct { |
|||
Pages []*Page `json:"pages,omitempty"` |
|||
|
|||
// The following fields are only populated by Webhook events.
|
|||
Repo *Repository `json:"repository,omitempty"` |
|||
Sender *User `json:"sender,omitempty"` |
|||
} |
|||
|
|||
// DEPRECATED: IssueActivityEvent represents the payload delivered by Issue webhook
|
|||
// Use IssuesEvent instead.
|
|||
type IssueActivityEvent struct { |
|||
Action *string `json:"action,omitempty"` |
|||
Issue *Issue `json:"issue,omitempty"` |
|||
|
|||
// The following fields are only populated by Webhook events.
|
|||
Repo *Repository `json:"repository,omitempty"` |
|||
Sender *User `json:"sender,omitempty"` |
|||
} |
|||
|
|||
// EditChange represents the changes when an issue, pull request, or comment has
|
|||
// been edited.
|
|||
type EditChange struct { |
|||
Title *struct { |
|||
From *string `json:"from,omitempty"` |
|||
} `json:"title,omitempty"` |
|||
Body *struct { |
|||
From *string `json:"from,omitempty"` |
|||
} `json:"body,omitempty"` |
|||
} |
|||
|
|||
// IssueCommentEvent is triggered when an issue comment is created on an issue
|
|||
// or pull request.
|
|||
// The Webhook event name is "issue_comment".
|
|||
//
|
|||
// GitHub docs: https://developer.github.com/v3/activity/events/types/#issuecommentevent
|
|||
type IssueCommentEvent struct { |
|||
// Action is the action that was performed on the comment.
|
|||
// Possible values are: "created", "edited", "deleted".
|
|||
Action *string `json:"action,omitempty"` |
|||
Issue *Issue `json:"issue,omitempty"` |
|||
Comment *IssueComment `json:"comment,omitempty"` |
|||
|
|||
// The following fields are only populated by Webhook events.
|
|||
Changes *EditChange `json:"changes,omitempty"` |
|||
Repo *Repository `json:"repository,omitempty"` |
|||
Sender *User `json:"sender,omitempty"` |
|||
} |
|||
|
|||
// IssuesEvent is triggered when an issue is assigned, unassigned, labeled,
|
|||
// unlabeled, opened, closed, or reopened.
|
|||
// The Webhook event name is "issues".
|
|||
//
|
|||
// GitHub docs: https://developer.github.com/v3/activity/events/types/#issuesevent
|
|||
type IssuesEvent struct { |
|||
// Action is the action that was performed. Possible values are: "assigned",
|
|||
// "unassigned", "labeled", "unlabeled", "opened", "closed", "reopened", "edited".
|
|||
Action *string `json:"action,omitempty"` |
|||
Issue *Issue `json:"issue,omitempty"` |
|||
Assignee *User `json:"assignee,omitempty"` |
|||
Label *Label `json:"label,omitempty"` |
|||
|
|||
// The following fields are only populated by Webhook events.
|
|||
Changes *EditChange `json:"changes,omitempty"` |
|||
Repo *Repository `json:"repository,omitempty"` |
|||
Sender *User `json:"sender,omitempty"` |
|||
} |
|||
|
|||
// MemberEvent is triggered when a user is added as a collaborator to a repository.
|
|||
// The Webhook event name is "member".
|
|||
//
|
|||
// GitHub docs: https://developer.github.com/v3/activity/events/types/#memberevent
|
|||
type MemberEvent struct { |
|||
// Action is the action that was performed. Possible value is: "added".
|
|||
Action *string `json:"action,omitempty"` |
|||
Member *User `json:"member,omitempty"` |
|||
|
|||
// The following fields are only populated by Webhook events.
|
|||
Repo *Repository `json:"repository,omitempty"` |
|||
Sender *User `json:"sender,omitempty"` |
|||
} |
|||
|
|||
// MembershipEvent is triggered when a user is added or removed from a team.
|
|||
// The Webhook event name is "membership".
|
|||
//
|
|||
// Events of this type are not visible in timelines, they are only used to
|
|||
// trigger organization webhooks.
|
|||
//
|
|||
// GitHub docs: https://developer.github.com/v3/activity/events/types/#membershipevent
|
|||
type MembershipEvent struct { |
|||
// Action is the action that was performed. Possible values are: "added", "removed".
|
|||
Action *string `json:"action,omitempty"` |
|||
// Scope is the scope of the membership. Possible value is: "team".
|
|||
Scope *string `json:"scope,omitempty"` |
|||
Member *User `json:"member,omitempty"` |
|||
Team *Team `json:"team,omitempty"` |
|||
|
|||
// The following fields are only populated by Webhook events.
|
|||
Org *Organization `json:"organization,omitempty"` |
|||
Sender *User `json:"sender,omitempty"` |
|||
} |
|||
|
|||
// PageBuildEvent represents an attempted build of a GitHub Pages site, whether
|
|||
// successful or not.
|
|||
// The Webhook event name is "page_build".
|
|||
//
|
|||
// This event is triggered on push to a GitHub Pages enabled branch (gh-pages
|
|||
// for project pages, master for user and organization pages).
|
|||
//
|
|||
// Events of this type are not visible in timelines, they are only used to trigger hooks.
|
|||
//
|
|||
// GitHub docs: https://developer.github.com/v3/activity/events/types/#pagebuildevent
|
|||
type PageBuildEvent struct { |
|||
Build *PagesBuild `json:"build,omitempty"` |
|||
|
|||
// The following fields are only populated by Webhook events.
|
|||
ID *int `json:"id,omitempty"` |
|||
Repo *Repository `json:"repository,omitempty"` |
|||
Sender *User `json:"sender,omitempty"` |
|||
} |
|||
|
|||
// PublicEvent is triggered when a private repository is open sourced.
|
|||
// According to GitHub: "Without a doubt: the best GitHub event."
|
|||
// The Webhook event name is "public".
|
|||
//
|
|||
// GitHub docs: https://developer.github.com/v3/activity/events/types/#publicevent
|
|||
type PublicEvent struct { |
|||
// The following fields are only populated by Webhook events.
|
|||
Repo *Repository `json:"repository,omitempty"` |
|||
Sender *User `json:"sender,omitempty"` |
|||
} |
|||
|
|||
// PullRequestEvent is triggered when a pull request is assigned, unassigned,
|
|||
// labeled, unlabeled, opened, closed, reopened, or synchronized.
|
|||
// The Webhook event name is "pull_request".
|
|||
//
|
|||
// GitHub docs: https://developer.github.com/v3/activity/events/types/#pullrequestevent
|
|||
type PullRequestEvent struct { |
|||
// Action is the action that was performed. Possible values are: "assigned",
|
|||
// "unassigned", "labeled", "unlabeled", "opened", "closed", or "reopened",
|
|||
// "synchronize", "edited". If the action is "closed" and the merged key is false,
|
|||
// the pull request was closed with unmerged commits. If the action is "closed"
|
|||
// and the merged key is true, the pull request was merged.
|
|||
Action *string `json:"action,omitempty"` |
|||
Number *int `json:"number,omitempty"` |
|||
PullRequest *PullRequest `json:"pull_request,omitempty"` |
|||
|
|||
// The following fields are only populated by Webhook events.
|
|||
Changes *EditChange `json:"changes,omitempty"` |
|||
Repo *Repository `json:"repository,omitempty"` |
|||
Sender *User `json:"sender,omitempty"` |
|||
} |
|||
|
|||
// PullRequestReviewCommentEvent is triggered when a comment is created on a
|
|||
// portion of the unified diff of a pull request.
|
|||
// The Webhook event name is "pull_request_review_comment".
|
|||
//
|
|||
// GitHub docs: https://developer.github.com/v3/activity/events/types/#pullrequestreviewcommentevent
|
|||
type PullRequestReviewCommentEvent struct { |
|||
// Action is the action that was performed on the comment.
|
|||
// Possible values are: "created", "edited", "deleted".
|
|||
Action *string `json:"action,omitempty"` |
|||
PullRequest *PullRequest `json:"pull_request,omitempty"` |
|||
Comment *PullRequestComment `json:"comment,omitempty"` |
|||
|
|||
// The following fields are only populated by Webhook events.
|
|||
Changes *EditChange `json:"changes,omitempty"` |
|||
Repo *Repository `json:"repository,omitempty"` |
|||
Sender *User `json:"sender,omitempty"` |
|||
} |
|||
|
|||
// PushEvent represents a git push to a GitHub repository.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/activity/events/types/#pushevent
|
|||
type PushEvent struct { |
|||
PushID *int `json:"push_id,omitempty"` |
|||
Head *string `json:"head,omitempty"` |
|||
Ref *string `json:"ref,omitempty"` |
|||
Size *int `json:"size,omitempty"` |
|||
Commits []PushEventCommit `json:"commits,omitempty"` |
|||
Repo *PushEventRepository `json:"repository,omitempty"` |
|||
Before *string `json:"before,omitempty"` |
|||
DistinctSize *int `json:"distinct_size,omitempty"` |
|||
|
|||
// The following fields are only populated by Webhook events.
|
|||
After *string `json:"after,omitempty"` |
|||
Created *bool `json:"created,omitempty"` |
|||
Deleted *bool `json:"deleted,omitempty"` |
|||
Forced *bool `json:"forced,omitempty"` |
|||
BaseRef *string `json:"base_ref,omitempty"` |
|||
Compare *string `json:"compare,omitempty"` |
|||
HeadCommit *PushEventCommit `json:"head_commit,omitempty"` |
|||
Pusher *User `json:"pusher,omitempty"` |
|||
Sender *User `json:"sender,omitempty"` |
|||
} |
|||
|
|||
func (p PushEvent) String() string { |
|||
return Stringify(p) |
|||
} |
|||
|
|||
// PushEventCommit represents a git commit in a GitHub PushEvent.
|
|||
type PushEventCommit struct { |
|||
Message *string `json:"message,omitempty"` |
|||
Author *CommitAuthor `json:"author,omitempty"` |
|||
URL *string `json:"url,omitempty"` |
|||
Distinct *bool `json:"distinct,omitempty"` |
|||
|
|||
// The following fields are only populated by Events API.
|
|||
SHA *string `json:"sha,omitempty"` |
|||
|
|||
// The following fields are only populated by Webhook events.
|
|||
ID *string `json:"id,omitempty"` |
|||
TreeID *string `json:"tree_id,omitempty"` |
|||
Timestamp *Timestamp `json:"timestamp,omitempty"` |
|||
Committer *CommitAuthor `json:"committer,omitempty"` |
|||
Added []string `json:"added,omitempty"` |
|||
Removed []string `json:"removed,omitempty"` |
|||
Modified []string `json:"modified,omitempty"` |
|||
} |
|||
|
|||
func (p PushEventCommit) String() string { |
|||
return Stringify(p) |
|||
} |
|||
|
|||
// PushEventRepository represents the repo object in a PushEvent payload
|
|||
type PushEventRepository struct { |
|||
ID *int `json:"id,omitempty"` |
|||
Name *string `json:"name,omitempty"` |
|||
FullName *string `json:"full_name,omitempty"` |
|||
Owner *PushEventRepoOwner `json:"owner,omitempty"` |
|||
Private *bool `json:"private,omitempty"` |
|||
Description *string `json:"description,omitempty"` |
|||
Fork *bool `json:"fork,omitempty"` |
|||
CreatedAt *Timestamp `json:"created_at,omitempty"` |
|||
PushedAt *Timestamp `json:"pushed_at,omitempty"` |
|||
UpdatedAt *Timestamp `json:"updated_at,omitempty"` |
|||
Homepage *string `json:"homepage,omitempty"` |
|||
Size *int `json:"size,omitempty"` |
|||
StargazersCount *int `json:"stargazers_count,omitempty"` |
|||
WatchersCount *int `json:"watchers_count,omitempty"` |
|||
Language *string `json:"language,omitempty"` |
|||
HasIssues *bool `json:"has_issues,omitempty"` |
|||
HasDownloads *bool `json:"has_downloads,omitempty"` |
|||
HasWiki *bool `json:"has_wiki,omitempty"` |
|||
HasPages *bool `json:"has_pages,omitempty"` |
|||
ForksCount *int `json:"forks_count,omitempty"` |
|||
OpenIssuesCount *int `json:"open_issues_count,omitempty"` |
|||
DefaultBranch *string `json:"default_branch,omitempty"` |
|||
MasterBranch *string `json:"master_branch,omitempty"` |
|||
Organization *string `json:"organization,omitempty"` |
|||
|
|||
// The following fields are only populated by Webhook events.
|
|||
URL *string `json:"url,omitempty"` |
|||
HTMLURL *string `json:"html_url,omitempty"` |
|||
} |
|||
|
|||
// PushEventRepoOwner is a basic reporesntation of user/org in a PushEvent payload
|
|||
type PushEventRepoOwner struct { |
|||
Name *string `json:"name,omitempty"` |
|||
Email *string `json:"email,omitempty"` |
|||
} |
|||
|
|||
// ReleaseEvent is triggered when a release is published.
|
|||
// The Webhook event name is "release".
|
|||
//
|
|||
// GitHub docs: https://developer.github.com/v3/activity/events/types/#releaseevent
|
|||
type ReleaseEvent struct { |
|||
// Action is the action that was performed. Possible value is: "published".
|
|||
Action *string `json:"action,omitempty"` |
|||
Release *RepositoryRelease `json:"release,omitempty"` |
|||
|
|||
// The following fields are only populated by Webhook events.
|
|||
Repo *Repository `json:"repository,omitempty"` |
|||
Sender *User `json:"sender,omitempty"` |
|||
} |
|||
|
|||
// RepositoryEvent is triggered when a repository is created.
|
|||
// The Webhook event name is "repository".
|
|||
//
|
|||
// Events of this type are not visible in timelines, they are only used to
|
|||
// trigger organization webhooks.
|
|||
//
|
|||
// GitHub docs: https://developer.github.com/v3/activity/events/types/#repositoryevent
|
|||
type RepositoryEvent struct { |
|||
// Action is the action that was performed. Possible values are: "created", "deleted",
|
|||
// "publicized", "privatized".
|
|||
Action *string `json:"action,omitempty"` |
|||
Repo *Repository `json:"repository,omitempty"` |
|||
|
|||
// The following fields are only populated by Webhook events.
|
|||
Org *Organization `json:"organization,omitempty"` |
|||
Sender *User `json:"sender,omitempty"` |
|||
} |
|||
|
|||
// StatusEvent is triggered when the status of a Git commit changes.
|
|||
// The Webhook event name is "status".
|
|||
//
|
|||
// Events of this type are not visible in timelines, they are only used to
|
|||
// trigger hooks.
|
|||
//
|
|||
// GitHub docs: https://developer.github.com/v3/activity/events/types/#statusevent
|
|||
type StatusEvent struct { |
|||
SHA *string `json:"sha,omitempty"` |
|||
// State is the new state. Possible values are: "pending", "success", "failure", "error".
|
|||
State *string `json:"state,omitempty"` |
|||
Description *string `json:"description,omitempty"` |
|||
TargetURL *string `json:"target_url,omitempty"` |
|||
Branches []*Branch `json:"branches,omitempty"` |
|||
|
|||
// The following fields are only populated by Webhook events.
|
|||
ID *int `json:"id,omitempty"` |
|||
Name *string `json:"name,omitempty"` |
|||
Context *string `json:"context,omitempty"` |
|||
Commit *PushEventCommit `json:"commit,omitempty"` |
|||
CreatedAt *Timestamp `json:"created_at,omitempty"` |
|||
UpdatedAt *Timestamp `json:"updated_at,omitempty"` |
|||
Repo *Repository `json:"repository,omitempty"` |
|||
Sender *User `json:"sender,omitempty"` |
|||
} |
|||
|
|||
// TeamAddEvent is triggered when a repository is added to a team.
|
|||
// The Webhook event name is "team_add".
|
|||
//
|
|||
// Events of this type are not visible in timelines. These events are only used
|
|||
// to trigger hooks.
|
|||
//
|
|||
// GitHub docs: https://developer.github.com/v3/activity/events/types/#teamaddevent
|
|||
type TeamAddEvent struct { |
|||
Team *Team `json:"team,omitempty"` |
|||
Repo *Repository `json:"repository,omitempty"` |
|||
|
|||
// The following fields are only populated by Webhook events.
|
|||
Org *Organization `json:"organization,omitempty"` |
|||
Sender *User `json:"sender,omitempty"` |
|||
} |
|||
|
|||
// WatchEvent is related to starring a repository, not watching. See this API
|
|||
// blog post for an explanation: https://developer.github.com/changes/2012-09-05-watcher-api/
|
|||
//
|
|||
// The event’s actor is the user who starred a repository, and the event’s
|
|||
// repository is the repository that was starred.
|
|||
//
|
|||
// GitHub docs: https://developer.github.com/v3/activity/events/types/#watchevent
|
|||
type WatchEvent struct { |
|||
// Action is the action that was performed. Possible value is: "started".
|
|||
Action *string `json:"action,omitempty"` |
|||
|
|||
// The following fields are only populated by Webhook events.
|
|||
Repo *Repository `json:"repository,omitempty"` |
|||
Sender *User `json:"sender,omitempty"` |
|||
} |
@ -0,0 +1,75 @@ |
|||
// Copyright 2016 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_test |
|||
|
|||
import ( |
|||
"fmt" |
|||
"log" |
|||
|
|||
"github.com/google/go-github/github" |
|||
) |
|||
|
|||
func ExampleClient_Markdown() { |
|||
client := github.NewClient(nil) |
|||
|
|||
input := "# heading #\n\nLink to issue #1" |
|||
opt := &github.MarkdownOptions{Mode: "gfm", Context: "google/go-github"} |
|||
|
|||
output, _, err := client.Markdown(input, opt) |
|||
if err != nil { |
|||
fmt.Println(err) |
|||
} |
|||
|
|||
fmt.Println(output) |
|||
} |
|||
|
|||
func ExampleRepositoriesService_GetReadme() { |
|||
client := github.NewClient(nil) |
|||
|
|||
readme, _, err := client.Repositories.GetReadme("google", "go-github", nil) |
|||
if err != nil { |
|||
fmt.Println(err) |
|||
return |
|||
} |
|||
|
|||
content, err := readme.GetContent() |
|||
if err != nil { |
|||
fmt.Println(err) |
|||
return |
|||
} |
|||
|
|||
fmt.Printf("google/go-github README:\n%v\n", content) |
|||
} |
|||
|
|||
func ExampleRepositoriesService_List() { |
|||
client := github.NewClient(nil) |
|||
|
|||
user := "willnorris" |
|||
opt := &github.RepositoryListOptions{Type: "owner", Sort: "updated", Direction: "desc"} |
|||
|
|||
repos, _, err := client.Repositories.List(user, opt) |
|||
if err != nil { |
|||
fmt.Println(err) |
|||
} |
|||
|
|||
fmt.Printf("Recently updated repositories by %q: %v", user, github.Stringify(repos)) |
|||
} |
|||
|
|||
func ExampleUsersService_ListAll() { |
|||
client := github.NewClient(nil) |
|||
opts := &github.UserListOptions{} |
|||
for { |
|||
users, _, err := client.Users.ListAll(opts) |
|||
if err != nil { |
|||
log.Fatalf("error listing users: %v", err) |
|||
} |
|||
if len(users) == 0 { |
|||
break |
|||
} |
|||
opts.Since = *users[len(users)-1].ID |
|||
// Process users...
|
|||
} |
|||
} |
@ -0,0 +1,343 @@ |
|||
// Copyright 2013 The go-github AUTHORS. All rights reserved.
|
|||
//
|
|||
// Use of this source code is governed by BSD-style
|
|||
// license that can be found in the LICENSE file.
|
|||
|
|||
package github |
|||
|
|||
import ( |
|||
"fmt" |
|||
"time" |
|||
) |
|||
|
|||
// GistsService handles communication with the Gist related
|
|||
// methods of the GitHub API.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/gists/
|
|||
type GistsService service |
|||
|
|||
// Gist represents a GitHub's gist.
|
|||
type Gist struct { |
|||
ID *string `json:"id,omitempty"` |
|||
Description *string `json:"description,omitempty"` |
|||
Public *bool `json:"public,omitempty"` |
|||
Owner *User `json:"owner,omitempty"` |
|||
Files map[GistFilename]GistFile `json:"files,omitempty"` |
|||
Comments *int `json:"comments,omitempty"` |
|||
HTMLURL *string `json:"html_url,omitempty"` |
|||
GitPullURL *string `json:"git_pull_url,omitempty"` |
|||
GitPushURL *string `json:"git_push_url,omitempty"` |
|||
CreatedAt *time.Time `json:"created_at,omitempty"` |
|||
UpdatedAt *time.Time `json:"updated_at,omitempty"` |
|||
} |
|||
|
|||
func (g Gist) String() string { |
|||
return Stringify(g) |
|||
} |
|||
|
|||
// GistFilename represents filename on a gist.
|
|||
type GistFilename string |
|||
|
|||
// GistFile represents a file on a gist.
|
|||
type GistFile struct { |
|||
Size *int `json:"size,omitempty"` |
|||
Filename *string `json:"filename,omitempty"` |
|||
RawURL *string `json:"raw_url,omitempty"` |
|||
Content *string `json:"content,omitempty"` |
|||
} |
|||
|
|||
func (g GistFile) String() string { |
|||
return Stringify(g) |
|||
} |
|||
|
|||
// GistCommit represents a commit on a gist.
|
|||
type GistCommit struct { |
|||
URL *string `json:"url,omitempty"` |
|||
Version *string `json:"version,omitempty"` |
|||
User *User `json:"user,omitempty"` |
|||
ChangeStatus *CommitStats `json:"change_status,omitempty"` |
|||
CommitedAt *Timestamp `json:"commited_at,omitempty"` |
|||
} |
|||
|
|||
func (gc GistCommit) String() string { |
|||
return Stringify(gc) |
|||
} |
|||
|
|||
// GistFork represents a fork of a gist.
|
|||
type GistFork struct { |
|||
URL *string `json:"url,omitempty"` |
|||
User *User `json:"user,omitempty"` |
|||
ID *string `json:"id,omitempty"` |
|||
CreatedAt *Timestamp `json:"created_at,omitempty"` |
|||
UpdatedAt *Timestamp `json:"updated_at,omitempty"` |
|||
} |
|||
|
|||
func (gf GistFork) String() string { |
|||
return Stringify(gf) |
|||
} |
|||
|
|||
// GistListOptions specifies the optional parameters to the
|
|||
// GistsService.List, GistsService.ListAll, and GistsService.ListStarred methods.
|
|||
type GistListOptions struct { |
|||
// Since filters Gists by time.
|
|||
Since time.Time `url:"since,omitempty"` |
|||
|
|||
ListOptions |
|||
} |
|||
|
|||
// List gists for a user. Passing the empty string will list
|
|||
// all public gists if called anonymously. However, if the call
|
|||
// is authenticated, it will returns all gists for the authenticated
|
|||
// user.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/gists/#list-gists
|
|||
func (s *GistsService) List(user string, opt *GistListOptions) ([]*Gist, *Response, error) { |
|||
var u string |
|||
if user != "" { |
|||
u = fmt.Sprintf("users/%v/gists", user) |
|||
} else { |
|||
u = "gists" |
|||
} |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
gists := new([]*Gist) |
|||
resp, err := s.client.Do(req, gists) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *gists, resp, err |
|||
} |
|||
|
|||
// ListAll lists all public gists.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/gists/#list-gists
|
|||
func (s *GistsService) ListAll(opt *GistListOptions) ([]*Gist, *Response, error) { |
|||
u, err := addOptions("gists/public", opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
gists := new([]*Gist) |
|||
resp, err := s.client.Do(req, gists) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *gists, resp, err |
|||
} |
|||
|
|||
// ListStarred lists starred gists of authenticated user.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/gists/#list-gists
|
|||
func (s *GistsService) ListStarred(opt *GistListOptions) ([]*Gist, *Response, error) { |
|||
u, err := addOptions("gists/starred", opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
gists := new([]*Gist) |
|||
resp, err := s.client.Do(req, gists) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *gists, resp, err |
|||
} |
|||
|
|||
// Get a single gist.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/gists/#get-a-single-gist
|
|||
func (s *GistsService) Get(id string) (*Gist, *Response, error) { |
|||
u := fmt.Sprintf("gists/%v", id) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
gist := new(Gist) |
|||
resp, err := s.client.Do(req, gist) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return gist, resp, err |
|||
} |
|||
|
|||
// GetRevision gets a specific revision of a gist.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/gists/#get-a-specific-revision-of-a-gist
|
|||
func (s *GistsService) GetRevision(id, sha string) (*Gist, *Response, error) { |
|||
u := fmt.Sprintf("gists/%v/%v", id, sha) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
gist := new(Gist) |
|||
resp, err := s.client.Do(req, gist) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return gist, resp, err |
|||
} |
|||
|
|||
// Create a gist for authenticated user.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/gists/#create-a-gist
|
|||
func (s *GistsService) Create(gist *Gist) (*Gist, *Response, error) { |
|||
u := "gists" |
|||
req, err := s.client.NewRequest("POST", u, gist) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
g := new(Gist) |
|||
resp, err := s.client.Do(req, g) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return g, resp, err |
|||
} |
|||
|
|||
// Edit a gist.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/gists/#edit-a-gist
|
|||
func (s *GistsService) Edit(id string, gist *Gist) (*Gist, *Response, error) { |
|||
u := fmt.Sprintf("gists/%v", id) |
|||
req, err := s.client.NewRequest("PATCH", u, gist) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
g := new(Gist) |
|||
resp, err := s.client.Do(req, g) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return g, resp, err |
|||
} |
|||
|
|||
// ListCommits lists commits of a gist.
|
|||
//
|
|||
// Github API docs: https://developer.github.com/v3/gists/#list-gist-commits
|
|||
func (s *GistsService) ListCommits(id string) ([]*GistCommit, *Response, error) { |
|||
u := fmt.Sprintf("gists/%v/commits", id) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
gistCommits := new([]*GistCommit) |
|||
resp, err := s.client.Do(req, gistCommits) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *gistCommits, resp, err |
|||
} |
|||
|
|||
// Delete a gist.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/gists/#delete-a-gist
|
|||
func (s *GistsService) Delete(id string) (*Response, error) { |
|||
u := fmt.Sprintf("gists/%v", id) |
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return s.client.Do(req, nil) |
|||
} |
|||
|
|||
// Star a gist on behalf of authenticated user.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/gists/#star-a-gist
|
|||
func (s *GistsService) Star(id string) (*Response, error) { |
|||
u := fmt.Sprintf("gists/%v/star", id) |
|||
req, err := s.client.NewRequest("PUT", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return s.client.Do(req, nil) |
|||
} |
|||
|
|||
// Unstar a gist on a behalf of authenticated user.
|
|||
//
|
|||
// Github API docs: http://developer.github.com/v3/gists/#unstar-a-gist
|
|||
func (s *GistsService) Unstar(id string) (*Response, error) { |
|||
u := fmt.Sprintf("gists/%v/star", id) |
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return s.client.Do(req, nil) |
|||
} |
|||
|
|||
// IsStarred checks if a gist is starred by authenticated user.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/gists/#check-if-a-gist-is-starred
|
|||
func (s *GistsService) IsStarred(id string) (bool, *Response, error) { |
|||
u := fmt.Sprintf("gists/%v/star", id) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return false, nil, err |
|||
} |
|||
resp, err := s.client.Do(req, nil) |
|||
starred, err := parseBoolResponse(err) |
|||
return starred, resp, err |
|||
} |
|||
|
|||
// Fork a gist.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/gists/#fork-a-gist
|
|||
func (s *GistsService) Fork(id string) (*Gist, *Response, error) { |
|||
u := fmt.Sprintf("gists/%v/forks", id) |
|||
req, err := s.client.NewRequest("POST", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
g := new(Gist) |
|||
resp, err := s.client.Do(req, g) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return g, resp, err |
|||
} |
|||
|
|||
// ListForks lists forks of a gist.
|
|||
//
|
|||
// Github API docs: https://developer.github.com/v3/gists/#list-gist-forks
|
|||
func (s *GistsService) ListForks(id string) ([]*GistFork, *Response, error) { |
|||
u := fmt.Sprintf("gists/%v/forks", id) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
gistForks := new([]*GistFork) |
|||
resp, err := s.client.Do(req, gistForks) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *gistForks, resp, err |
|||
} |
@ -0,0 +1,118 @@ |
|||
// Copyright 2013 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 ( |
|||
"fmt" |
|||
"time" |
|||
) |
|||
|
|||
// GistComment represents a Gist comment.
|
|||
type GistComment struct { |
|||
ID *int `json:"id,omitempty"` |
|||
URL *string `json:"url,omitempty"` |
|||
Body *string `json:"body,omitempty"` |
|||
User *User `json:"user,omitempty"` |
|||
CreatedAt *time.Time `json:"created_at,omitempty"` |
|||
} |
|||
|
|||
func (g GistComment) String() string { |
|||
return Stringify(g) |
|||
} |
|||
|
|||
// ListComments lists all comments for a gist.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/gists/comments/#list-comments-on-a-gist
|
|||
func (s *GistsService) ListComments(gistID string, opt *ListOptions) ([]*GistComment, *Response, error) { |
|||
u := fmt.Sprintf("gists/%v/comments", gistID) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
comments := new([]*GistComment) |
|||
resp, err := s.client.Do(req, comments) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *comments, resp, err |
|||
} |
|||
|
|||
// GetComment retrieves a single comment from a gist.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/gists/comments/#get-a-single-comment
|
|||
func (s *GistsService) GetComment(gistID string, commentID int) (*GistComment, *Response, error) { |
|||
u := fmt.Sprintf("gists/%v/comments/%v", gistID, commentID) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
c := new(GistComment) |
|||
resp, err := s.client.Do(req, c) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return c, resp, err |
|||
} |
|||
|
|||
// CreateComment creates a comment for a gist.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/gists/comments/#create-a-comment
|
|||
func (s *GistsService) CreateComment(gistID string, comment *GistComment) (*GistComment, *Response, error) { |
|||
u := fmt.Sprintf("gists/%v/comments", gistID) |
|||
req, err := s.client.NewRequest("POST", u, comment) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
c := new(GistComment) |
|||
resp, err := s.client.Do(req, c) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return c, resp, err |
|||
} |
|||
|
|||
// EditComment edits an existing gist comment.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/gists/comments/#edit-a-comment
|
|||
func (s *GistsService) EditComment(gistID string, commentID int, comment *GistComment) (*GistComment, *Response, error) { |
|||
u := fmt.Sprintf("gists/%v/comments/%v", gistID, commentID) |
|||
req, err := s.client.NewRequest("PATCH", u, comment) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
c := new(GistComment) |
|||
resp, err := s.client.Do(req, c) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return c, resp, err |
|||
} |
|||
|
|||
// DeleteComment deletes a gist comment.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/gists/comments/#delete-a-comment
|
|||
func (s *GistsService) DeleteComment(gistID string, commentID int) (*Response, error) { |
|||
u := fmt.Sprintf("gists/%v/comments/%v", gistID, commentID) |
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
return s.client.Do(req, nil) |
|||
} |
@ -0,0 +1,153 @@ |
|||
// Copyright 2013 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestGistsService_ListComments(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/gists/1/comments", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{"page": "2"}) |
|||
fmt.Fprint(w, `[{"id": 1}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 2} |
|||
comments, _, err := client.Gists.ListComments("1", opt) |
|||
if err != nil { |
|||
t.Errorf("Gists.Comments returned error: %v", err) |
|||
} |
|||
|
|||
want := []*GistComment{{ID: Int(1)}} |
|||
if !reflect.DeepEqual(comments, want) { |
|||
t.Errorf("Gists.ListComments returned %+v, want %+v", comments, want) |
|||
} |
|||
} |
|||
|
|||
func TestGistsService_ListComments_invalidID(t *testing.T) { |
|||
_, _, err := client.Gists.ListComments("%", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestGistsService_GetComment(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/gists/1/comments/2", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{"id": 1}`) |
|||
}) |
|||
|
|||
comment, _, err := client.Gists.GetComment("1", 2) |
|||
if err != nil { |
|||
t.Errorf("Gists.GetComment returned error: %v", err) |
|||
} |
|||
|
|||
want := &GistComment{ID: Int(1)} |
|||
if !reflect.DeepEqual(comment, want) { |
|||
t.Errorf("Gists.GetComment returned %+v, want %+v", comment, want) |
|||
} |
|||
} |
|||
|
|||
func TestGistsService_GetComment_invalidID(t *testing.T) { |
|||
_, _, err := client.Gists.GetComment("%", 1) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestGistsService_CreateComment(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &GistComment{ID: Int(1), Body: String("b")} |
|||
|
|||
mux.HandleFunc("/gists/1/comments", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(GistComment) |
|||
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.Gists.CreateComment("1", input) |
|||
if err != nil { |
|||
t.Errorf("Gists.CreateComment returned error: %v", err) |
|||
} |
|||
|
|||
want := &GistComment{ID: Int(1)} |
|||
if !reflect.DeepEqual(comment, want) { |
|||
t.Errorf("Gists.CreateComment returned %+v, want %+v", comment, want) |
|||
} |
|||
} |
|||
|
|||
func TestGistsService_CreateComment_invalidID(t *testing.T) { |
|||
_, _, err := client.Gists.CreateComment("%", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestGistsService_EditComment(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &GistComment{ID: Int(1), Body: String("b")} |
|||
|
|||
mux.HandleFunc("/gists/1/comments/2", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(GistComment) |
|||
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.Gists.EditComment("1", 2, input) |
|||
if err != nil { |
|||
t.Errorf("Gists.EditComment returned error: %v", err) |
|||
} |
|||
|
|||
want := &GistComment{ID: Int(1)} |
|||
if !reflect.DeepEqual(comment, want) { |
|||
t.Errorf("Gists.EditComment returned %+v, want %+v", comment, want) |
|||
} |
|||
} |
|||
|
|||
func TestGistsService_EditComment_invalidID(t *testing.T) { |
|||
_, _, err := client.Gists.EditComment("%", 1, nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestGistsService_DeleteComment(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/gists/1/comments/2", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
}) |
|||
|
|||
_, err := client.Gists.DeleteComment("1", 2) |
|||
if err != nil { |
|||
t.Errorf("Gists.Delete returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestGistsService_DeleteComment_invalidID(t *testing.T) { |
|||
_, err := client.Gists.DeleteComment("%", 1) |
|||
testURLParseError(t, err) |
|||
} |
@ -0,0 +1,488 @@ |
|||
// Copyright 2013 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
"time" |
|||
) |
|||
|
|||
func TestGistsService_List_specifiedUser(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
since := "2013-01-01T00:00:00Z" |
|||
|
|||
mux.HandleFunc("/users/u/gists", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{ |
|||
"since": since, |
|||
}) |
|||
fmt.Fprint(w, `[{"id": "1"}]`) |
|||
}) |
|||
|
|||
opt := &GistListOptions{Since: time.Date(2013, time.January, 1, 0, 0, 0, 0, time.UTC)} |
|||
gists, _, err := client.Gists.List("u", opt) |
|||
if err != nil { |
|||
t.Errorf("Gists.List returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Gist{{ID: String("1")}} |
|||
if !reflect.DeepEqual(gists, want) { |
|||
t.Errorf("Gists.List returned %+v, want %+v", gists, want) |
|||
} |
|||
} |
|||
|
|||
func TestGistsService_List_authenticatedUser(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/gists", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `[{"id": "1"}]`) |
|||
}) |
|||
|
|||
gists, _, err := client.Gists.List("", nil) |
|||
if err != nil { |
|||
t.Errorf("Gists.List returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Gist{{ID: String("1")}} |
|||
if !reflect.DeepEqual(gists, want) { |
|||
t.Errorf("Gists.List returned %+v, want %+v", gists, want) |
|||
} |
|||
} |
|||
|
|||
func TestGistsService_List_invalidUser(t *testing.T) { |
|||
_, _, err := client.Gists.List("%", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestGistsService_ListAll(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
since := "2013-01-01T00:00:00Z" |
|||
|
|||
mux.HandleFunc("/gists/public", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{ |
|||
"since": since, |
|||
}) |
|||
fmt.Fprint(w, `[{"id": "1"}]`) |
|||
}) |
|||
|
|||
opt := &GistListOptions{Since: time.Date(2013, time.January, 1, 0, 0, 0, 0, time.UTC)} |
|||
gists, _, err := client.Gists.ListAll(opt) |
|||
if err != nil { |
|||
t.Errorf("Gists.ListAll returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Gist{{ID: String("1")}} |
|||
if !reflect.DeepEqual(gists, want) { |
|||
t.Errorf("Gists.ListAll returned %+v, want %+v", gists, want) |
|||
} |
|||
} |
|||
|
|||
func TestGistsService_ListStarred(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
since := "2013-01-01T00:00:00Z" |
|||
|
|||
mux.HandleFunc("/gists/starred", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{ |
|||
"since": since, |
|||
}) |
|||
fmt.Fprint(w, `[{"id": "1"}]`) |
|||
}) |
|||
|
|||
opt := &GistListOptions{Since: time.Date(2013, time.January, 1, 0, 0, 0, 0, time.UTC)} |
|||
gists, _, err := client.Gists.ListStarred(opt) |
|||
if err != nil { |
|||
t.Errorf("Gists.ListStarred returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Gist{{ID: String("1")}} |
|||
if !reflect.DeepEqual(gists, want) { |
|||
t.Errorf("Gists.ListStarred returned %+v, want %+v", gists, want) |
|||
} |
|||
} |
|||
|
|||
func TestGistsService_Get(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/gists/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{"id": "1"}`) |
|||
}) |
|||
|
|||
gist, _, err := client.Gists.Get("1") |
|||
if err != nil { |
|||
t.Errorf("Gists.Get returned error: %v", err) |
|||
} |
|||
|
|||
want := &Gist{ID: String("1")} |
|||
if !reflect.DeepEqual(gist, want) { |
|||
t.Errorf("Gists.Get returned %+v, want %+v", gist, want) |
|||
} |
|||
} |
|||
|
|||
func TestGistsService_Get_invalidID(t *testing.T) { |
|||
_, _, err := client.Gists.Get("%") |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestGistsService_GetRevision(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/gists/1/s", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{"id": "1"}`) |
|||
}) |
|||
|
|||
gist, _, err := client.Gists.GetRevision("1", "s") |
|||
if err != nil { |
|||
t.Errorf("Gists.Get returned error: %v", err) |
|||
} |
|||
|
|||
want := &Gist{ID: String("1")} |
|||
if !reflect.DeepEqual(gist, want) { |
|||
t.Errorf("Gists.Get returned %+v, want %+v", gist, want) |
|||
} |
|||
} |
|||
|
|||
func TestGistsService_GetRevision_invalidID(t *testing.T) { |
|||
_, _, err := client.Gists.GetRevision("%", "%") |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestGistsService_Create(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &Gist{ |
|||
Description: String("Gist description"), |
|||
Public: Bool(false), |
|||
Files: map[GistFilename]GistFile{ |
|||
"test.txt": {Content: String("Gist file content")}, |
|||
}, |
|||
} |
|||
|
|||
mux.HandleFunc("/gists", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(Gist) |
|||
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", |
|||
"description": "Gist description", |
|||
"public": false, |
|||
"files": { |
|||
"test.txt": { |
|||
"filename": "test.txt" |
|||
} |
|||
} |
|||
}`) |
|||
}) |
|||
|
|||
gist, _, err := client.Gists.Create(input) |
|||
if err != nil { |
|||
t.Errorf("Gists.Create returned error: %v", err) |
|||
} |
|||
|
|||
want := &Gist{ |
|||
ID: String("1"), |
|||
Description: String("Gist description"), |
|||
Public: Bool(false), |
|||
Files: map[GistFilename]GistFile{ |
|||
"test.txt": {Filename: String("test.txt")}, |
|||
}, |
|||
} |
|||
if !reflect.DeepEqual(gist, want) { |
|||
t.Errorf("Gists.Create returned %+v, want %+v", gist, want) |
|||
} |
|||
} |
|||
|
|||
func TestGistsService_Edit(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &Gist{ |
|||
Description: String("New description"), |
|||
Files: map[GistFilename]GistFile{ |
|||
"new.txt": {Content: String("new file content")}, |
|||
}, |
|||
} |
|||
|
|||
mux.HandleFunc("/gists/1", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(Gist) |
|||
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", |
|||
"description": "new description", |
|||
"public": false, |
|||
"files": { |
|||
"test.txt": { |
|||
"filename": "test.txt" |
|||
}, |
|||
"new.txt": { |
|||
"filename": "new.txt" |
|||
} |
|||
} |
|||
}`) |
|||
}) |
|||
|
|||
gist, _, err := client.Gists.Edit("1", input) |
|||
if err != nil { |
|||
t.Errorf("Gists.Edit returned error: %v", err) |
|||
} |
|||
|
|||
want := &Gist{ |
|||
ID: String("1"), |
|||
Description: String("new description"), |
|||
Public: Bool(false), |
|||
Files: map[GistFilename]GistFile{ |
|||
"test.txt": {Filename: String("test.txt")}, |
|||
"new.txt": {Filename: String("new.txt")}, |
|||
}, |
|||
} |
|||
if !reflect.DeepEqual(gist, want) { |
|||
t.Errorf("Gists.Edit returned %+v, want %+v", gist, want) |
|||
} |
|||
} |
|||
|
|||
func TestGistsService_Edit_invalidID(t *testing.T) { |
|||
_, _, err := client.Gists.Edit("%", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestGistsService_ListCommits(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/gists/1/commits", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, nil) |
|||
fmt.Fprint(w, ` |
|||
[ |
|||
{ |
|||
"url": "https://api.github.com/gists/1/1", |
|||
"version": "1", |
|||
"user": { |
|||
"id": 1 |
|||
}, |
|||
"change_status": { |
|||
"deletions": 0, |
|||
"additions": 180, |
|||
"total": 180 |
|||
}, |
|||
"commited_at": "2010-01-01T00:00:00Z" |
|||
} |
|||
] |
|||
`) |
|||
}) |
|||
|
|||
gistCommits, _, err := client.Gists.ListCommits("1") |
|||
if err != nil { |
|||
t.Errorf("Gists.ListCommits returned error: %v", err) |
|||
} |
|||
|
|||
want := []*GistCommit{{ |
|||
URL: String("https://api.github.com/gists/1/1"), |
|||
Version: String("1"), |
|||
User: &User{ID: Int(1)}, |
|||
CommitedAt: &Timestamp{time.Date(2010, 1, 1, 00, 00, 00, 0, time.UTC)}, |
|||
ChangeStatus: &CommitStats{ |
|||
Additions: Int(180), |
|||
Deletions: Int(0), |
|||
Total: Int(180), |
|||
}}} |
|||
|
|||
if !reflect.DeepEqual(gistCommits, want) { |
|||
t.Errorf("Gists.ListCommits returned %+v, want %+v", gistCommits, want) |
|||
} |
|||
} |
|||
|
|||
func TestGistsService_Delete(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/gists/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
}) |
|||
|
|||
_, err := client.Gists.Delete("1") |
|||
if err != nil { |
|||
t.Errorf("Gists.Delete returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestGistsService_Delete_invalidID(t *testing.T) { |
|||
_, err := client.Gists.Delete("%") |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestGistsService_Star(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/gists/1/star", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "PUT") |
|||
}) |
|||
|
|||
_, err := client.Gists.Star("1") |
|||
if err != nil { |
|||
t.Errorf("Gists.Star returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestGistsService_Star_invalidID(t *testing.T) { |
|||
_, err := client.Gists.Star("%") |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestGistsService_Unstar(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/gists/1/star", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
}) |
|||
|
|||
_, err := client.Gists.Unstar("1") |
|||
if err != nil { |
|||
t.Errorf("Gists.Unstar returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestGistsService_Unstar_invalidID(t *testing.T) { |
|||
_, err := client.Gists.Unstar("%") |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestGistsService_IsStarred_hasStar(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/gists/1/star", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
w.WriteHeader(http.StatusNoContent) |
|||
}) |
|||
|
|||
star, _, err := client.Gists.IsStarred("1") |
|||
if err != nil { |
|||
t.Errorf("Gists.Starred returned error: %v", err) |
|||
} |
|||
if want := true; star != want { |
|||
t.Errorf("Gists.Starred returned %+v, want %+v", star, want) |
|||
} |
|||
} |
|||
|
|||
func TestGistsService_IsStarred_noStar(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/gists/1/star", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
w.WriteHeader(http.StatusNotFound) |
|||
}) |
|||
|
|||
star, _, err := client.Gists.IsStarred("1") |
|||
if err != nil { |
|||
t.Errorf("Gists.Starred returned error: %v", err) |
|||
} |
|||
if want := false; star != want { |
|||
t.Errorf("Gists.Starred returned %+v, want %+v", star, want) |
|||
} |
|||
} |
|||
|
|||
func TestGistsService_IsStarred_invalidID(t *testing.T) { |
|||
_, _, err := client.Gists.IsStarred("%") |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestGistsService_Fork(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/gists/1/forks", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "POST") |
|||
fmt.Fprint(w, `{"id": "2"}`) |
|||
}) |
|||
|
|||
gist, _, err := client.Gists.Fork("1") |
|||
if err != nil { |
|||
t.Errorf("Gists.Fork returned error: %v", err) |
|||
} |
|||
|
|||
want := &Gist{ID: String("2")} |
|||
if !reflect.DeepEqual(gist, want) { |
|||
t.Errorf("Gists.Fork returned %+v, want %+v", gist, want) |
|||
} |
|||
} |
|||
|
|||
func TestGistsService_ListForks(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/gists/1/forks", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, nil) |
|||
fmt.Fprint(w, ` |
|||
[ |
|||
{"url": "https://api.github.com/gists/1", |
|||
"user": {"id": 1}, |
|||
"id": "1", |
|||
"created_at": "2010-01-01T00:00:00Z", |
|||
"updated_at": "2013-01-01T00:00:00Z" |
|||
} |
|||
] |
|||
`) |
|||
}) |
|||
|
|||
gistForks, _, err := client.Gists.ListForks("1") |
|||
if err != nil { |
|||
t.Errorf("Gists.ListForks returned error: %v", err) |
|||
} |
|||
|
|||
want := []*GistFork{{ |
|||
URL: String("https://api.github.com/gists/1"), |
|||
ID: String("1"), |
|||
User: &User{ID: Int(1)}, |
|||
CreatedAt: &Timestamp{time.Date(2010, 1, 1, 00, 00, 00, 0, time.UTC)}, |
|||
UpdatedAt: &Timestamp{time.Date(2013, 1, 1, 00, 00, 00, 0, time.UTC)}}} |
|||
|
|||
if !reflect.DeepEqual(gistForks, want) { |
|||
t.Errorf("Gists.ListForks returned %+v, want %+v", gistForks, want) |
|||
} |
|||
} |
|||
|
|||
func TestGistsService_Fork_invalidID(t *testing.T) { |
|||
_, _, err := client.Gists.Fork("%") |
|||
testURLParseError(t, err) |
|||
} |
@ -0,0 +1,12 @@ |
|||
// Copyright 2013 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 |
|||
|
|||
// GitService handles communication with the git data related
|
|||
// methods of the GitHub API.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/git/
|
|||
type GitService service |
@ -0,0 +1,47 @@ |
|||
// Copyright 2013 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 "fmt" |
|||
|
|||
// Blob represents a blob object.
|
|||
type Blob struct { |
|||
Content *string `json:"content,omitempty"` |
|||
Encoding *string `json:"encoding,omitempty"` |
|||
SHA *string `json:"sha,omitempty"` |
|||
Size *int `json:"size,omitempty"` |
|||
URL *string `json:"url,omitempty"` |
|||
} |
|||
|
|||
// GetBlob fetchs a blob from a repo given a SHA.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/git/blobs/#get-a-blob
|
|||
func (s *GitService) GetBlob(owner string, repo string, sha string) (*Blob, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/git/blobs/%v", owner, repo, sha) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
blob := new(Blob) |
|||
resp, err := s.client.Do(req, blob) |
|||
return blob, resp, err |
|||
} |
|||
|
|||
// CreateBlob creates a blob object.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/git/blobs/#create-a-blob
|
|||
func (s *GitService) CreateBlob(owner string, repo string, blob *Blob) (*Blob, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/git/blobs", owner, repo) |
|||
req, err := s.client.NewRequest("POST", u, blob) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
t := new(Blob) |
|||
resp, err := s.client.Do(req, t) |
|||
return t, resp, err |
|||
} |
@ -0,0 +1,97 @@ |
|||
// Copyright 2014 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestGitService_GetBlob(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/git/blobs/s", func(w http.ResponseWriter, r *http.Request) { |
|||
if m := "GET"; m != r.Method { |
|||
t.Errorf("Request method = %v, want %v", r.Method, m) |
|||
} |
|||
fmt.Fprint(w, `{ |
|||
"sha": "s", |
|||
"content": "blob content" |
|||
}`) |
|||
}) |
|||
|
|||
blob, _, err := client.Git.GetBlob("o", "r", "s") |
|||
if err != nil { |
|||
t.Errorf("Git.GetBlob returned error: %v", err) |
|||
} |
|||
|
|||
want := Blob{ |
|||
SHA: String("s"), |
|||
Content: String("blob content"), |
|||
} |
|||
|
|||
if !reflect.DeepEqual(*blob, want) { |
|||
t.Errorf("Blob.Get returned %+v, want %+v", *blob, want) |
|||
} |
|||
} |
|||
|
|||
func TestGitService_GetBlob_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Git.GetBlob("%", "%", "%") |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestGitService_CreateBlob(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &Blob{ |
|||
SHA: String("s"), |
|||
Content: String("blob content"), |
|||
Encoding: String("utf-8"), |
|||
Size: Int(12), |
|||
} |
|||
|
|||
mux.HandleFunc("/repos/o/r/git/blobs", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(Blob) |
|||
json.NewDecoder(r.Body).Decode(v) |
|||
|
|||
if m := "POST"; m != r.Method { |
|||
t.Errorf("Request method = %v, want %v", r.Method, m) |
|||
} |
|||
|
|||
want := input |
|||
if !reflect.DeepEqual(v, want) { |
|||
t.Errorf("Git.CreateBlob request body: %+v, want %+v", v, want) |
|||
} |
|||
|
|||
fmt.Fprint(w, `{ |
|||
"sha": "s", |
|||
"content": "blob content", |
|||
"encoding": "utf-8", |
|||
"size": 12 |
|||
}`) |
|||
}) |
|||
|
|||
blob, _, err := client.Git.CreateBlob("o", "r", input) |
|||
if err != nil { |
|||
t.Errorf("Git.CreateBlob returned error: %v", err) |
|||
} |
|||
|
|||
want := input |
|||
|
|||
if !reflect.DeepEqual(*blob, *want) { |
|||
t.Errorf("Git.CreateBlob returned %+v, want %+v", *blob, *want) |
|||
} |
|||
} |
|||
|
|||
func TestGitService_CreateBlob_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Git.CreateBlob("%", "%", &Blob{}) |
|||
testURLParseError(t, err) |
|||
} |
@ -0,0 +1,127 @@ |
|||
// Copyright 2013 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 ( |
|||
"fmt" |
|||
"time" |
|||
) |
|||
|
|||
// SignatureVerification represents GPG signature verification.
|
|||
type SignatureVerification struct { |
|||
Verified *bool `json:"verified,omitempty"` |
|||
Reason *string `json:"reason,omitempty"` |
|||
Signature *string `json:"signature,omitempty"` |
|||
Payload *string `json:"payload,omitempty"` |
|||
} |
|||
|
|||
// Commit represents a GitHub commit.
|
|||
type Commit struct { |
|||
SHA *string `json:"sha,omitempty"` |
|||
Author *CommitAuthor `json:"author,omitempty"` |
|||
Committer *CommitAuthor `json:"committer,omitempty"` |
|||
Message *string `json:"message,omitempty"` |
|||
Tree *Tree `json:"tree,omitempty"` |
|||
Parents []Commit `json:"parents,omitempty"` |
|||
Stats *CommitStats `json:"stats,omitempty"` |
|||
URL *string `json:"url,omitempty"` |
|||
Verification *SignatureVerification `json:"verification,omitempty"` |
|||
|
|||
// CommentCount is the number of GitHub comments on the commit. This
|
|||
// is only populated for requests that fetch GitHub data like
|
|||
// Pulls.ListCommits, Repositories.ListCommits, etc.
|
|||
CommentCount *int `json:"comment_count,omitempty"` |
|||
} |
|||
|
|||
func (c Commit) String() string { |
|||
return Stringify(c) |
|||
} |
|||
|
|||
// CommitAuthor represents the author or committer of a commit. The commit
|
|||
// author may not correspond to a GitHub User.
|
|||
type CommitAuthor struct { |
|||
Date *time.Time `json:"date,omitempty"` |
|||
Name *string `json:"name,omitempty"` |
|||
Email *string `json:"email,omitempty"` |
|||
|
|||
// The following fields are only populated by Webhook events.
|
|||
Login *string `json:"username,omitempty"` // Renamed for go-github consistency.
|
|||
} |
|||
|
|||
func (c CommitAuthor) String() string { |
|||
return Stringify(c) |
|||
} |
|||
|
|||
// GetCommit fetchs the Commit object for a given SHA.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/git/commits/#get-a-commit
|
|||
func (s *GitService) GetCommit(owner string, repo string, sha string) (*Commit, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/git/commits/%v", owner, repo, sha) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeGitSigningPreview) |
|||
|
|||
c := new(Commit) |
|||
resp, err := s.client.Do(req, c) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return c, resp, err |
|||
} |
|||
|
|||
// createCommit represents the body of a CreateCommit request.
|
|||
type createCommit struct { |
|||
Author *CommitAuthor `json:"author,omitempty"` |
|||
Committer *CommitAuthor `json:"committer,omitempty"` |
|||
Message *string `json:"message,omitempty"` |
|||
Tree *string `json:"tree,omitempty"` |
|||
Parents []string `json:"parents,omitempty"` |
|||
} |
|||
|
|||
// CreateCommit creates a new commit in a repository.
|
|||
//
|
|||
// The commit.Committer is optional and will be filled with the commit.Author
|
|||
// data if omitted. If the commit.Author is omitted, it will be filled in with
|
|||
// the authenticated user’s information and the current date.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/git/commits/#create-a-commit
|
|||
func (s *GitService) CreateCommit(owner string, repo string, commit *Commit) (*Commit, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/git/commits", owner, repo) |
|||
|
|||
body := &createCommit{} |
|||
if commit != nil { |
|||
parents := make([]string, len(commit.Parents)) |
|||
for i, parent := range commit.Parents { |
|||
parents[i] = *parent.SHA |
|||
} |
|||
|
|||
body = &createCommit{ |
|||
Author: commit.Author, |
|||
Committer: commit.Committer, |
|||
Message: commit.Message, |
|||
Tree: commit.Tree.SHA, |
|||
Parents: parents, |
|||
} |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("POST", u, body) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
c := new(Commit) |
|||
resp, err := s.client.Do(req, c) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return c, resp, err |
|||
} |
@ -0,0 +1,83 @@ |
|||
// Copyright 2013 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestGitService_GetCommit(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/git/commits/s", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeGitSigningPreview) |
|||
fmt.Fprint(w, `{"sha":"s","message":"m","author":{"name":"n"}}`) |
|||
}) |
|||
|
|||
commit, _, err := client.Git.GetCommit("o", "r", "s") |
|||
if err != nil { |
|||
t.Errorf("Git.GetCommit returned error: %v", err) |
|||
} |
|||
|
|||
want := &Commit{SHA: String("s"), Message: String("m"), Author: &CommitAuthor{Name: String("n")}} |
|||
if !reflect.DeepEqual(commit, want) { |
|||
t.Errorf("Git.GetCommit returned %+v, want %+v", commit, want) |
|||
} |
|||
} |
|||
|
|||
func TestGitService_GetCommit_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Git.GetCommit("%", "%", "%") |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestGitService_CreateCommit(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &Commit{ |
|||
Message: String("m"), |
|||
Tree: &Tree{SHA: String("t")}, |
|||
Parents: []Commit{{SHA: String("p")}}, |
|||
} |
|||
|
|||
mux.HandleFunc("/repos/o/r/git/commits", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(createCommit) |
|||
json.NewDecoder(r.Body).Decode(v) |
|||
|
|||
testMethod(t, r, "POST") |
|||
|
|||
want := &createCommit{ |
|||
Message: input.Message, |
|||
Tree: String("t"), |
|||
Parents: []string{"p"}, |
|||
} |
|||
if !reflect.DeepEqual(v, want) { |
|||
t.Errorf("Request body = %+v, want %+v", v, want) |
|||
} |
|||
fmt.Fprint(w, `{"sha":"s"}`) |
|||
}) |
|||
|
|||
commit, _, err := client.Git.CreateCommit("o", "r", input) |
|||
if err != nil { |
|||
t.Errorf("Git.CreateCommit returned error: %v", err) |
|||
} |
|||
|
|||
want := &Commit{SHA: String("s")} |
|||
if !reflect.DeepEqual(commit, want) { |
|||
t.Errorf("Git.CreateCommit returned %+v, want %+v", commit, want) |
|||
} |
|||
} |
|||
|
|||
func TestGitService_CreateCommit_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Git.CreateCommit("%", "%", nil) |
|||
testURLParseError(t, err) |
|||
} |
@ -0,0 +1,162 @@ |
|||
// Copyright 2013 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 ( |
|||
"fmt" |
|||
"strings" |
|||
) |
|||
|
|||
// Reference represents a GitHub reference.
|
|||
type Reference struct { |
|||
Ref *string `json:"ref"` |
|||
URL *string `json:"url"` |
|||
Object *GitObject `json:"object"` |
|||
} |
|||
|
|||
func (r Reference) String() string { |
|||
return Stringify(r) |
|||
} |
|||
|
|||
// GitObject represents a Git object.
|
|||
type GitObject struct { |
|||
Type *string `json:"type"` |
|||
SHA *string `json:"sha"` |
|||
URL *string `json:"url"` |
|||
} |
|||
|
|||
func (o GitObject) String() string { |
|||
return Stringify(o) |
|||
} |
|||
|
|||
// createRefRequest represents the payload for creating a reference.
|
|||
type createRefRequest struct { |
|||
Ref *string `json:"ref"` |
|||
SHA *string `json:"sha"` |
|||
} |
|||
|
|||
// updateRefRequest represents the payload for updating a reference.
|
|||
type updateRefRequest struct { |
|||
SHA *string `json:"sha"` |
|||
Force *bool `json:"force"` |
|||
} |
|||
|
|||
// GetRef fetches the Reference object for a given Git ref.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/git/refs/#get-a-reference
|
|||
func (s *GitService) GetRef(owner string, repo string, ref string) (*Reference, *Response, error) { |
|||
ref = strings.TrimPrefix(ref, "refs/") |
|||
u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, ref) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
r := new(Reference) |
|||
resp, err := s.client.Do(req, r) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return r, resp, err |
|||
} |
|||
|
|||
// ReferenceListOptions specifies optional parameters to the
|
|||
// GitService.ListRefs method.
|
|||
type ReferenceListOptions struct { |
|||
Type string `url:"-"` |
|||
|
|||
ListOptions |
|||
} |
|||
|
|||
// ListRefs lists all refs in a repository.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/git/refs/#get-all-references
|
|||
func (s *GitService) ListRefs(owner, repo string, opt *ReferenceListOptions) ([]*Reference, *Response, error) { |
|||
var u string |
|||
if opt != nil && opt.Type != "" { |
|||
u = fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, opt.Type) |
|||
} else { |
|||
u = fmt.Sprintf("repos/%v/%v/git/refs", owner, repo) |
|||
} |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
var rs []*Reference |
|||
resp, err := s.client.Do(req, &rs) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return rs, resp, err |
|||
} |
|||
|
|||
// CreateRef creates a new ref in a repository.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/git/refs/#create-a-reference
|
|||
func (s *GitService) CreateRef(owner string, repo string, ref *Reference) (*Reference, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/git/refs", owner, repo) |
|||
req, err := s.client.NewRequest("POST", u, &createRefRequest{ |
|||
// back-compat with previous behavior that didn't require 'refs/' prefix
|
|||
Ref: String("refs/" + strings.TrimPrefix(*ref.Ref, "refs/")), |
|||
SHA: ref.Object.SHA, |
|||
}) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
r := new(Reference) |
|||
resp, err := s.client.Do(req, r) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return r, resp, err |
|||
} |
|||
|
|||
// UpdateRef updates an existing ref in a repository.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/git/refs/#update-a-reference
|
|||
func (s *GitService) UpdateRef(owner string, repo string, ref *Reference, force bool) (*Reference, *Response, error) { |
|||
refPath := strings.TrimPrefix(*ref.Ref, "refs/") |
|||
u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, refPath) |
|||
req, err := s.client.NewRequest("PATCH", u, &updateRefRequest{ |
|||
SHA: ref.Object.SHA, |
|||
Force: &force, |
|||
}) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
r := new(Reference) |
|||
resp, err := s.client.Do(req, r) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return r, resp, err |
|||
} |
|||
|
|||
// DeleteRef deletes a ref from a repository.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/git/refs/#delete-a-reference
|
|||
func (s *GitService) DeleteRef(owner string, repo string, ref string) (*Response, error) { |
|||
ref = strings.TrimPrefix(ref, "refs/") |
|||
u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, ref) |
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
return s.client.Do(req, nil) |
|||
} |
@ -0,0 +1,280 @@ |
|||
// Copyright 2013 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestGitService_GetRef(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/git/refs/heads/b", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, ` |
|||
{ |
|||
"ref": "refs/heads/b", |
|||
"url": "https://api.github.com/repos/o/r/git/refs/heads/b", |
|||
"object": { |
|||
"type": "commit", |
|||
"sha": "aa218f56b14c9653891f9e74264a383fa43fefbd", |
|||
"url": "https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd" |
|||
} |
|||
}`) |
|||
}) |
|||
|
|||
ref, _, err := client.Git.GetRef("o", "r", "refs/heads/b") |
|||
if err != nil { |
|||
t.Errorf("Git.GetRef returned error: %v", err) |
|||
} |
|||
|
|||
want := &Reference{ |
|||
Ref: String("refs/heads/b"), |
|||
URL: String("https://api.github.com/repos/o/r/git/refs/heads/b"), |
|||
Object: &GitObject{ |
|||
Type: String("commit"), |
|||
SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), |
|||
URL: String("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), |
|||
}, |
|||
} |
|||
if !reflect.DeepEqual(ref, want) { |
|||
t.Errorf("Git.GetRef returned %+v, want %+v", ref, want) |
|||
} |
|||
|
|||
// without 'refs/' prefix
|
|||
if _, _, err := client.Git.GetRef("o", "r", "heads/b"); err != nil { |
|||
t.Errorf("Git.GetRef returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestGitService_ListRefs(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/git/refs", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, ` |
|||
[ |
|||
{ |
|||
"ref": "refs/heads/branchA", |
|||
"url": "https://api.github.com/repos/o/r/git/refs/heads/branchA", |
|||
"object": { |
|||
"type": "commit", |
|||
"sha": "aa218f56b14c9653891f9e74264a383fa43fefbd", |
|||
"url": "https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd" |
|||
} |
|||
}, |
|||
{ |
|||
"ref": "refs/heads/branchB", |
|||
"url": "https://api.github.com/repos/o/r/git/refs/heads/branchB", |
|||
"object": { |
|||
"type": "commit", |
|||
"sha": "aa218f56b14c9653891f9e74264a383fa43fefbd", |
|||
"url": "https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd" |
|||
} |
|||
} |
|||
]`) |
|||
}) |
|||
|
|||
refs, _, err := client.Git.ListRefs("o", "r", nil) |
|||
if err != nil { |
|||
t.Errorf("Git.ListRefs returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Reference{ |
|||
{ |
|||
Ref: String("refs/heads/branchA"), |
|||
URL: String("https://api.github.com/repos/o/r/git/refs/heads/branchA"), |
|||
Object: &GitObject{ |
|||
Type: String("commit"), |
|||
SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), |
|||
URL: String("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), |
|||
}, |
|||
}, |
|||
{ |
|||
Ref: String("refs/heads/branchB"), |
|||
URL: String("https://api.github.com/repos/o/r/git/refs/heads/branchB"), |
|||
Object: &GitObject{ |
|||
Type: String("commit"), |
|||
SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), |
|||
URL: String("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), |
|||
}, |
|||
}, |
|||
} |
|||
if !reflect.DeepEqual(refs, want) { |
|||
t.Errorf("Git.ListRefs returned %+v, want %+v", refs, want) |
|||
} |
|||
} |
|||
|
|||
func TestGitService_ListRefs_options(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/git/refs/t", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{"page": "2"}) |
|||
fmt.Fprint(w, `[{"ref": "r"}]`) |
|||
}) |
|||
|
|||
opt := &ReferenceListOptions{Type: "t", ListOptions: ListOptions{Page: 2}} |
|||
refs, _, err := client.Git.ListRefs("o", "r", opt) |
|||
if err != nil { |
|||
t.Errorf("Git.ListRefs returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Reference{{Ref: String("r")}} |
|||
if !reflect.DeepEqual(refs, want) { |
|||
t.Errorf("Git.ListRefs returned %+v, want %+v", refs, want) |
|||
} |
|||
} |
|||
|
|||
func TestGitService_CreateRef(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
args := &createRefRequest{ |
|||
Ref: String("refs/heads/b"), |
|||
SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), |
|||
} |
|||
|
|||
mux.HandleFunc("/repos/o/r/git/refs", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(createRefRequest) |
|||
json.NewDecoder(r.Body).Decode(v) |
|||
|
|||
testMethod(t, r, "POST") |
|||
if !reflect.DeepEqual(v, args) { |
|||
t.Errorf("Request body = %+v, want %+v", v, args) |
|||
} |
|||
fmt.Fprint(w, ` |
|||
{ |
|||
"ref": "refs/heads/b", |
|||
"url": "https://api.github.com/repos/o/r/git/refs/heads/b", |
|||
"object": { |
|||
"type": "commit", |
|||
"sha": "aa218f56b14c9653891f9e74264a383fa43fefbd", |
|||
"url": "https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd" |
|||
} |
|||
}`) |
|||
}) |
|||
|
|||
ref, _, err := client.Git.CreateRef("o", "r", &Reference{ |
|||
Ref: String("refs/heads/b"), |
|||
Object: &GitObject{ |
|||
SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), |
|||
}, |
|||
}) |
|||
if err != nil { |
|||
t.Errorf("Git.CreateRef returned error: %v", err) |
|||
} |
|||
|
|||
want := &Reference{ |
|||
Ref: String("refs/heads/b"), |
|||
URL: String("https://api.github.com/repos/o/r/git/refs/heads/b"), |
|||
Object: &GitObject{ |
|||
Type: String("commit"), |
|||
SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), |
|||
URL: String("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), |
|||
}, |
|||
} |
|||
if !reflect.DeepEqual(ref, want) { |
|||
t.Errorf("Git.CreateRef returned %+v, want %+v", ref, want) |
|||
} |
|||
|
|||
// without 'refs/' prefix
|
|||
_, _, err = client.Git.CreateRef("o", "r", &Reference{ |
|||
Ref: String("heads/b"), |
|||
Object: &GitObject{ |
|||
SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), |
|||
}, |
|||
}) |
|||
if err != nil { |
|||
t.Errorf("Git.CreateRef returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestGitService_UpdateRef(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
args := &updateRefRequest{ |
|||
SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), |
|||
Force: Bool(true), |
|||
} |
|||
|
|||
mux.HandleFunc("/repos/o/r/git/refs/heads/b", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(updateRefRequest) |
|||
json.NewDecoder(r.Body).Decode(v) |
|||
|
|||
testMethod(t, r, "PATCH") |
|||
if !reflect.DeepEqual(v, args) { |
|||
t.Errorf("Request body = %+v, want %+v", v, args) |
|||
} |
|||
fmt.Fprint(w, ` |
|||
{ |
|||
"ref": "refs/heads/b", |
|||
"url": "https://api.github.com/repos/o/r/git/refs/heads/b", |
|||
"object": { |
|||
"type": "commit", |
|||
"sha": "aa218f56b14c9653891f9e74264a383fa43fefbd", |
|||
"url": "https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd" |
|||
} |
|||
}`) |
|||
}) |
|||
|
|||
ref, _, err := client.Git.UpdateRef("o", "r", &Reference{ |
|||
Ref: String("refs/heads/b"), |
|||
Object: &GitObject{SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd")}, |
|||
}, true) |
|||
if err != nil { |
|||
t.Errorf("Git.UpdateRef returned error: %v", err) |
|||
} |
|||
|
|||
want := &Reference{ |
|||
Ref: String("refs/heads/b"), |
|||
URL: String("https://api.github.com/repos/o/r/git/refs/heads/b"), |
|||
Object: &GitObject{ |
|||
Type: String("commit"), |
|||
SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd"), |
|||
URL: String("https://api.github.com/repos/o/r/git/commits/aa218f56b14c9653891f9e74264a383fa43fefbd"), |
|||
}, |
|||
} |
|||
if !reflect.DeepEqual(ref, want) { |
|||
t.Errorf("Git.UpdateRef returned %+v, want %+v", ref, want) |
|||
} |
|||
|
|||
// without 'refs/' prefix
|
|||
_, _, err = client.Git.UpdateRef("o", "r", &Reference{ |
|||
Ref: String("heads/b"), |
|||
Object: &GitObject{SHA: String("aa218f56b14c9653891f9e74264a383fa43fefbd")}, |
|||
}, true) |
|||
if err != nil { |
|||
t.Errorf("Git.UpdateRef returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestGitService_DeleteRef(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/git/refs/heads/b", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
}) |
|||
|
|||
_, err := client.Git.DeleteRef("o", "r", "refs/heads/b") |
|||
if err != nil { |
|||
t.Errorf("Git.DeleteRef returned error: %v", err) |
|||
} |
|||
|
|||
// without 'refs/' prefix
|
|||
if _, err := client.Git.DeleteRef("o", "r", "heads/b"); err != nil { |
|||
t.Errorf("Git.DeleteRef returned error: %v", err) |
|||
} |
|||
} |
@ -0,0 +1,77 @@ |
|||
// Copyright 2013 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 ( |
|||
"fmt" |
|||
) |
|||
|
|||
// Tag represents a tag object.
|
|||
type Tag struct { |
|||
Tag *string `json:"tag,omitempty"` |
|||
SHA *string `json:"sha,omitempty"` |
|||
URL *string `json:"url,omitempty"` |
|||
Message *string `json:"message,omitempty"` |
|||
Tagger *CommitAuthor `json:"tagger,omitempty"` |
|||
Object *GitObject `json:"object,omitempty"` |
|||
Verification *SignatureVerification `json:"verification,omitempty"` |
|||
} |
|||
|
|||
// createTagRequest represents the body of a CreateTag request. This is mostly
|
|||
// identical to Tag with the exception that the object SHA and Type are
|
|||
// top-level fields, rather than being nested inside a JSON object.
|
|||
type createTagRequest struct { |
|||
Tag *string `json:"tag,omitempty"` |
|||
Message *string `json:"message,omitempty"` |
|||
Object *string `json:"object,omitempty"` |
|||
Type *string `json:"type,omitempty"` |
|||
Tagger *CommitAuthor `json:"tagger,omitempty"` |
|||
} |
|||
|
|||
// GetTag fetchs a tag from a repo given a SHA.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/git/tags/#get-a-tag
|
|||
func (s *GitService) GetTag(owner string, repo string, sha string) (*Tag, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/git/tags/%v", owner, repo, sha) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeGitSigningPreview) |
|||
|
|||
tag := new(Tag) |
|||
resp, err := s.client.Do(req, tag) |
|||
return tag, resp, err |
|||
} |
|||
|
|||
// CreateTag creates a tag object.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/git/tags/#create-a-tag-object
|
|||
func (s *GitService) CreateTag(owner string, repo string, tag *Tag) (*Tag, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/git/tags", owner, repo) |
|||
|
|||
// convert Tag into a createTagRequest
|
|||
tagRequest := &createTagRequest{ |
|||
Tag: tag.Tag, |
|||
Message: tag.Message, |
|||
Tagger: tag.Tagger, |
|||
} |
|||
if tag.Object != nil { |
|||
tagRequest.Object = tag.Object.SHA |
|||
tagRequest.Type = tag.Object.Type |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("POST", u, tagRequest) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
t := new(Tag) |
|||
resp, err := s.client.Do(req, t) |
|||
return t, resp, err |
|||
} |
@ -0,0 +1,68 @@ |
|||
// Copyright 2013 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestGitService_GetTag(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/git/tags/s", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeGitSigningPreview) |
|||
|
|||
fmt.Fprint(w, `{"tag": "t"}`) |
|||
}) |
|||
|
|||
tag, _, err := client.Git.GetTag("o", "r", "s") |
|||
if err != nil { |
|||
t.Errorf("Git.GetTag returned error: %v", err) |
|||
} |
|||
|
|||
want := &Tag{Tag: String("t")} |
|||
if !reflect.DeepEqual(tag, want) { |
|||
t.Errorf("Git.GetTag returned %+v, want %+v", tag, want) |
|||
} |
|||
} |
|||
|
|||
func TestGitService_CreateTag(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &createTagRequest{Tag: String("t"), Object: String("s")} |
|||
|
|||
mux.HandleFunc("/repos/o/r/git/tags", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(createTagRequest) |
|||
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, `{"tag": "t"}`) |
|||
}) |
|||
|
|||
tag, _, err := client.Git.CreateTag("o", "r", &Tag{ |
|||
Tag: input.Tag, |
|||
Object: &GitObject{SHA: input.Object}, |
|||
}) |
|||
if err != nil { |
|||
t.Errorf("Git.CreateTag returned error: %v", err) |
|||
} |
|||
|
|||
want := &Tag{Tag: String("t")} |
|||
if !reflect.DeepEqual(tag, want) { |
|||
t.Errorf("Git.GetTag returned %+v, want %+v", tag, want) |
|||
} |
|||
} |
@ -0,0 +1,89 @@ |
|||
// Copyright 2013 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 "fmt" |
|||
|
|||
// Tree represents a GitHub tree.
|
|||
type Tree struct { |
|||
SHA *string `json:"sha,omitempty"` |
|||
Entries []TreeEntry `json:"tree,omitempty"` |
|||
} |
|||
|
|||
func (t Tree) String() string { |
|||
return Stringify(t) |
|||
} |
|||
|
|||
// TreeEntry represents the contents of a tree structure. TreeEntry can
|
|||
// represent either a blob, a commit (in the case of a submodule), or another
|
|||
// tree.
|
|||
type TreeEntry struct { |
|||
SHA *string `json:"sha,omitempty"` |
|||
Path *string `json:"path,omitempty"` |
|||
Mode *string `json:"mode,omitempty"` |
|||
Type *string `json:"type,omitempty"` |
|||
Size *int `json:"size,omitempty"` |
|||
Content *string `json:"content,omitempty"` |
|||
} |
|||
|
|||
func (t TreeEntry) String() string { |
|||
return Stringify(t) |
|||
} |
|||
|
|||
// GetTree fetches the Tree object for a given sha hash from a repository.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/git/trees/#get-a-tree
|
|||
func (s *GitService) GetTree(owner string, repo string, sha string, recursive bool) (*Tree, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/git/trees/%v", owner, repo, sha) |
|||
if recursive { |
|||
u += "?recursive=1" |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
t := new(Tree) |
|||
resp, err := s.client.Do(req, t) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return t, resp, err |
|||
} |
|||
|
|||
// createTree represents the body of a CreateTree request.
|
|||
type createTree struct { |
|||
BaseTree string `json:"base_tree,omitempty"` |
|||
Entries []TreeEntry `json:"tree"` |
|||
} |
|||
|
|||
// CreateTree creates a new tree in a repository. If both a tree and a nested
|
|||
// path modifying that tree are specified, it will overwrite the contents of
|
|||
// that tree with the new path contents and write a new tree out.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/git/trees/#create-a-tree
|
|||
func (s *GitService) CreateTree(owner string, repo string, baseTree string, entries []TreeEntry) (*Tree, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/git/trees", owner, repo) |
|||
|
|||
body := &createTree{ |
|||
BaseTree: baseTree, |
|||
Entries: entries, |
|||
} |
|||
req, err := s.client.NewRequest("POST", u, body) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
t := new(Tree) |
|||
resp, err := s.client.Do(req, t) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return t, resp, err |
|||
} |
@ -0,0 +1,189 @@ |
|||
// Copyright 2013 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestGitService_GetTree(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/git/trees/s", func(w http.ResponseWriter, r *http.Request) { |
|||
if m := "GET"; m != r.Method { |
|||
t.Errorf("Request method = %v, want %v", r.Method, m) |
|||
} |
|||
fmt.Fprint(w, `{ |
|||
"sha": "s", |
|||
"tree": [ { "type": "blob" } ] |
|||
}`) |
|||
}) |
|||
|
|||
tree, _, err := client.Git.GetTree("o", "r", "s", true) |
|||
if err != nil { |
|||
t.Errorf("Git.GetTree returned error: %v", err) |
|||
} |
|||
|
|||
want := Tree{ |
|||
SHA: String("s"), |
|||
Entries: []TreeEntry{ |
|||
{ |
|||
Type: String("blob"), |
|||
}, |
|||
}, |
|||
} |
|||
if !reflect.DeepEqual(*tree, want) { |
|||
t.Errorf("Tree.Get returned %+v, want %+v", *tree, want) |
|||
} |
|||
} |
|||
|
|||
func TestGitService_GetTree_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Git.GetTree("%", "%", "%", false) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestGitService_CreateTree(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := []TreeEntry{ |
|||
{ |
|||
Path: String("file.rb"), |
|||
Mode: String("100644"), |
|||
Type: String("blob"), |
|||
SHA: String("7c258a9869f33c1e1e1f74fbb32f07c86cb5a75b"), |
|||
}, |
|||
} |
|||
|
|||
mux.HandleFunc("/repos/o/r/git/trees", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(createTree) |
|||
json.NewDecoder(r.Body).Decode(v) |
|||
|
|||
if m := "POST"; m != r.Method { |
|||
t.Errorf("Request method = %v, want %v", r.Method, m) |
|||
} |
|||
|
|||
want := &createTree{ |
|||
BaseTree: "b", |
|||
Entries: input, |
|||
} |
|||
if !reflect.DeepEqual(v, want) { |
|||
t.Errorf("Git.CreateTree request body: %+v, want %+v", v, want) |
|||
} |
|||
|
|||
fmt.Fprint(w, `{ |
|||
"sha": "cd8274d15fa3ae2ab983129fb037999f264ba9a7", |
|||
"tree": [ |
|||
{ |
|||
"path": "file.rb", |
|||
"mode": "100644", |
|||
"type": "blob", |
|||
"size": 132, |
|||
"sha": "7c258a9869f33c1e1e1f74fbb32f07c86cb5a75b" |
|||
} |
|||
] |
|||
}`) |
|||
}) |
|||
|
|||
tree, _, err := client.Git.CreateTree("o", "r", "b", input) |
|||
if err != nil { |
|||
t.Errorf("Git.CreateTree returned error: %v", err) |
|||
} |
|||
|
|||
want := Tree{ |
|||
String("cd8274d15fa3ae2ab983129fb037999f264ba9a7"), |
|||
[]TreeEntry{ |
|||
{ |
|||
Path: String("file.rb"), |
|||
Mode: String("100644"), |
|||
Type: String("blob"), |
|||
Size: Int(132), |
|||
SHA: String("7c258a9869f33c1e1e1f74fbb32f07c86cb5a75b"), |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
if !reflect.DeepEqual(*tree, want) { |
|||
t.Errorf("Git.CreateTree returned %+v, want %+v", *tree, want) |
|||
} |
|||
} |
|||
|
|||
func TestGitService_CreateTree_Content(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := []TreeEntry{ |
|||
{ |
|||
Path: String("content.md"), |
|||
Mode: String("100644"), |
|||
Content: String("file content"), |
|||
}, |
|||
} |
|||
|
|||
mux.HandleFunc("/repos/o/r/git/trees", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(createTree) |
|||
json.NewDecoder(r.Body).Decode(v) |
|||
|
|||
if m := "POST"; m != r.Method { |
|||
t.Errorf("Request method = %v, want %v", r.Method, m) |
|||
} |
|||
|
|||
want := &createTree{ |
|||
BaseTree: "b", |
|||
Entries: input, |
|||
} |
|||
if !reflect.DeepEqual(v, want) { |
|||
t.Errorf("Git.CreateTree request body: %+v, want %+v", v, want) |
|||
} |
|||
|
|||
fmt.Fprint(w, `{ |
|||
"sha": "5c6780ad2c68743383b740fd1dab6f6a33202b11", |
|||
"url": "https://api.github.com/repos/o/r/git/trees/5c6780ad2c68743383b740fd1dab6f6a33202b11", |
|||
"tree": [ |
|||
{ |
|||
"mode": "100644", |
|||
"type": "blob", |
|||
"sha": "aad8feacf6f8063150476a7b2bd9770f2794c08b", |
|||
"path": "content.md", |
|||
"size": 12, |
|||
"url": "https://api.github.com/repos/o/r/git/blobs/aad8feacf6f8063150476a7b2bd9770f2794c08b" |
|||
} |
|||
] |
|||
}`) |
|||
}) |
|||
|
|||
tree, _, err := client.Git.CreateTree("o", "r", "b", input) |
|||
if err != nil { |
|||
t.Errorf("Git.CreateTree returned error: %v", err) |
|||
} |
|||
|
|||
want := Tree{ |
|||
String("5c6780ad2c68743383b740fd1dab6f6a33202b11"), |
|||
[]TreeEntry{ |
|||
{ |
|||
Path: String("content.md"), |
|||
Mode: String("100644"), |
|||
Type: String("blob"), |
|||
Size: Int(12), |
|||
SHA: String("aad8feacf6f8063150476a7b2bd9770f2794c08b"), |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
if !reflect.DeepEqual(*tree, want) { |
|||
t.Errorf("Git.CreateTree returned %+v, want %+v", *tree, want) |
|||
} |
|||
} |
|||
|
|||
func TestGitService_CreateTree_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Git.CreateTree("%", "%", "", nil) |
|||
testURLParseError(t, err) |
|||
} |
@ -0,0 +1,807 @@ |
|||
// Copyright 2013 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 ( |
|||
"bytes" |
|||
"encoding/json" |
|||
"errors" |
|||
"fmt" |
|||
"io" |
|||
"io/ioutil" |
|||
"net/http" |
|||
"net/url" |
|||
"reflect" |
|||
"strconv" |
|||
"strings" |
|||
"sync" |
|||
"time" |
|||
|
|||
"github.com/google/go-querystring/query" |
|||
) |
|||
|
|||
const ( |
|||
// StatusUnprocessableEntity is the status code returned when sending a request with invalid fields.
|
|||
StatusUnprocessableEntity = 422 |
|||
) |
|||
|
|||
const ( |
|||
libraryVersion = "2" |
|||
defaultBaseURL = "https://api.github.com/" |
|||
uploadBaseURL = "https://uploads.github.com/" |
|||
userAgent = "go-github/" + libraryVersion |
|||
|
|||
headerRateLimit = "X-RateLimit-Limit" |
|||
headerRateRemaining = "X-RateLimit-Remaining" |
|||
headerRateReset = "X-RateLimit-Reset" |
|||
headerOTP = "X-GitHub-OTP" |
|||
|
|||
mediaTypeV3 = "application/vnd.github.v3+json" |
|||
defaultMediaType = "application/octet-stream" |
|||
mediaTypeV3SHA = "application/vnd.github.v3.sha" |
|||
mediaTypeOrgPermissionRepo = "application/vnd.github.v3.repository+json" |
|||
|
|||
// Media Type values to access preview APIs
|
|||
|
|||
// https://developer.github.com/changes/2015-03-09-licenses-api/
|
|||
mediaTypeLicensesPreview = "application/vnd.github.drax-preview+json" |
|||
|
|||
// https://developer.github.com/changes/2014-12-09-new-attributes-for-stars-api/
|
|||
mediaTypeStarringPreview = "application/vnd.github.v3.star+json" |
|||
|
|||
// https://developer.github.com/changes/2015-11-11-protected-branches-api/
|
|||
mediaTypeProtectedBranchesPreview = "application/vnd.github.loki-preview+json" |
|||
|
|||
// https://help.github.com/enterprise/2.4/admin/guides/migrations/exporting-the-github-com-organization-s-repositories/
|
|||
mediaTypeMigrationsPreview = "application/vnd.github.wyandotte-preview+json" |
|||
|
|||
// https://developer.github.com/changes/2016-04-06-deployment-and-deployment-status-enhancements/
|
|||
mediaTypeDeploymentStatusPreview = "application/vnd.github.ant-man-preview+json" |
|||
|
|||
// https://developer.github.com/changes/2016-02-19-source-import-preview-api/
|
|||
mediaTypeImportPreview = "application/vnd.github.barred-rock-preview" |
|||
|
|||
// https://developer.github.com/changes/2016-05-12-reactions-api-preview/
|
|||
mediaTypeReactionsPreview = "application/vnd.github.squirrel-girl-preview" |
|||
|
|||
// https://developer.github.com/changes/2016-04-01-squash-api-preview/
|
|||
mediaTypeSquashPreview = "application/vnd.github.polaris-preview+json" |
|||
|
|||
// https://developer.github.com/changes/2016-04-04-git-signing-api-preview/
|
|||
mediaTypeGitSigningPreview = "application/vnd.github.cryptographer-preview+json" |
|||
|
|||
// https://developer.github.com/changes/2016-05-23-timeline-preview-api/
|
|||
mediaTypeTimelinePreview = "application/vnd.github.mockingbird-preview+json" |
|||
|
|||
// https://developer.github.com/changes/2016-06-14-repository-invitations/
|
|||
mediaTypeRepositoryInvitationsPreview = "application/vnd.github.swamp-thing-preview+json" |
|||
|
|||
// https://developer.github.com/changes/2016-04-21-oauth-authorizations-grants-api-preview/
|
|||
mediaTypeOAuthGrantAuthorizationsPreview = "application/vnd.github.damage-preview+json" |
|||
|
|||
// https://developer.github.com/changes/2016-07-06-github-pages-preiew-api/
|
|||
mediaTypePagesPreview = "application/vnd.github.mister-fantastic-preview+json" |
|||
) |
|||
|
|||
// A Client manages communication with the GitHub API.
|
|||
type Client struct { |
|||
clientMu sync.Mutex // clientMu protects the client during calls that modify the CheckRedirect func.
|
|||
client *http.Client // HTTP client used to communicate with the API.
|
|||
|
|||
// Base URL for API requests. Defaults to the public GitHub API, but can be
|
|||
// set to a domain endpoint to use with GitHub Enterprise. BaseURL should
|
|||
// always be specified with a trailing slash.
|
|||
BaseURL *url.URL |
|||
|
|||
// Base URL for uploading files.
|
|||
UploadURL *url.URL |
|||
|
|||
// User agent used when communicating with the GitHub API.
|
|||
UserAgent string |
|||
|
|||
rateMu sync.Mutex |
|||
rateLimits [categories]Rate // Rate limits for the client as determined by the most recent API calls.
|
|||
mostRecent rateLimitCategory |
|||
|
|||
common service // Reuse a single struct instead of allocating one for each service on the heap.
|
|||
|
|||
// Services used for talking to different parts of the GitHub API.
|
|||
Activity *ActivityService |
|||
Authorizations *AuthorizationsService |
|||
Gists *GistsService |
|||
Git *GitService |
|||
Gitignores *GitignoresService |
|||
Issues *IssuesService |
|||
Organizations *OrganizationsService |
|||
PullRequests *PullRequestsService |
|||
Repositories *RepositoriesService |
|||
Search *SearchService |
|||
Users *UsersService |
|||
Licenses *LicensesService |
|||
Migrations *MigrationService |
|||
Reactions *ReactionsService |
|||
} |
|||
|
|||
type service struct { |
|||
client *Client |
|||
} |
|||
|
|||
// ListOptions specifies the optional parameters to various List methods that
|
|||
// support pagination.
|
|||
type ListOptions struct { |
|||
// For paginated result sets, page of results to retrieve.
|
|||
Page int `url:"page,omitempty"` |
|||
|
|||
// For paginated result sets, the number of results to include per page.
|
|||
PerPage int `url:"per_page,omitempty"` |
|||
} |
|||
|
|||
// UploadOptions specifies the parameters to methods that support uploads.
|
|||
type UploadOptions struct { |
|||
Name string `url:"name,omitempty"` |
|||
} |
|||
|
|||
// addOptions adds the parameters in opt as URL query parameters to s. opt
|
|||
// must be a struct whose fields may contain "url" tags.
|
|||
func addOptions(s string, opt interface{}) (string, error) { |
|||
v := reflect.ValueOf(opt) |
|||
if v.Kind() == reflect.Ptr && v.IsNil() { |
|||
return s, nil |
|||
} |
|||
|
|||
u, err := url.Parse(s) |
|||
if err != nil { |
|||
return s, err |
|||
} |
|||
|
|||
qs, err := query.Values(opt) |
|||
if err != nil { |
|||
return s, err |
|||
} |
|||
|
|||
u.RawQuery = qs.Encode() |
|||
return u.String(), nil |
|||
} |
|||
|
|||
// NewClient returns a new GitHub API client. If a nil httpClient is
|
|||
// provided, http.DefaultClient will be used. To use API methods which require
|
|||
// authentication, provide an http.Client that will perform the authentication
|
|||
// for you (such as that provided by the golang.org/x/oauth2 library).
|
|||
func NewClient(httpClient *http.Client) *Client { |
|||
if httpClient == nil { |
|||
httpClient = http.DefaultClient |
|||
} |
|||
baseURL, _ := url.Parse(defaultBaseURL) |
|||
uploadURL, _ := url.Parse(uploadBaseURL) |
|||
|
|||
c := &Client{client: httpClient, BaseURL: baseURL, UserAgent: userAgent, UploadURL: uploadURL} |
|||
c.common.client = c |
|||
c.Activity = (*ActivityService)(&c.common) |
|||
c.Authorizations = (*AuthorizationsService)(&c.common) |
|||
c.Gists = (*GistsService)(&c.common) |
|||
c.Git = (*GitService)(&c.common) |
|||
c.Gitignores = (*GitignoresService)(&c.common) |
|||
c.Issues = (*IssuesService)(&c.common) |
|||
c.Licenses = (*LicensesService)(&c.common) |
|||
c.Migrations = (*MigrationService)(&c.common) |
|||
c.Organizations = (*OrganizationsService)(&c.common) |
|||
c.PullRequests = (*PullRequestsService)(&c.common) |
|||
c.Reactions = (*ReactionsService)(&c.common) |
|||
c.Repositories = (*RepositoriesService)(&c.common) |
|||
c.Search = (*SearchService)(&c.common) |
|||
c.Users = (*UsersService)(&c.common) |
|||
return c |
|||
} |
|||
|
|||
// NewRequest creates an API request. A relative URL can be provided in urlStr,
|
|||
// in which case it is resolved relative to the BaseURL of the Client.
|
|||
// Relative URLs should always be specified without a preceding slash. If
|
|||
// specified, the value pointed to by body is JSON encoded and included as the
|
|||
// request body.
|
|||
func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) { |
|||
rel, err := url.Parse(urlStr) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
u := c.BaseURL.ResolveReference(rel) |
|||
|
|||
var buf io.ReadWriter |
|||
if body != nil { |
|||
buf = new(bytes.Buffer) |
|||
err := json.NewEncoder(buf).Encode(body) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
} |
|||
|
|||
req, err := http.NewRequest(method, u.String(), buf) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
if body != nil { |
|||
req.Header.Set("Content-Type", "application/json") |
|||
} |
|||
req.Header.Set("Accept", mediaTypeV3) |
|||
if c.UserAgent != "" { |
|||
req.Header.Set("User-Agent", c.UserAgent) |
|||
} |
|||
return req, nil |
|||
} |
|||
|
|||
// NewUploadRequest creates an upload request. A relative URL can be provided in
|
|||
// urlStr, in which case it is resolved relative to the UploadURL of the Client.
|
|||
// Relative URLs should always be specified without a preceding slash.
|
|||
func (c *Client) NewUploadRequest(urlStr string, reader io.Reader, size int64, mediaType string) (*http.Request, error) { |
|||
rel, err := url.Parse(urlStr) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
u := c.UploadURL.ResolveReference(rel) |
|||
req, err := http.NewRequest("POST", u.String(), reader) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
req.ContentLength = size |
|||
|
|||
if mediaType == "" { |
|||
mediaType = defaultMediaType |
|||
} |
|||
req.Header.Set("Content-Type", mediaType) |
|||
req.Header.Set("Accept", mediaTypeV3) |
|||
req.Header.Set("User-Agent", c.UserAgent) |
|||
return req, nil |
|||
} |
|||
|
|||
// Response is a GitHub API response. This wraps the standard http.Response
|
|||
// returned from GitHub and provides convenient access to things like
|
|||
// pagination links.
|
|||
type Response struct { |
|||
*http.Response |
|||
|
|||
// These fields provide the page values for paginating through a set of
|
|||
// results. Any or all of these may be set to the zero value for
|
|||
// responses that are not part of a paginated set, or for which there
|
|||
// are no additional pages.
|
|||
|
|||
NextPage int |
|||
PrevPage int |
|||
FirstPage int |
|||
LastPage int |
|||
|
|||
Rate |
|||
} |
|||
|
|||
// newResponse creates a new Response for the provided http.Response.
|
|||
func newResponse(r *http.Response) *Response { |
|||
response := &Response{Response: r} |
|||
response.populatePageValues() |
|||
response.Rate = parseRate(r) |
|||
return response |
|||
} |
|||
|
|||
// populatePageValues parses the HTTP Link response headers and populates the
|
|||
// various pagination link values in the Response.
|
|||
func (r *Response) populatePageValues() { |
|||
if links, ok := r.Response.Header["Link"]; ok && len(links) > 0 { |
|||
for _, link := range strings.Split(links[0], ",") { |
|||
segments := strings.Split(strings.TrimSpace(link), ";") |
|||
|
|||
// link must at least have href and rel
|
|||
if len(segments) < 2 { |
|||
continue |
|||
} |
|||
|
|||
// ensure href is properly formatted
|
|||
if !strings.HasPrefix(segments[0], "<") || !strings.HasSuffix(segments[0], ">") { |
|||
continue |
|||
} |
|||
|
|||
// try to pull out page parameter
|
|||
url, err := url.Parse(segments[0][1 : len(segments[0])-1]) |
|||
if err != nil { |
|||
continue |
|||
} |
|||
page := url.Query().Get("page") |
|||
if page == "" { |
|||
continue |
|||
} |
|||
|
|||
for _, segment := range segments[1:] { |
|||
switch strings.TrimSpace(segment) { |
|||
case `rel="next"`: |
|||
r.NextPage, _ = strconv.Atoi(page) |
|||
case `rel="prev"`: |
|||
r.PrevPage, _ = strconv.Atoi(page) |
|||
case `rel="first"`: |
|||
r.FirstPage, _ = strconv.Atoi(page) |
|||
case `rel="last"`: |
|||
r.LastPage, _ = strconv.Atoi(page) |
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// parseRate parses the rate related headers.
|
|||
func parseRate(r *http.Response) Rate { |
|||
var rate Rate |
|||
if limit := r.Header.Get(headerRateLimit); limit != "" { |
|||
rate.Limit, _ = strconv.Atoi(limit) |
|||
} |
|||
if remaining := r.Header.Get(headerRateRemaining); remaining != "" { |
|||
rate.Remaining, _ = strconv.Atoi(remaining) |
|||
} |
|||
if reset := r.Header.Get(headerRateReset); reset != "" { |
|||
if v, _ := strconv.ParseInt(reset, 10, 64); v != 0 { |
|||
rate.Reset = Timestamp{time.Unix(v, 0)} |
|||
} |
|||
} |
|||
return rate |
|||
} |
|||
|
|||
// Rate specifies the current rate limit for the client as determined by the
|
|||
// most recent API call. If the client is used in a multi-user application,
|
|||
// this rate may not always be up-to-date.
|
|||
//
|
|||
// Deprecated: Use the Response.Rate returned from most recent API call instead.
|
|||
// Call RateLimits() to check the current rate.
|
|||
func (c *Client) Rate() Rate { |
|||
c.rateMu.Lock() |
|||
rate := c.rateLimits[c.mostRecent] |
|||
c.rateMu.Unlock() |
|||
return rate |
|||
} |
|||
|
|||
// Do sends an API request and returns the API response. The API response is
|
|||
// JSON decoded and stored in the value pointed to by v, or returned as an
|
|||
// error if an API error has occurred. If v implements the io.Writer
|
|||
// interface, the raw response body will be written to v, without attempting to
|
|||
// first decode it. If rate limit is exceeded and reset time is in the future,
|
|||
// Do returns *RateLimitError immediately without making a network API call.
|
|||
func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) { |
|||
rateLimitCategory := category(req.URL.Path) |
|||
|
|||
// If we've hit rate limit, don't make further requests before Reset time.
|
|||
if err := c.checkRateLimitBeforeDo(req, rateLimitCategory); err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
resp, err := c.client.Do(req) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
defer func() { |
|||
// Drain up to 512 bytes and close the body to let the Transport reuse the connection
|
|||
io.CopyN(ioutil.Discard, resp.Body, 512) |
|||
resp.Body.Close() |
|||
}() |
|||
|
|||
response := newResponse(resp) |
|||
|
|||
c.rateMu.Lock() |
|||
c.rateLimits[rateLimitCategory] = response.Rate |
|||
c.mostRecent = rateLimitCategory |
|||
c.rateMu.Unlock() |
|||
|
|||
err = CheckResponse(resp) |
|||
if err != nil { |
|||
// even though there was an error, we still return the response
|
|||
// in case the caller wants to inspect it further
|
|||
return response, err |
|||
} |
|||
|
|||
if v != nil { |
|||
if w, ok := v.(io.Writer); ok { |
|||
io.Copy(w, resp.Body) |
|||
} else { |
|||
err = json.NewDecoder(resp.Body).Decode(v) |
|||
if err == io.EOF { |
|||
err = nil // ignore EOF errors caused by empty response body
|
|||
} |
|||
} |
|||
} |
|||
|
|||
return response, err |
|||
} |
|||
|
|||
// checkRateLimitBeforeDo does not make any network calls, but uses existing knowledge from
|
|||
// current client state in order to quickly check if *RateLimitError can be immediately returned
|
|||
// from Client.Do, and if so, returns it so that Client.Do can skip making a network API call unneccessarily.
|
|||
// Otherwise it returns nil, and Client.Do should proceed normally.
|
|||
func (c *Client) checkRateLimitBeforeDo(req *http.Request, rateLimitCategory rateLimitCategory) error { |
|||
c.rateMu.Lock() |
|||
rate := c.rateLimits[rateLimitCategory] |
|||
c.rateMu.Unlock() |
|||
if !rate.Reset.Time.IsZero() && rate.Remaining == 0 && time.Now().Before(rate.Reset.Time) { |
|||
// Create a fake response.
|
|||
resp := &http.Response{ |
|||
Status: http.StatusText(http.StatusForbidden), |
|||
StatusCode: http.StatusForbidden, |
|||
Request: req, |
|||
Header: make(http.Header), |
|||
Body: ioutil.NopCloser(strings.NewReader("")), |
|||
} |
|||
return &RateLimitError{ |
|||
Rate: rate, |
|||
Response: resp, |
|||
Message: fmt.Sprintf("API rate limit of %v still exceeded until %v, not making remote request.", rate.Limit, rate.Reset.Time), |
|||
} |
|||
} |
|||
|
|||
return nil |
|||
} |
|||
|
|||
/* |
|||
An ErrorResponse reports one or more errors caused by an API request. |
|||
|
|||
GitHub API docs: http://developer.github.com/v3/#client-errors
|
|||
*/ |
|||
type ErrorResponse struct { |
|||
Response *http.Response // HTTP response that caused this error
|
|||
Message string `json:"message"` // error message
|
|||
Errors []Error `json:"errors"` // more detail on individual errors
|
|||
// Block is only populated on certain types of errors such as code 451.
|
|||
// See https://developer.github.com/changes/2016-03-17-the-451-status-code-is-now-supported/
|
|||
// for more information.
|
|||
Block *struct { |
|||
Reason string `json:"reason,omitempty"` |
|||
CreatedAt *Timestamp `json:"created_at,omitempty"` |
|||
} `json:"block,omitempty"` |
|||
// Most errors will also include a documentation_url field pointing
|
|||
// to some content that might help you resolve the error, see
|
|||
// https://developer.github.com/v3/#client-errors
|
|||
DocumentationURL string `json:"documentation_url,omitempty"` |
|||
} |
|||
|
|||
func (r *ErrorResponse) Error() string { |
|||
return fmt.Sprintf("%v %v: %d %v %+v", |
|||
r.Response.Request.Method, sanitizeURL(r.Response.Request.URL), |
|||
r.Response.StatusCode, r.Message, r.Errors) |
|||
} |
|||
|
|||
// TwoFactorAuthError occurs when using HTTP Basic Authentication for a user
|
|||
// that has two-factor authentication enabled. The request can be reattempted
|
|||
// by providing a one-time password in the request.
|
|||
type TwoFactorAuthError ErrorResponse |
|||
|
|||
func (r *TwoFactorAuthError) Error() string { return (*ErrorResponse)(r).Error() } |
|||
|
|||
// RateLimitError occurs when GitHub returns 403 Forbidden response with a rate limit
|
|||
// remaining value of 0, and error message starts with "API rate limit exceeded for ".
|
|||
type RateLimitError struct { |
|||
Rate Rate // Rate specifies last known rate limit for the client
|
|||
Response *http.Response // HTTP response that caused this error
|
|||
Message string `json:"message"` // error message
|
|||
} |
|||
|
|||
func (r *RateLimitError) Error() string { |
|||
return fmt.Sprintf("%v %v: %d %v; rate reset in %v", |
|||
r.Response.Request.Method, sanitizeURL(r.Response.Request.URL), |
|||
r.Response.StatusCode, r.Message, r.Rate.Reset.Time.Sub(time.Now())) |
|||
} |
|||
|
|||
// sanitizeURL redacts the client_secret parameter from the URL which may be
|
|||
// exposed to the user, specifically in the ErrorResponse error message.
|
|||
func sanitizeURL(uri *url.URL) *url.URL { |
|||
if uri == nil { |
|||
return nil |
|||
} |
|||
params := uri.Query() |
|||
if len(params.Get("client_secret")) > 0 { |
|||
params.Set("client_secret", "REDACTED") |
|||
uri.RawQuery = params.Encode() |
|||
} |
|||
return uri |
|||
} |
|||
|
|||
/* |
|||
An Error reports more details on an individual error in an ErrorResponse. |
|||
These are the possible validation error codes: |
|||
|
|||
missing: |
|||
resource does not exist |
|||
missing_field: |
|||
a required field on a resource has not been set |
|||
invalid: |
|||
the formatting of a field is invalid |
|||
already_exists: |
|||
another resource has the same valid as this field |
|||
custom: |
|||
some resources return this (e.g. github.User.CreateKey()), additional |
|||
information is set in the Message field of the Error |
|||
|
|||
GitHub API docs: http://developer.github.com/v3/#client-errors
|
|||
*/ |
|||
type Error struct { |
|||
Resource string `json:"resource"` // resource on which the error occurred
|
|||
Field string `json:"field"` // field on which the error occurred
|
|||
Code string `json:"code"` // validation error code
|
|||
Message string `json:"message"` // Message describing the error. Errors with Code == "custom" will always have this set.
|
|||
} |
|||
|
|||
func (e *Error) Error() string { |
|||
return fmt.Sprintf("%v error caused by %v field on %v resource", |
|||
e.Code, e.Field, e.Resource) |
|||
} |
|||
|
|||
// CheckResponse checks the API response for errors, and returns them if
|
|||
// present. A response is considered an error if it has a status code outside
|
|||
// the 200 range. API error responses are expected to have either no response
|
|||
// body, or a JSON response body that maps to ErrorResponse. Any other
|
|||
// response body will be silently ignored.
|
|||
//
|
|||
// The error type will be *RateLimitError for rate limit exceeded errors,
|
|||
// and *TwoFactorAuthError for two-factor authentication errors.
|
|||
func CheckResponse(r *http.Response) error { |
|||
if c := r.StatusCode; 200 <= c && c <= 299 { |
|||
return nil |
|||
} |
|||
errorResponse := &ErrorResponse{Response: r} |
|||
data, err := ioutil.ReadAll(r.Body) |
|||
if err == nil && data != nil { |
|||
json.Unmarshal(data, errorResponse) |
|||
} |
|||
switch { |
|||
case r.StatusCode == http.StatusUnauthorized && strings.HasPrefix(r.Header.Get(headerOTP), "required"): |
|||
return (*TwoFactorAuthError)(errorResponse) |
|||
case r.StatusCode == http.StatusForbidden && r.Header.Get(headerRateRemaining) == "0" && strings.HasPrefix(errorResponse.Message, "API rate limit exceeded for "): |
|||
return &RateLimitError{ |
|||
Rate: parseRate(r), |
|||
Response: errorResponse.Response, |
|||
Message: errorResponse.Message, |
|||
} |
|||
default: |
|||
return errorResponse |
|||
} |
|||
} |
|||
|
|||
// parseBoolResponse determines the boolean result from a GitHub API response.
|
|||
// Several GitHub API methods return boolean responses indicated by the HTTP
|
|||
// status code in the response (true indicated by a 204, false indicated by a
|
|||
// 404). This helper function will determine that result and hide the 404
|
|||
// error if present. Any other error will be returned through as-is.
|
|||
func parseBoolResponse(err error) (bool, error) { |
|||
if err == nil { |
|||
return true, nil |
|||
} |
|||
|
|||
if err, ok := err.(*ErrorResponse); ok && err.Response.StatusCode == http.StatusNotFound { |
|||
// Simply false. In this one case, we do not pass the error through.
|
|||
return false, nil |
|||
} |
|||
|
|||
// some other real error occurred
|
|||
return false, err |
|||
} |
|||
|
|||
// Rate represents the rate limit for the current client.
|
|||
type Rate struct { |
|||
// The number of requests per hour the client is currently limited to.
|
|||
Limit int `json:"limit"` |
|||
|
|||
// The number of remaining requests the client can make this hour.
|
|||
Remaining int `json:"remaining"` |
|||
|
|||
// The time at which the current rate limit will reset.
|
|||
Reset Timestamp `json:"reset"` |
|||
} |
|||
|
|||
func (r Rate) String() string { |
|||
return Stringify(r) |
|||
} |
|||
|
|||
// RateLimits represents the rate limits for the current client.
|
|||
type RateLimits struct { |
|||
// The rate limit for non-search API requests. Unauthenticated
|
|||
// requests are limited to 60 per hour. Authenticated requests are
|
|||
// limited to 5,000 per hour.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/#rate-limiting
|
|||
Core *Rate `json:"core"` |
|||
|
|||
// The rate limit for search API requests. Unauthenticated requests
|
|||
// are limited to 5 requests per minutes. Authenticated requests are
|
|||
// limited to 20 per minute.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/search/#rate-limit
|
|||
Search *Rate `json:"search"` |
|||
} |
|||
|
|||
func (r RateLimits) String() string { |
|||
return Stringify(r) |
|||
} |
|||
|
|||
type rateLimitCategory uint8 |
|||
|
|||
const ( |
|||
coreCategory rateLimitCategory = iota |
|||
searchCategory |
|||
|
|||
categories // An array of this length will be able to contain all rate limit categories.
|
|||
) |
|||
|
|||
// category returns the rate limit category of the endpoint, determined by Request.URL.Path.
|
|||
func category(path string) rateLimitCategory { |
|||
switch { |
|||
default: |
|||
return coreCategory |
|||
case strings.HasPrefix(path, "/search/"): |
|||
return searchCategory |
|||
} |
|||
} |
|||
|
|||
// Deprecated: RateLimit is deprecated, use RateLimits instead.
|
|||
func (c *Client) RateLimit() (*Rate, *Response, error) { |
|||
limits, resp, err := c.RateLimits() |
|||
if limits == nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
return limits.Core, resp, err |
|||
} |
|||
|
|||
// RateLimits returns the rate limits for the current client.
|
|||
func (c *Client) RateLimits() (*RateLimits, *Response, error) { |
|||
req, err := c.NewRequest("GET", "rate_limit", nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
response := new(struct { |
|||
Resources *RateLimits `json:"resources"` |
|||
}) |
|||
resp, err := c.Do(req, response) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
if response.Resources != nil { |
|||
c.rateMu.Lock() |
|||
if response.Resources.Core != nil { |
|||
c.rateLimits[coreCategory] = *response.Resources.Core |
|||
} |
|||
if response.Resources.Search != nil { |
|||
c.rateLimits[searchCategory] = *response.Resources.Search |
|||
} |
|||
c.rateMu.Unlock() |
|||
} |
|||
|
|||
return response.Resources, resp, err |
|||
} |
|||
|
|||
/* |
|||
UnauthenticatedRateLimitedTransport allows you to make unauthenticated calls |
|||
that need to use a higher rate limit associated with your OAuth application. |
|||
|
|||
t := &github.UnauthenticatedRateLimitedTransport{ |
|||
ClientID: "your app's client ID", |
|||
ClientSecret: "your app's client secret", |
|||
} |
|||
client := github.NewClient(t.Client()) |
|||
|
|||
This will append the querystring params client_id=xxx&client_secret=yyy to all |
|||
requests. |
|||
|
|||
See http://developer.github.com/v3/#unauthenticated-rate-limited-requests for
|
|||
more information. |
|||
*/ |
|||
type UnauthenticatedRateLimitedTransport struct { |
|||
// ClientID is the GitHub OAuth client ID of the current application, which
|
|||
// can be found by selecting its entry in the list at
|
|||
// https://github.com/settings/applications.
|
|||
ClientID string |
|||
|
|||
// ClientSecret is the GitHub OAuth client secret of the current
|
|||
// application.
|
|||
ClientSecret string |
|||
|
|||
// Transport is the underlying HTTP transport to use when making requests.
|
|||
// It will default to http.DefaultTransport if nil.
|
|||
Transport http.RoundTripper |
|||
} |
|||
|
|||
// RoundTrip implements the RoundTripper interface.
|
|||
func (t *UnauthenticatedRateLimitedTransport) RoundTrip(req *http.Request) (*http.Response, error) { |
|||
if t.ClientID == "" { |
|||
return nil, errors.New("t.ClientID is empty") |
|||
} |
|||
if t.ClientSecret == "" { |
|||
return nil, errors.New("t.ClientSecret is empty") |
|||
} |
|||
|
|||
// To set extra querystring params, we must make a copy of the Request so
|
|||
// that we don't modify the Request we were given. This is required by the
|
|||
// specification of http.RoundTripper.
|
|||
req = cloneRequest(req) |
|||
q := req.URL.Query() |
|||
q.Set("client_id", t.ClientID) |
|||
q.Set("client_secret", t.ClientSecret) |
|||
req.URL.RawQuery = q.Encode() |
|||
|
|||
// Make the HTTP request.
|
|||
return t.transport().RoundTrip(req) |
|||
} |
|||
|
|||
// Client returns an *http.Client that makes requests which are subject to the
|
|||
// rate limit of your OAuth application.
|
|||
func (t *UnauthenticatedRateLimitedTransport) Client() *http.Client { |
|||
return &http.Client{Transport: t} |
|||
} |
|||
|
|||
func (t *UnauthenticatedRateLimitedTransport) transport() http.RoundTripper { |
|||
if t.Transport != nil { |
|||
return t.Transport |
|||
} |
|||
return http.DefaultTransport |
|||
} |
|||
|
|||
// BasicAuthTransport is an http.RoundTripper that authenticates all requests
|
|||
// using HTTP Basic Authentication with the provided username and password. It
|
|||
// additionally supports users who have two-factor authentication enabled on
|
|||
// their GitHub account.
|
|||
type BasicAuthTransport struct { |
|||
Username string // GitHub username
|
|||
Password string // GitHub password
|
|||
OTP string // one-time password for users with two-factor auth enabled
|
|||
|
|||
// Transport is the underlying HTTP transport to use when making requests.
|
|||
// It will default to http.DefaultTransport if nil.
|
|||
Transport http.RoundTripper |
|||
} |
|||
|
|||
// RoundTrip implements the RoundTripper interface.
|
|||
func (t *BasicAuthTransport) RoundTrip(req *http.Request) (*http.Response, error) { |
|||
req = cloneRequest(req) // per RoundTrip contract
|
|||
req.SetBasicAuth(t.Username, t.Password) |
|||
if t.OTP != "" { |
|||
req.Header.Set(headerOTP, t.OTP) |
|||
} |
|||
return t.transport().RoundTrip(req) |
|||
} |
|||
|
|||
// Client returns an *http.Client that makes requests that are authenticated
|
|||
// using HTTP Basic Authentication.
|
|||
func (t *BasicAuthTransport) Client() *http.Client { |
|||
return &http.Client{Transport: t} |
|||
} |
|||
|
|||
func (t *BasicAuthTransport) transport() http.RoundTripper { |
|||
if t.Transport != nil { |
|||
return t.Transport |
|||
} |
|||
return http.DefaultTransport |
|||
} |
|||
|
|||
// cloneRequest returns a clone of the provided *http.Request. The clone is a
|
|||
// shallow copy of the struct and its Header map.
|
|||
func cloneRequest(r *http.Request) *http.Request { |
|||
// shallow copy of the struct
|
|||
r2 := new(http.Request) |
|||
*r2 = *r |
|||
// deep copy of the Header
|
|||
r2.Header = make(http.Header, len(r.Header)) |
|||
for k, s := range r.Header { |
|||
r2.Header[k] = append([]string(nil), s...) |
|||
} |
|||
return r2 |
|||
} |
|||
|
|||
// Bool is a helper routine that allocates a new bool value
|
|||
// to store v and returns a pointer to it.
|
|||
func Bool(v bool) *bool { return &v } |
|||
|
|||
// Int is a helper routine that allocates a new int value
|
|||
// to store v and returns a pointer to it.
|
|||
func Int(v int) *int { return &v } |
|||
|
|||
// String is a helper routine that allocates a new string value
|
|||
// to store v and returns a pointer to it.
|
|||
func String(v string) *string { return &v } |
@ -0,0 +1,865 @@ |
|||
// Copyright 2013 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 ( |
|||
"bytes" |
|||
"encoding/json" |
|||
"fmt" |
|||
"io/ioutil" |
|||
"net/http" |
|||
"net/http/httptest" |
|||
"net/url" |
|||
"os" |
|||
"path" |
|||
"reflect" |
|||
"strings" |
|||
"testing" |
|||
"time" |
|||
) |
|||
|
|||
var ( |
|||
// mux is the HTTP request multiplexer used with the test server.
|
|||
mux *http.ServeMux |
|||
|
|||
// client is the GitHub client being tested.
|
|||
client *Client |
|||
|
|||
// server is a test HTTP server used to provide mock API responses.
|
|||
server *httptest.Server |
|||
) |
|||
|
|||
// setup sets up a test HTTP server along with a github.Client that is
|
|||
// configured to talk to that test server. Tests should register handlers on
|
|||
// mux which provide mock responses for the API method being tested.
|
|||
func setup() { |
|||
// test server
|
|||
mux = http.NewServeMux() |
|||
server = httptest.NewServer(mux) |
|||
|
|||
// github client configured to use test server
|
|||
client = NewClient(nil) |
|||
url, _ := url.Parse(server.URL) |
|||
client.BaseURL = url |
|||
client.UploadURL = url |
|||
} |
|||
|
|||
// teardown closes the test HTTP server.
|
|||
func teardown() { |
|||
server.Close() |
|||
} |
|||
|
|||
// openTestFile creates a new file with the given name and content for testing.
|
|||
// In order to ensure the exact file name, this function will create a new temp
|
|||
// directory, and create the file in that directory. It is the caller's
|
|||
// responsibility to remove the directory and its contents when no longer needed.
|
|||
func openTestFile(name, content string) (file *os.File, dir string, err error) { |
|||
dir, err = ioutil.TempDir("", "go-github") |
|||
if err != nil { |
|||
return nil, dir, err |
|||
} |
|||
|
|||
file, err = os.OpenFile(path.Join(dir, name), os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) |
|||
if err != nil { |
|||
return nil, dir, err |
|||
} |
|||
|
|||
fmt.Fprint(file, content) |
|||
|
|||
// close and re-open the file to keep file.Stat() happy
|
|||
file.Close() |
|||
file, err = os.Open(file.Name()) |
|||
if err != nil { |
|||
return nil, dir, err |
|||
} |
|||
|
|||
return file, dir, err |
|||
} |
|||
|
|||
func testMethod(t *testing.T, r *http.Request, want string) { |
|||
if got := r.Method; got != want { |
|||
t.Errorf("Request method: %v, want %v", got, want) |
|||
} |
|||
} |
|||
|
|||
type values map[string]string |
|||
|
|||
func testFormValues(t *testing.T, r *http.Request, values values) { |
|||
want := url.Values{} |
|||
for k, v := range values { |
|||
want.Add(k, v) |
|||
} |
|||
|
|||
r.ParseForm() |
|||
if got := r.Form; !reflect.DeepEqual(got, want) { |
|||
t.Errorf("Request parameters: %v, want %v", got, want) |
|||
} |
|||
} |
|||
|
|||
func testHeader(t *testing.T, r *http.Request, header string, want string) { |
|||
if got := r.Header.Get(header); got != want { |
|||
t.Errorf("Header.Get(%q) returned %q, want %q", header, got, want) |
|||
} |
|||
} |
|||
|
|||
func testURLParseError(t *testing.T, err error) { |
|||
if err == nil { |
|||
t.Errorf("Expected error to be returned") |
|||
} |
|||
if err, ok := err.(*url.Error); !ok || err.Op != "parse" { |
|||
t.Errorf("Expected URL parse error, got %+v", err) |
|||
} |
|||
} |
|||
|
|||
func testBody(t *testing.T, r *http.Request, want string) { |
|||
b, err := ioutil.ReadAll(r.Body) |
|||
if err != nil { |
|||
t.Errorf("Error reading request body: %v", err) |
|||
} |
|||
if got := string(b); got != want { |
|||
t.Errorf("request Body is %s, want %s", got, want) |
|||
} |
|||
} |
|||
|
|||
// Helper function to test that a value is marshalled to JSON as expected.
|
|||
func testJSONMarshal(t *testing.T, v interface{}, want string) { |
|||
j, err := json.Marshal(v) |
|||
if err != nil { |
|||
t.Errorf("Unable to marshal JSON for %v", v) |
|||
} |
|||
|
|||
w := new(bytes.Buffer) |
|||
err = json.Compact(w, []byte(want)) |
|||
if err != nil { |
|||
t.Errorf("String is not valid json: %s", want) |
|||
} |
|||
|
|||
if w.String() != string(j) { |
|||
t.Errorf("json.Marshal(%q) returned %s, want %s", v, j, w) |
|||
} |
|||
|
|||
// now go the other direction and make sure things unmarshal as expected
|
|||
u := reflect.ValueOf(v).Interface() |
|||
if err := json.Unmarshal([]byte(want), u); err != nil { |
|||
t.Errorf("Unable to unmarshal JSON for %v", want) |
|||
} |
|||
|
|||
if !reflect.DeepEqual(v, u) { |
|||
t.Errorf("json.Unmarshal(%q) returned %s, want %s", want, u, v) |
|||
} |
|||
} |
|||
|
|||
func TestNewClient(t *testing.T) { |
|||
c := NewClient(nil) |
|||
|
|||
if got, want := c.BaseURL.String(), defaultBaseURL; got != want { |
|||
t.Errorf("NewClient BaseURL is %v, want %v", got, want) |
|||
} |
|||
if got, want := c.UserAgent, userAgent; got != want { |
|||
t.Errorf("NewClient UserAgent is %v, want %v", got, want) |
|||
} |
|||
} |
|||
|
|||
// Ensure that length of Client.rateLimits is the same as number of fields in RateLimits struct.
|
|||
func TestClient_rateLimits(t *testing.T) { |
|||
if got, want := len(Client{}.rateLimits), reflect.TypeOf(RateLimits{}).NumField(); got != want { |
|||
t.Errorf("len(Client{}.rateLimits) is %v, want %v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestNewRequest(t *testing.T) { |
|||
c := NewClient(nil) |
|||
|
|||
inURL, outURL := "/foo", defaultBaseURL+"foo" |
|||
inBody, outBody := &User{Login: String("l")}, `{"login":"l"}`+"\n" |
|||
req, _ := c.NewRequest("GET", inURL, inBody) |
|||
|
|||
// test that relative URL was expanded
|
|||
if got, want := req.URL.String(), outURL; got != want { |
|||
t.Errorf("NewRequest(%q) URL is %v, want %v", inURL, got, want) |
|||
} |
|||
|
|||
// test that body was JSON encoded
|
|||
body, _ := ioutil.ReadAll(req.Body) |
|||
if got, want := string(body), outBody; got != want { |
|||
t.Errorf("NewRequest(%q) Body is %v, want %v", inBody, got, want) |
|||
} |
|||
|
|||
// test that default user-agent is attached to the request
|
|||
if got, want := req.Header.Get("User-Agent"), c.UserAgent; got != want { |
|||
t.Errorf("NewRequest() User-Agent is %v, want %v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestNewRequest_invalidJSON(t *testing.T) { |
|||
c := NewClient(nil) |
|||
|
|||
type T struct { |
|||
A map[interface{}]interface{} |
|||
} |
|||
_, err := c.NewRequest("GET", "/", &T{}) |
|||
|
|||
if err == nil { |
|||
t.Error("Expected error to be returned.") |
|||
} |
|||
if err, ok := err.(*json.UnsupportedTypeError); !ok { |
|||
t.Errorf("Expected a JSON error; got %#v.", err) |
|||
} |
|||
} |
|||
|
|||
func TestNewRequest_badURL(t *testing.T) { |
|||
c := NewClient(nil) |
|||
_, err := c.NewRequest("GET", ":", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
// ensure that no User-Agent header is set if the client's UserAgent is empty.
|
|||
// This caused a problem with Google's internal http client.
|
|||
func TestNewRequest_emptyUserAgent(t *testing.T) { |
|||
c := NewClient(nil) |
|||
c.UserAgent = "" |
|||
req, err := c.NewRequest("GET", "/", nil) |
|||
if err != nil { |
|||
t.Fatalf("NewRequest returned unexpected error: %v", err) |
|||
} |
|||
if _, ok := req.Header["User-Agent"]; ok { |
|||
t.Fatal("constructed request contains unexpected User-Agent header") |
|||
} |
|||
} |
|||
|
|||
// If a nil body is passed to github.NewRequest, make sure that nil is also
|
|||
// passed to http.NewRequest. In most cases, passing an io.Reader that returns
|
|||
// no content is fine, since there is no difference between an HTTP request
|
|||
// body that is an empty string versus one that is not set at all. However in
|
|||
// certain cases, intermediate systems may treat these differently resulting in
|
|||
// subtle errors.
|
|||
func TestNewRequest_emptyBody(t *testing.T) { |
|||
c := NewClient(nil) |
|||
req, err := c.NewRequest("GET", "/", nil) |
|||
if err != nil { |
|||
t.Fatalf("NewRequest returned unexpected error: %v", err) |
|||
} |
|||
if req.Body != nil { |
|||
t.Fatalf("constructed request contains a non-nil Body") |
|||
} |
|||
} |
|||
|
|||
func TestResponse_populatePageValues(t *testing.T) { |
|||
r := http.Response{ |
|||
Header: http.Header{ |
|||
"Link": {`<https://api.github.com/?page=1>; rel="first",` + |
|||
` <https://api.github.com/?page=2>; rel="prev",` + |
|||
` <https://api.github.com/?page=4>; rel="next",` + |
|||
` <https://api.github.com/?page=5>; rel="last"`, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
response := newResponse(&r) |
|||
if got, want := response.FirstPage, 1; got != want { |
|||
t.Errorf("response.FirstPage: %v, want %v", got, want) |
|||
} |
|||
if got, want := response.PrevPage, 2; want != got { |
|||
t.Errorf("response.PrevPage: %v, want %v", got, want) |
|||
} |
|||
if got, want := response.NextPage, 4; want != got { |
|||
t.Errorf("response.NextPage: %v, want %v", got, want) |
|||
} |
|||
if got, want := response.LastPage, 5; want != got { |
|||
t.Errorf("response.LastPage: %v, want %v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestResponse_populatePageValues_invalid(t *testing.T) { |
|||
r := http.Response{ |
|||
Header: http.Header{ |
|||
"Link": {`<https://api.github.com/?page=1>,` + |
|||
`<https://api.github.com/?page=abc>; rel="first",` + |
|||
`https://api.github.com/?page=2; rel="prev",` + |
|||
`<https://api.github.com/>; rel="next",` + |
|||
`<https://api.github.com/?page=>; rel="last"`, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
response := newResponse(&r) |
|||
if got, want := response.FirstPage, 0; got != want { |
|||
t.Errorf("response.FirstPage: %v, want %v", got, want) |
|||
} |
|||
if got, want := response.PrevPage, 0; got != want { |
|||
t.Errorf("response.PrevPage: %v, want %v", got, want) |
|||
} |
|||
if got, want := response.NextPage, 0; got != want { |
|||
t.Errorf("response.NextPage: %v, want %v", got, want) |
|||
} |
|||
if got, want := response.LastPage, 0; got != want { |
|||
t.Errorf("response.LastPage: %v, want %v", got, want) |
|||
} |
|||
|
|||
// more invalid URLs
|
|||
r = http.Response{ |
|||
Header: http.Header{ |
|||
"Link": {`<https://api.github.com/%?page=2>; rel="first"`}, |
|||
}, |
|||
} |
|||
|
|||
response = newResponse(&r) |
|||
if got, want := response.FirstPage, 0; got != want { |
|||
t.Errorf("response.FirstPage: %v, want %v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestDo(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
type foo struct { |
|||
A string |
|||
} |
|||
|
|||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { |
|||
if m := "GET"; m != r.Method { |
|||
t.Errorf("Request method = %v, want %v", r.Method, m) |
|||
} |
|||
fmt.Fprint(w, `{"A":"a"}`) |
|||
}) |
|||
|
|||
req, _ := client.NewRequest("GET", "/", nil) |
|||
body := new(foo) |
|||
client.Do(req, body) |
|||
|
|||
want := &foo{"a"} |
|||
if !reflect.DeepEqual(body, want) { |
|||
t.Errorf("Response body = %v, want %v", body, want) |
|||
} |
|||
} |
|||
|
|||
func TestDo_httpError(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { |
|||
http.Error(w, "Bad Request", 400) |
|||
}) |
|||
|
|||
req, _ := client.NewRequest("GET", "/", nil) |
|||
_, err := client.Do(req, nil) |
|||
|
|||
if err == nil { |
|||
t.Error("Expected HTTP 400 error.") |
|||
} |
|||
} |
|||
|
|||
// Test handling of an error caused by the internal http client's Do()
|
|||
// function. A redirect loop is pretty unlikely to occur within the GitHub
|
|||
// API, but does allow us to exercise the right code path.
|
|||
func TestDo_redirectLoop(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { |
|||
http.Redirect(w, r, "/", http.StatusFound) |
|||
}) |
|||
|
|||
req, _ := client.NewRequest("GET", "/", nil) |
|||
_, err := client.Do(req, nil) |
|||
|
|||
if err == nil { |
|||
t.Error("Expected error to be returned.") |
|||
} |
|||
if err, ok := err.(*url.Error); !ok { |
|||
t.Errorf("Expected a URL error; got %#v.", err) |
|||
} |
|||
} |
|||
|
|||
func TestDo_rateLimit(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { |
|||
w.Header().Add(headerRateLimit, "60") |
|||
w.Header().Add(headerRateRemaining, "59") |
|||
w.Header().Add(headerRateReset, "1372700873") |
|||
}) |
|||
|
|||
if got, want := client.Rate().Limit, 0; got != want { |
|||
t.Errorf("Client rate limit = %v, want %v", got, want) |
|||
} |
|||
if got, want := client.Rate().Remaining, 0; got != want { |
|||
t.Errorf("Client rate remaining = %v, got %v", got, want) |
|||
} |
|||
if !client.Rate().Reset.IsZero() { |
|||
t.Errorf("Client rate reset not initialized to zero value") |
|||
} |
|||
|
|||
req, _ := client.NewRequest("GET", "/", nil) |
|||
_, err := client.Do(req, nil) |
|||
if err != nil { |
|||
t.Errorf("Do returned unexpected error: %v", err) |
|||
} |
|||
if got, want := client.Rate().Limit, 60; got != want { |
|||
t.Errorf("Client rate limit = %v, want %v", got, want) |
|||
} |
|||
if got, want := client.Rate().Remaining, 59; got != want { |
|||
t.Errorf("Client rate remaining = %v, want %v", got, want) |
|||
} |
|||
reset := time.Date(2013, 7, 1, 17, 47, 53, 0, time.UTC) |
|||
if client.Rate().Reset.UTC() != reset { |
|||
t.Errorf("Client rate reset = %v, want %v", client.Rate().Reset, reset) |
|||
} |
|||
} |
|||
|
|||
// ensure rate limit is still parsed, even for error responses
|
|||
func TestDo_rateLimit_errorResponse(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { |
|||
w.Header().Add(headerRateLimit, "60") |
|||
w.Header().Add(headerRateRemaining, "59") |
|||
w.Header().Add(headerRateReset, "1372700873") |
|||
http.Error(w, "Bad Request", 400) |
|||
}) |
|||
|
|||
req, _ := client.NewRequest("GET", "/", nil) |
|||
_, err := client.Do(req, nil) |
|||
|
|||
if err == nil { |
|||
t.Error("Expected error to be returned.") |
|||
} |
|||
if _, ok := err.(*RateLimitError); ok { |
|||
t.Errorf("Did not expect a *RateLimitError error; got %#v.", err) |
|||
} |
|||
if got, want := client.Rate().Limit, 60; got != want { |
|||
t.Errorf("Client rate limit = %v, want %v", got, want) |
|||
} |
|||
if got, want := client.Rate().Remaining, 59; got != want { |
|||
t.Errorf("Client rate remaining = %v, want %v", got, want) |
|||
} |
|||
reset := time.Date(2013, 7, 1, 17, 47, 53, 0, time.UTC) |
|||
if client.Rate().Reset.UTC() != reset { |
|||
t.Errorf("Client rate reset = %v, want %v", client.Rate().Reset, reset) |
|||
} |
|||
} |
|||
|
|||
// Ensure *RateLimitError is returned when API rate limit is exceeded.
|
|||
func TestDo_rateLimit_rateLimitError(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { |
|||
w.Header().Add(headerRateLimit, "60") |
|||
w.Header().Add(headerRateRemaining, "0") |
|||
w.Header().Add(headerRateReset, "1372700873") |
|||
w.Header().Set("Content-Type", "application/json; charset=utf-8") |
|||
w.WriteHeader(http.StatusForbidden) |
|||
fmt.Fprintln(w, `{ |
|||
"message": "API rate limit exceeded for xxx.xxx.xxx.xxx. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)", |
|||
"documentation_url": "https://developer.github.com/v3/#rate-limiting" |
|||
}`) |
|||
}) |
|||
|
|||
req, _ := client.NewRequest("GET", "/", nil) |
|||
_, err := client.Do(req, nil) |
|||
|
|||
if err == nil { |
|||
t.Error("Expected error to be returned.") |
|||
} |
|||
rateLimitErr, ok := err.(*RateLimitError) |
|||
if !ok { |
|||
t.Fatalf("Expected a *RateLimitError error; got %#v.", err) |
|||
} |
|||
if got, want := rateLimitErr.Rate.Limit, 60; got != want { |
|||
t.Errorf("rateLimitErr rate limit = %v, want %v", got, want) |
|||
} |
|||
if got, want := rateLimitErr.Rate.Remaining, 0; got != want { |
|||
t.Errorf("rateLimitErr rate remaining = %v, want %v", got, want) |
|||
} |
|||
reset := time.Date(2013, 7, 1, 17, 47, 53, 0, time.UTC) |
|||
if rateLimitErr.Rate.Reset.UTC() != reset { |
|||
t.Errorf("rateLimitErr rate reset = %v, want %v", rateLimitErr.Rate.Reset.UTC(), reset) |
|||
} |
|||
} |
|||
|
|||
// Ensure a network call is not made when it's known that API rate limit is still exceeded.
|
|||
func TestDo_rateLimit_noNetworkCall(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
reset := time.Now().UTC().Round(time.Second).Add(time.Minute) // Rate reset is a minute from now, with 1 second precision.
|
|||
|
|||
mux.HandleFunc("/first", func(w http.ResponseWriter, r *http.Request) { |
|||
w.Header().Add(headerRateLimit, "60") |
|||
w.Header().Add(headerRateRemaining, "0") |
|||
w.Header().Add(headerRateReset, fmt.Sprint(reset.Unix())) |
|||
w.Header().Set("Content-Type", "application/json; charset=utf-8") |
|||
w.WriteHeader(http.StatusForbidden) |
|||
fmt.Fprintln(w, `{ |
|||
"message": "API rate limit exceeded for xxx.xxx.xxx.xxx. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)", |
|||
"documentation_url": "https://developer.github.com/v3/#rate-limiting" |
|||
}`) |
|||
}) |
|||
|
|||
madeNetworkCall := false |
|||
mux.HandleFunc("/second", func(w http.ResponseWriter, r *http.Request) { |
|||
madeNetworkCall = true |
|||
}) |
|||
|
|||
// First request is made, and it makes the client aware of rate reset time being in the future.
|
|||
req, _ := client.NewRequest("GET", "/first", nil) |
|||
client.Do(req, nil) |
|||
|
|||
// Second request should not cause a network call to be made, since client can predict a rate limit error.
|
|||
req, _ = client.NewRequest("GET", "/second", nil) |
|||
_, err := client.Do(req, nil) |
|||
|
|||
if madeNetworkCall { |
|||
t.Fatal("Network call was made, even though rate limit is known to still be exceeded.") |
|||
} |
|||
|
|||
if err == nil { |
|||
t.Error("Expected error to be returned.") |
|||
} |
|||
rateLimitErr, ok := err.(*RateLimitError) |
|||
if !ok { |
|||
t.Fatalf("Expected a *RateLimitError error; got %#v.", err) |
|||
} |
|||
if got, want := rateLimitErr.Rate.Limit, 60; got != want { |
|||
t.Errorf("rateLimitErr rate limit = %v, want %v", got, want) |
|||
} |
|||
if got, want := rateLimitErr.Rate.Remaining, 0; got != want { |
|||
t.Errorf("rateLimitErr rate remaining = %v, want %v", got, want) |
|||
} |
|||
if rateLimitErr.Rate.Reset.UTC() != reset { |
|||
t.Errorf("rateLimitErr rate reset = %v, want %v", rateLimitErr.Rate.Reset.UTC(), reset) |
|||
} |
|||
} |
|||
|
|||
func TestDo_noContent(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { |
|||
w.WriteHeader(http.StatusNoContent) |
|||
}) |
|||
|
|||
var body json.RawMessage |
|||
|
|||
req, _ := client.NewRequest("GET", "/", nil) |
|||
_, err := client.Do(req, &body) |
|||
if err != nil { |
|||
t.Fatalf("Do returned unexpected error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestSanitizeURL(t *testing.T) { |
|||
tests := []struct { |
|||
in, want string |
|||
}{ |
|||
{"/?a=b", "/?a=b"}, |
|||
{"/?a=b&client_secret=secret", "/?a=b&client_secret=REDACTED"}, |
|||
{"/?a=b&client_id=id&client_secret=secret", "/?a=b&client_id=id&client_secret=REDACTED"}, |
|||
} |
|||
|
|||
for _, tt := range tests { |
|||
inURL, _ := url.Parse(tt.in) |
|||
want, _ := url.Parse(tt.want) |
|||
|
|||
if got := sanitizeURL(inURL); !reflect.DeepEqual(got, want) { |
|||
t.Errorf("sanitizeURL(%v) returned %v, want %v", tt.in, got, want) |
|||
} |
|||
} |
|||
} |
|||
|
|||
func TestCheckResponse(t *testing.T) { |
|||
res := &http.Response{ |
|||
Request: &http.Request{}, |
|||
StatusCode: http.StatusBadRequest, |
|||
Body: ioutil.NopCloser(strings.NewReader(`{"message":"m", |
|||
"errors": [{"resource": "r", "field": "f", "code": "c"}], |
|||
"block": {"reason": "dmca", "created_at": "2016-03-17T15:39:46Z"}}`)), |
|||
} |
|||
err := CheckResponse(res).(*ErrorResponse) |
|||
|
|||
if err == nil { |
|||
t.Errorf("Expected error response.") |
|||
} |
|||
|
|||
want := &ErrorResponse{ |
|||
Response: res, |
|||
Message: "m", |
|||
Errors: []Error{{Resource: "r", Field: "f", Code: "c"}}, |
|||
Block: &struct { |
|||
Reason string `json:"reason,omitempty"` |
|||
CreatedAt *Timestamp `json:"created_at,omitempty"` |
|||
}{ |
|||
Reason: "dmca", |
|||
CreatedAt: &Timestamp{time.Date(2016, 3, 17, 15, 39, 46, 0, time.UTC)}, |
|||
}, |
|||
} |
|||
if !reflect.DeepEqual(err, want) { |
|||
t.Errorf("Error = %#v, want %#v", err, want) |
|||
} |
|||
} |
|||
|
|||
// ensure that we properly handle API errors that do not contain a response body
|
|||
func TestCheckResponse_noBody(t *testing.T) { |
|||
res := &http.Response{ |
|||
Request: &http.Request{}, |
|||
StatusCode: http.StatusBadRequest, |
|||
Body: ioutil.NopCloser(strings.NewReader("")), |
|||
} |
|||
err := CheckResponse(res).(*ErrorResponse) |
|||
|
|||
if err == nil { |
|||
t.Errorf("Expected error response.") |
|||
} |
|||
|
|||
want := &ErrorResponse{ |
|||
Response: res, |
|||
} |
|||
if !reflect.DeepEqual(err, want) { |
|||
t.Errorf("Error = %#v, want %#v", err, want) |
|||
} |
|||
} |
|||
|
|||
func TestParseBooleanResponse_true(t *testing.T) { |
|||
result, err := parseBoolResponse(nil) |
|||
if err != nil { |
|||
t.Errorf("parseBoolResponse returned error: %+v", err) |
|||
} |
|||
|
|||
if want := true; result != want { |
|||
t.Errorf("parseBoolResponse returned %+v, want: %+v", result, want) |
|||
} |
|||
} |
|||
|
|||
func TestParseBooleanResponse_false(t *testing.T) { |
|||
v := &ErrorResponse{Response: &http.Response{StatusCode: http.StatusNotFound}} |
|||
result, err := parseBoolResponse(v) |
|||
if err != nil { |
|||
t.Errorf("parseBoolResponse returned error: %+v", err) |
|||
} |
|||
|
|||
if want := false; result != want { |
|||
t.Errorf("parseBoolResponse returned %+v, want: %+v", result, want) |
|||
} |
|||
} |
|||
|
|||
func TestParseBooleanResponse_error(t *testing.T) { |
|||
v := &ErrorResponse{Response: &http.Response{StatusCode: http.StatusBadRequest}} |
|||
result, err := parseBoolResponse(v) |
|||
|
|||
if err == nil { |
|||
t.Errorf("Expected error to be returned.") |
|||
} |
|||
|
|||
if want := false; result != want { |
|||
t.Errorf("parseBoolResponse returned %+v, want: %+v", result, want) |
|||
} |
|||
} |
|||
|
|||
func TestErrorResponse_Error(t *testing.T) { |
|||
res := &http.Response{Request: &http.Request{}} |
|||
err := ErrorResponse{Message: "m", Response: res} |
|||
if err.Error() == "" { |
|||
t.Errorf("Expected non-empty ErrorResponse.Error()") |
|||
} |
|||
} |
|||
|
|||
func TestError_Error(t *testing.T) { |
|||
err := Error{} |
|||
if err.Error() == "" { |
|||
t.Errorf("Expected non-empty Error.Error()") |
|||
} |
|||
} |
|||
|
|||
func TestRateLimit(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/rate_limit", func(w http.ResponseWriter, r *http.Request) { |
|||
if m := "GET"; m != r.Method { |
|||
t.Errorf("Request method = %v, want %v", r.Method, m) |
|||
} |
|||
fmt.Fprint(w, `{"resources":{ |
|||
"core": {"limit":2,"remaining":1,"reset":1372700873}, |
|||
"search": {"limit":3,"remaining":2,"reset":1372700874} |
|||
}}`) |
|||
}) |
|||
|
|||
rate, _, err := client.RateLimit() |
|||
if err != nil { |
|||
t.Errorf("Rate limit returned error: %v", err) |
|||
} |
|||
|
|||
want := &Rate{ |
|||
Limit: 2, |
|||
Remaining: 1, |
|||
Reset: Timestamp{time.Date(2013, 7, 1, 17, 47, 53, 0, time.UTC).Local()}, |
|||
} |
|||
if !reflect.DeepEqual(rate, want) { |
|||
t.Errorf("RateLimit returned %+v, want %+v", rate, want) |
|||
} |
|||
} |
|||
|
|||
func TestRateLimits(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/rate_limit", func(w http.ResponseWriter, r *http.Request) { |
|||
if m := "GET"; m != r.Method { |
|||
t.Errorf("Request method = %v, want %v", r.Method, m) |
|||
} |
|||
fmt.Fprint(w, `{"resources":{ |
|||
"core": {"limit":2,"remaining":1,"reset":1372700873}, |
|||
"search": {"limit":3,"remaining":2,"reset":1372700874} |
|||
}}`) |
|||
}) |
|||
|
|||
rate, _, err := client.RateLimits() |
|||
if err != nil { |
|||
t.Errorf("RateLimits returned error: %v", err) |
|||
} |
|||
|
|||
want := &RateLimits{ |
|||
Core: &Rate{ |
|||
Limit: 2, |
|||
Remaining: 1, |
|||
Reset: Timestamp{time.Date(2013, 7, 1, 17, 47, 53, 0, time.UTC).Local()}, |
|||
}, |
|||
Search: &Rate{ |
|||
Limit: 3, |
|||
Remaining: 2, |
|||
Reset: Timestamp{time.Date(2013, 7, 1, 17, 47, 54, 0, time.UTC).Local()}, |
|||
}, |
|||
} |
|||
if !reflect.DeepEqual(rate, want) { |
|||
t.Errorf("RateLimits returned %+v, want %+v", rate, want) |
|||
} |
|||
|
|||
if got, want := client.rateLimits[coreCategory], *want.Core; got != want { |
|||
t.Errorf("client.rateLimits[coreCategory] is %+v, want %+v", got, want) |
|||
} |
|||
if got, want := client.rateLimits[searchCategory], *want.Search; got != want { |
|||
t.Errorf("client.rateLimits[searchCategory] is %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestUnauthenticatedRateLimitedTransport(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { |
|||
var v, want string |
|||
q := r.URL.Query() |
|||
if v, want = q.Get("client_id"), "id"; v != want { |
|||
t.Errorf("OAuth Client ID = %v, want %v", v, want) |
|||
} |
|||
if v, want = q.Get("client_secret"), "secret"; v != want { |
|||
t.Errorf("OAuth Client Secret = %v, want %v", v, want) |
|||
} |
|||
}) |
|||
|
|||
tp := &UnauthenticatedRateLimitedTransport{ |
|||
ClientID: "id", |
|||
ClientSecret: "secret", |
|||
} |
|||
unauthedClient := NewClient(tp.Client()) |
|||
unauthedClient.BaseURL = client.BaseURL |
|||
req, _ := unauthedClient.NewRequest("GET", "/", nil) |
|||
unauthedClient.Do(req, nil) |
|||
} |
|||
|
|||
func TestUnauthenticatedRateLimitedTransport_missingFields(t *testing.T) { |
|||
// missing ClientID
|
|||
tp := &UnauthenticatedRateLimitedTransport{ |
|||
ClientSecret: "secret", |
|||
} |
|||
_, err := tp.RoundTrip(nil) |
|||
if err == nil { |
|||
t.Errorf("Expected error to be returned") |
|||
} |
|||
|
|||
// missing ClientSecret
|
|||
tp = &UnauthenticatedRateLimitedTransport{ |
|||
ClientID: "id", |
|||
} |
|||
_, err = tp.RoundTrip(nil) |
|||
if err == nil { |
|||
t.Errorf("Expected error to be returned") |
|||
} |
|||
} |
|||
|
|||
func TestUnauthenticatedRateLimitedTransport_transport(t *testing.T) { |
|||
// default transport
|
|||
tp := &UnauthenticatedRateLimitedTransport{ |
|||
ClientID: "id", |
|||
ClientSecret: "secret", |
|||
} |
|||
if tp.transport() != http.DefaultTransport { |
|||
t.Errorf("Expected http.DefaultTransport to be used.") |
|||
} |
|||
|
|||
// custom transport
|
|||
tp = &UnauthenticatedRateLimitedTransport{ |
|||
ClientID: "id", |
|||
ClientSecret: "secret", |
|||
Transport: &http.Transport{}, |
|||
} |
|||
if tp.transport() == http.DefaultTransport { |
|||
t.Errorf("Expected custom transport to be used.") |
|||
} |
|||
} |
|||
|
|||
func TestBasicAuthTransport(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
username, password, otp := "u", "p", "123456" |
|||
|
|||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { |
|||
u, p, ok := r.BasicAuth() |
|||
if !ok { |
|||
t.Errorf("request does not contain basic auth credentials") |
|||
} |
|||
if u != username { |
|||
t.Errorf("request contained basic auth username %q, want %q", u, username) |
|||
} |
|||
if p != password { |
|||
t.Errorf("request contained basic auth password %q, want %q", p, password) |
|||
} |
|||
if got, want := r.Header.Get(headerOTP), otp; got != want { |
|||
t.Errorf("request contained OTP %q, want %q", got, want) |
|||
} |
|||
}) |
|||
|
|||
tp := &BasicAuthTransport{ |
|||
Username: username, |
|||
Password: password, |
|||
OTP: otp, |
|||
} |
|||
basicAuthClient := NewClient(tp.Client()) |
|||
basicAuthClient.BaseURL = client.BaseURL |
|||
req, _ := basicAuthClient.NewRequest("GET", "/", nil) |
|||
basicAuthClient.Do(req, nil) |
|||
} |
|||
|
|||
func TestBasicAuthTransport_transport(t *testing.T) { |
|||
// default transport
|
|||
tp := &BasicAuthTransport{} |
|||
if tp.transport() != http.DefaultTransport { |
|||
t.Errorf("Expected http.DefaultTransport to be used.") |
|||
} |
|||
|
|||
// custom transport
|
|||
tp = &BasicAuthTransport{ |
|||
Transport: &http.Transport{}, |
|||
} |
|||
if tp.transport() == http.DefaultTransport { |
|||
t.Errorf("Expected custom transport to be used.") |
|||
} |
|||
} |
@ -0,0 +1,61 @@ |
|||
// Copyright 2013 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 "fmt" |
|||
|
|||
// GitignoresService provides access to the gitignore related functions in the
|
|||
// GitHub API.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/gitignore/
|
|||
type GitignoresService service |
|||
|
|||
// Gitignore represents a .gitignore file as returned by the GitHub API.
|
|||
type Gitignore struct { |
|||
Name *string `json:"name,omitempty"` |
|||
Source *string `json:"source,omitempty"` |
|||
} |
|||
|
|||
func (g Gitignore) String() string { |
|||
return Stringify(g) |
|||
} |
|||
|
|||
// List all available Gitignore templates.
|
|||
//
|
|||
// http://developer.github.com/v3/gitignore/#listing-available-templates
|
|||
func (s GitignoresService) List() ([]string, *Response, error) { |
|||
req, err := s.client.NewRequest("GET", "gitignore/templates", nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
availableTemplates := new([]string) |
|||
resp, err := s.client.Do(req, availableTemplates) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *availableTemplates, resp, err |
|||
} |
|||
|
|||
// Get a Gitignore by name.
|
|||
//
|
|||
// http://developer.github.com/v3/gitignore/#get-a-single-template
|
|||
func (s GitignoresService) Get(name string) (*Gitignore, *Response, error) { |
|||
u := fmt.Sprintf("gitignore/templates/%v", name) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
gitignore := new(Gitignore) |
|||
resp, err := s.client.Do(req, gitignore) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return gitignore, resp, err |
|||
} |
@ -0,0 +1,58 @@ |
|||
// Copyright 2013 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 ( |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestGitignoresService_List(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/gitignore/templates", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `["C", "Go"]`) |
|||
}) |
|||
|
|||
available, _, err := client.Gitignores.List() |
|||
if err != nil { |
|||
t.Errorf("Gitignores.List returned error: %v", err) |
|||
} |
|||
|
|||
want := []string{"C", "Go"} |
|||
if !reflect.DeepEqual(available, want) { |
|||
t.Errorf("Gitignores.List returned %+v, want %+v", available, want) |
|||
} |
|||
} |
|||
|
|||
func TestGitignoresService_Get(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/gitignore/templates/name", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{"name":"Name","source":"template source"}`) |
|||
}) |
|||
|
|||
gitignore, _, err := client.Gitignores.Get("name") |
|||
if err != nil { |
|||
t.Errorf("Gitignores.List returned error: %v", err) |
|||
} |
|||
|
|||
want := &Gitignore{Name: String("Name"), Source: String("template source")} |
|||
if !reflect.DeepEqual(gitignore, want) { |
|||
t.Errorf("Gitignores.Get returned %+v, want %+v", gitignore, want) |
|||
} |
|||
} |
|||
|
|||
func TestGitignoresService_Get_invalidTemplate(t *testing.T) { |
|||
_, _, err := client.Gitignores.Get("%") |
|||
testURLParseError(t, err) |
|||
} |
@ -0,0 +1,299 @@ |
|||
// Copyright 2013 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 ( |
|||
"fmt" |
|||
"time" |
|||
) |
|||
|
|||
// IssuesService handles communication with the issue related
|
|||
// methods of the GitHub API.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/issues/
|
|||
type IssuesService service |
|||
|
|||
// Issue represents a GitHub issue on a repository.
|
|||
type Issue struct { |
|||
ID *int `json:"id,omitempty"` |
|||
Number *int `json:"number,omitempty"` |
|||
State *string `json:"state,omitempty"` |
|||
Title *string `json:"title,omitempty"` |
|||
Body *string `json:"body,omitempty"` |
|||
User *User `json:"user,omitempty"` |
|||
Labels []Label `json:"labels,omitempty"` |
|||
Assignee *User `json:"assignee,omitempty"` |
|||
Comments *int `json:"comments,omitempty"` |
|||
ClosedAt *time.Time `json:"closed_at,omitempty"` |
|||
CreatedAt *time.Time `json:"created_at,omitempty"` |
|||
UpdatedAt *time.Time `json:"updated_at,omitempty"` |
|||
URL *string `json:"url,omitempty"` |
|||
HTMLURL *string `json:"html_url,omitempty"` |
|||
Milestone *Milestone `json:"milestone,omitempty"` |
|||
PullRequestLinks *PullRequestLinks `json:"pull_request,omitempty"` |
|||
Repository *Repository `json:"repository,omitempty"` |
|||
Reactions *Reactions `json:"reactions,omitempty"` |
|||
Assignees []*User `json:"assignees,omitempty"` |
|||
|
|||
// 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"` |
|||
} |
|||
|
|||
func (i Issue) String() string { |
|||
return Stringify(i) |
|||
} |
|||
|
|||
// IssueRequest represents a request to create/edit an issue.
|
|||
// It is separate from Issue above because otherwise Labels
|
|||
// and Assignee fail to serialize to the correct JSON.
|
|||
type IssueRequest struct { |
|||
Title *string `json:"title,omitempty"` |
|||
Body *string `json:"body,omitempty"` |
|||
Labels *[]string `json:"labels,omitempty"` |
|||
Assignee *string `json:"assignee,omitempty"` |
|||
State *string `json:"state,omitempty"` |
|||
Milestone *int `json:"milestone,omitempty"` |
|||
Assignees *[]string `json:"assignees,omitempty"` |
|||
} |
|||
|
|||
// IssueListOptions specifies the optional parameters to the IssuesService.List
|
|||
// and IssuesService.ListByOrg methods.
|
|||
type IssueListOptions struct { |
|||
// Filter specifies which issues to list. Possible values are: assigned,
|
|||
// created, mentioned, subscribed, all. Default is "assigned".
|
|||
Filter string `url:"filter,omitempty"` |
|||
|
|||
// State filters issues based on their state. Possible values are: open,
|
|||
// closed, all. Default is "open".
|
|||
State string `url:"state,omitempty"` |
|||
|
|||
// Labels filters issues based on their label.
|
|||
Labels []string `url:"labels,comma,omitempty"` |
|||
|
|||
// Sort specifies how to sort issues. Possible values are: created, updated,
|
|||
// and comments. Default value is "created".
|
|||
Sort string `url:"sort,omitempty"` |
|||
|
|||
// Direction in which to sort issues. Possible values are: asc, desc.
|
|||
// Default is "desc".
|
|||
Direction string `url:"direction,omitempty"` |
|||
|
|||
// Since filters issues by time.
|
|||
Since time.Time `url:"since,omitempty"` |
|||
|
|||
ListOptions |
|||
} |
|||
|
|||
// PullRequestLinks object is added to the Issue object when it's an issue included
|
|||
// in the IssueCommentEvent webhook payload, if the webhooks is fired by a comment on a PR
|
|||
type PullRequestLinks struct { |
|||
URL *string `json:"url,omitempty"` |
|||
HTMLURL *string `json:"html_url,omitempty"` |
|||
DiffURL *string `json:"diff_url,omitempty"` |
|||
PatchURL *string `json:"patch_url,omitempty"` |
|||
} |
|||
|
|||
// List the issues for the authenticated user. If all is true, list issues
|
|||
// across all the user's visible repositories including owned, member, and
|
|||
// organization repositories; if false, list only owned and member
|
|||
// repositories.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/issues/#list-issues
|
|||
func (s *IssuesService) List(all bool, opt *IssueListOptions) ([]*Issue, *Response, error) { |
|||
var u string |
|||
if all { |
|||
u = "issues" |
|||
} else { |
|||
u = "user/issues" |
|||
} |
|||
return s.listIssues(u, opt) |
|||
} |
|||
|
|||
// ListByOrg fetches the issues in the specified organization for the
|
|||
// authenticated user.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/issues/#list-issues
|
|||
func (s *IssuesService) ListByOrg(org string, opt *IssueListOptions) ([]*Issue, *Response, error) { |
|||
u := fmt.Sprintf("orgs/%v/issues", org) |
|||
return s.listIssues(u, opt) |
|||
} |
|||
|
|||
func (s *IssuesService) listIssues(u string, opt *IssueListOptions) ([]*Issue, *Response, error) { |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeReactionsPreview) |
|||
|
|||
issues := new([]*Issue) |
|||
resp, err := s.client.Do(req, issues) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *issues, resp, err |
|||
} |
|||
|
|||
// IssueListByRepoOptions specifies the optional parameters to the
|
|||
// IssuesService.ListByRepo method.
|
|||
type IssueListByRepoOptions struct { |
|||
// Milestone limits issues for the specified milestone. Possible values are
|
|||
// a milestone number, "none" for issues with no milestone, "*" for issues
|
|||
// with any milestone.
|
|||
Milestone string `url:"milestone,omitempty"` |
|||
|
|||
// State filters issues based on their state. Possible values are: open,
|
|||
// closed, all. Default is "open".
|
|||
State string `url:"state,omitempty"` |
|||
|
|||
// Assignee filters issues based on their assignee. Possible values are a
|
|||
// user name, "none" for issues that are not assigned, "*" for issues with
|
|||
// any assigned user.
|
|||
Assignee string `url:"assignee,omitempty"` |
|||
|
|||
// Creator filters issues based on their creator.
|
|||
Creator string `url:"creator,omitempty"` |
|||
|
|||
// Mentioned filters issues to those mentioned a specific user.
|
|||
Mentioned string `url:"mentioned,omitempty"` |
|||
|
|||
// Labels filters issues based on their label.
|
|||
Labels []string `url:"labels,omitempty,comma"` |
|||
|
|||
// Sort specifies how to sort issues. Possible values are: created, updated,
|
|||
// and comments. Default value is "created".
|
|||
Sort string `url:"sort,omitempty"` |
|||
|
|||
// Direction in which to sort issues. Possible values are: asc, desc.
|
|||
// Default is "desc".
|
|||
Direction string `url:"direction,omitempty"` |
|||
|
|||
// Since filters issues by time.
|
|||
Since time.Time `url:"since,omitempty"` |
|||
|
|||
ListOptions |
|||
} |
|||
|
|||
// ListByRepo lists the issues for the specified repository.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/issues/#list-issues-for-a-repository
|
|||
func (s *IssuesService) ListByRepo(owner string, repo string, opt *IssueListByRepoOptions) ([]*Issue, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/issues", owner, repo) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeReactionsPreview) |
|||
|
|||
issues := new([]*Issue) |
|||
resp, err := s.client.Do(req, issues) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *issues, resp, err |
|||
} |
|||
|
|||
// Get a single issue.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/issues/#get-a-single-issue
|
|||
func (s *IssuesService) Get(owner string, repo string, number int) (*Issue, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/issues/%d", owner, repo, number) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeReactionsPreview) |
|||
|
|||
issue := new(Issue) |
|||
resp, err := s.client.Do(req, issue) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return issue, resp, err |
|||
} |
|||
|
|||
// Create a new issue on the specified repository.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/issues/#create-an-issue
|
|||
func (s *IssuesService) Create(owner string, repo string, issue *IssueRequest) (*Issue, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/issues", owner, repo) |
|||
req, err := s.client.NewRequest("POST", u, issue) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
i := new(Issue) |
|||
resp, err := s.client.Do(req, i) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return i, resp, err |
|||
} |
|||
|
|||
// Edit an issue.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/issues/#edit-an-issue
|
|||
func (s *IssuesService) Edit(owner string, repo string, number int, issue *IssueRequest) (*Issue, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/issues/%d", owner, repo, number) |
|||
req, err := s.client.NewRequest("PATCH", u, issue) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
i := new(Issue) |
|||
resp, err := s.client.Do(req, i) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return i, resp, err |
|||
} |
|||
|
|||
// Lock an issue's conversation.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/issues/#lock-an-issue
|
|||
func (s *IssuesService) Lock(owner string, repo string, number int) (*Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/issues/%d/lock", owner, repo, number) |
|||
req, err := s.client.NewRequest("PUT", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
return s.client.Do(req, nil) |
|||
} |
|||
|
|||
// Unlock an issue's conversation.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/issues/#unlock-an-issue
|
|||
func (s *IssuesService) Unlock(owner string, repo string, number int) (*Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/issues/%d/lock", owner, repo, number) |
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
return s.client.Do(req, nil) |
|||
} |
@ -0,0 +1,82 @@ |
|||
// Copyright 2013 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 "fmt" |
|||
|
|||
// ListAssignees fetches all available assignees (owners and collaborators) to
|
|||
// which issues may be assigned.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/issues/assignees/#list-assignees
|
|||
func (s *IssuesService) ListAssignees(owner, repo string, opt *ListOptions) ([]*User, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/assignees", owner, repo) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
assignees := new([]*User) |
|||
resp, err := s.client.Do(req, assignees) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *assignees, resp, err |
|||
} |
|||
|
|||
// IsAssignee checks if a user is an assignee for the specified repository.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/issues/assignees/#check-assignee
|
|||
func (s *IssuesService) IsAssignee(owner, repo, user string) (bool, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/assignees/%v", owner, repo, user) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return false, nil, err |
|||
} |
|||
resp, err := s.client.Do(req, nil) |
|||
assignee, err := parseBoolResponse(err) |
|||
return assignee, resp, err |
|||
} |
|||
|
|||
// AddAssignees adds the provided GitHub users as assignees to the issue.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/issues/assignees/#add-assignees-to-an-issue
|
|||
func (s *IssuesService) AddAssignees(owner, repo string, number int, assignees []string) (*Issue, *Response, error) { |
|||
users := &struct { |
|||
Assignees []string `json:"assignees,omitempty"` |
|||
}{Assignees: assignees} |
|||
u := fmt.Sprintf("repos/%v/%v/issues/%v/assignees", owner, repo, number) |
|||
req, err := s.client.NewRequest("POST", u, users) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
issue := &Issue{} |
|||
resp, err := s.client.Do(req, issue) |
|||
return issue, resp, err |
|||
} |
|||
|
|||
// RemoveAssignees removes the provided GitHub users as assignees from the issue.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/issues/assignees/#remove-assignees-from-an-issue
|
|||
func (s *IssuesService) RemoveAssignees(owner, repo string, number int, assignees []string) (*Issue, *Response, error) { |
|||
users := &struct { |
|||
Assignees []string `json:"assignees,omitempty"` |
|||
}{Assignees: assignees} |
|||
u := fmt.Sprintf("repos/%v/%v/issues/%v/assignees", owner, repo, number) |
|||
req, err := s.client.NewRequest("DELETE", u, users) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
issue := &Issue{} |
|||
resp, err := s.client.Do(req, issue) |
|||
return issue, resp, err |
|||
} |
@ -0,0 +1,157 @@ |
|||
// Copyright 2013 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestIssuesService_ListAssignees(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/assignees", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{"page": "2"}) |
|||
fmt.Fprint(w, `[{"id":1}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 2} |
|||
assignees, _, err := client.Issues.ListAssignees("o", "r", opt) |
|||
if err != nil { |
|||
t.Errorf("Issues.ListAssignees returned error: %v", err) |
|||
} |
|||
|
|||
want := []*User{{ID: Int(1)}} |
|||
if !reflect.DeepEqual(assignees, want) { |
|||
t.Errorf("Issues.ListAssignees returned %+v, want %+v", assignees, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_ListAssignees_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Issues.ListAssignees("%", "r", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestIssuesService_IsAssignee_true(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/assignees/u", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
}) |
|||
|
|||
assignee, _, err := client.Issues.IsAssignee("o", "r", "u") |
|||
if err != nil { |
|||
t.Errorf("Issues.IsAssignee returned error: %v", err) |
|||
} |
|||
if want := true; assignee != want { |
|||
t.Errorf("Issues.IsAssignee returned %+v, want %+v", assignee, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_IsAssignee_false(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/assignees/u", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
w.WriteHeader(http.StatusNotFound) |
|||
}) |
|||
|
|||
assignee, _, err := client.Issues.IsAssignee("o", "r", "u") |
|||
if err != nil { |
|||
t.Errorf("Issues.IsAssignee returned error: %v", err) |
|||
} |
|||
if want := false; assignee != want { |
|||
t.Errorf("Issues.IsAssignee returned %+v, want %+v", assignee, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_IsAssignee_error(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/assignees/u", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
http.Error(w, "BadRequest", http.StatusBadRequest) |
|||
}) |
|||
|
|||
assignee, _, err := client.Issues.IsAssignee("o", "r", "u") |
|||
if err == nil { |
|||
t.Errorf("Expected HTTP 400 response") |
|||
} |
|||
if want := false; assignee != want { |
|||
t.Errorf("Issues.IsAssignee returned %+v, want %+v", assignee, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_IsAssignee_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Issues.IsAssignee("%", "r", "u") |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestIssuesService_AddAssignees(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/issues/1/assignees", func(w http.ResponseWriter, r *http.Request) { |
|||
var assignees struct { |
|||
Assignees []string `json:"assignees,omitempty"` |
|||
} |
|||
json.NewDecoder(r.Body).Decode(&assignees) |
|||
|
|||
testMethod(t, r, "POST") |
|||
want := []string{"user1", "user2"} |
|||
if !reflect.DeepEqual(assignees.Assignees, want) { |
|||
t.Errorf("assignees = %+v, want %+v", assignees, want) |
|||
} |
|||
fmt.Fprint(w, `{"number":1,"assignees":[{"login":"user1"},{"login":"user2"}]}`) |
|||
}) |
|||
|
|||
got, _, err := client.Issues.AddAssignees("o", "r", 1, []string{"user1", "user2"}) |
|||
if err != nil { |
|||
t.Errorf("Issues.AddAssignees returned error: %v", err) |
|||
} |
|||
|
|||
want := &Issue{Number: Int(1), Assignees: []*User{{Login: String("user1")}, {Login: String("user2")}}} |
|||
if !reflect.DeepEqual(got, want) { |
|||
t.Errorf("Issues.AddAssignees = %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_RemoveAssignees(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/issues/1/assignees", func(w http.ResponseWriter, r *http.Request) { |
|||
var assignees struct { |
|||
Assignees []string `json:"assignees,omitempty"` |
|||
} |
|||
json.NewDecoder(r.Body).Decode(&assignees) |
|||
|
|||
testMethod(t, r, "DELETE") |
|||
want := []string{"user1", "user2"} |
|||
if !reflect.DeepEqual(assignees.Assignees, want) { |
|||
t.Errorf("assignees = %+v, want %+v", assignees, want) |
|||
} |
|||
fmt.Fprint(w, `{"number":1,"assignees":[]}`) |
|||
}) |
|||
|
|||
got, _, err := client.Issues.RemoveAssignees("o", "r", 1, []string{"user1", "user2"}) |
|||
if err != nil { |
|||
t.Errorf("Issues.RemoveAssignees returned error: %v", err) |
|||
} |
|||
|
|||
want := &Issue{Number: Int(1), Assignees: []*User{}} |
|||
if !reflect.DeepEqual(got, want) { |
|||
t.Errorf("Issues.RemoveAssignees = %+v, want %+v", got, want) |
|||
} |
|||
} |
@ -0,0 +1,147 @@ |
|||
// Copyright 2013 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 ( |
|||
"fmt" |
|||
"time" |
|||
) |
|||
|
|||
// IssueComment represents a comment left on an issue.
|
|||
type IssueComment struct { |
|||
ID *int `json:"id,omitempty"` |
|||
Body *string `json:"body,omitempty"` |
|||
User *User `json:"user,omitempty"` |
|||
Reactions *Reactions `json:"reactions,omitempty"` |
|||
CreatedAt *time.Time `json:"created_at,omitempty"` |
|||
UpdatedAt *time.Time `json:"updated_at,omitempty"` |
|||
URL *string `json:"url,omitempty"` |
|||
HTMLURL *string `json:"html_url,omitempty"` |
|||
IssueURL *string `json:"issue_url,omitempty"` |
|||
} |
|||
|
|||
func (i IssueComment) String() string { |
|||
return Stringify(i) |
|||
} |
|||
|
|||
// IssueListCommentsOptions specifies the optional parameters to the
|
|||
// IssuesService.ListComments method.
|
|||
type IssueListCommentsOptions struct { |
|||
// Sort specifies how to sort comments. Possible values are: created, updated.
|
|||
Sort string `url:"sort,omitempty"` |
|||
|
|||
// Direction in which to sort comments. Possible values are: asc, desc.
|
|||
Direction string `url:"direction,omitempty"` |
|||
|
|||
// Since filters comments by time.
|
|||
Since time.Time `url:"since,omitempty"` |
|||
|
|||
ListOptions |
|||
} |
|||
|
|||
// ListComments lists all comments on the specified issue. Specifying an issue
|
|||
// number of 0 will return all comments on all issues for the repository.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/issues/comments/#list-comments-on-an-issue
|
|||
func (s *IssuesService) ListComments(owner string, repo string, number int, opt *IssueListCommentsOptions) ([]*IssueComment, *Response, error) { |
|||
var u string |
|||
if number == 0 { |
|||
u = fmt.Sprintf("repos/%v/%v/issues/comments", owner, repo) |
|||
} else { |
|||
u = fmt.Sprintf("repos/%v/%v/issues/%d/comments", owner, repo, number) |
|||
} |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeReactionsPreview) |
|||
|
|||
comments := new([]*IssueComment) |
|||
resp, err := s.client.Do(req, comments) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *comments, resp, err |
|||
} |
|||
|
|||
// GetComment fetches the specified issue comment.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/issues/comments/#get-a-single-comment
|
|||
func (s *IssuesService) GetComment(owner string, repo string, id int) (*IssueComment, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/issues/comments/%d", owner, repo, id) |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeReactionsPreview) |
|||
|
|||
comment := new(IssueComment) |
|||
resp, err := s.client.Do(req, comment) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return comment, resp, err |
|||
} |
|||
|
|||
// CreateComment creates a new comment on the specified issue.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/issues/comments/#create-a-comment
|
|||
func (s *IssuesService) CreateComment(owner string, repo string, number int, comment *IssueComment) (*IssueComment, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/issues/%d/comments", owner, repo, number) |
|||
req, err := s.client.NewRequest("POST", u, comment) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
c := new(IssueComment) |
|||
resp, err := s.client.Do(req, c) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return c, resp, err |
|||
} |
|||
|
|||
// EditComment updates an issue comment.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/issues/comments/#edit-a-comment
|
|||
func (s *IssuesService) EditComment(owner string, repo string, id int, comment *IssueComment) (*IssueComment, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/issues/comments/%d", owner, repo, id) |
|||
req, err := s.client.NewRequest("PATCH", u, comment) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
c := new(IssueComment) |
|||
resp, err := s.client.Do(req, c) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return c, resp, err |
|||
} |
|||
|
|||
// DeleteComment deletes an issue comment.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/issues/comments/#delete-a-comment
|
|||
func (s *IssuesService) DeleteComment(owner string, repo string, id int) (*Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/issues/comments/%d", owner, repo, id) |
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return s.client.Do(req, nil) |
|||
} |
@ -0,0 +1,187 @@ |
|||
// Copyright 2013 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
"time" |
|||
) |
|||
|
|||
func TestIssuesService_ListComments_allIssues(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/issues/comments", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeReactionsPreview) |
|||
testFormValues(t, r, values{ |
|||
"sort": "updated", |
|||
"direction": "desc", |
|||
"since": "2002-02-10T15:30:00Z", |
|||
"page": "2", |
|||
}) |
|||
fmt.Fprint(w, `[{"id":1}]`) |
|||
}) |
|||
|
|||
opt := &IssueListCommentsOptions{ |
|||
Sort: "updated", |
|||
Direction: "desc", |
|||
Since: time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC), |
|||
ListOptions: ListOptions{Page: 2}, |
|||
} |
|||
comments, _, err := client.Issues.ListComments("o", "r", 0, opt) |
|||
if err != nil { |
|||
t.Errorf("Issues.ListComments returned error: %v", err) |
|||
} |
|||
|
|||
want := []*IssueComment{{ID: Int(1)}} |
|||
if !reflect.DeepEqual(comments, want) { |
|||
t.Errorf("Issues.ListComments returned %+v, want %+v", comments, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_ListComments_specificIssue(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/issues/1/comments", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeReactionsPreview) |
|||
fmt.Fprint(w, `[{"id":1}]`) |
|||
}) |
|||
|
|||
comments, _, err := client.Issues.ListComments("o", "r", 1, nil) |
|||
if err != nil { |
|||
t.Errorf("Issues.ListComments returned error: %v", err) |
|||
} |
|||
|
|||
want := []*IssueComment{{ID: Int(1)}} |
|||
if !reflect.DeepEqual(comments, want) { |
|||
t.Errorf("Issues.ListComments returned %+v, want %+v", comments, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_ListComments_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Issues.ListComments("%", "r", 1, nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestIssuesService_GetComment(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/issues/comments/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeReactionsPreview) |
|||
fmt.Fprint(w, `{"id":1}`) |
|||
}) |
|||
|
|||
comment, _, err := client.Issues.GetComment("o", "r", 1) |
|||
if err != nil { |
|||
t.Errorf("Issues.GetComment returned error: %v", err) |
|||
} |
|||
|
|||
want := &IssueComment{ID: Int(1)} |
|||
if !reflect.DeepEqual(comment, want) { |
|||
t.Errorf("Issues.GetComment returned %+v, want %+v", comment, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_GetComment_invalidOrg(t *testing.T) { |
|||
_, _, err := client.Issues.GetComment("%", "r", 1) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestIssuesService_CreateComment(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &IssueComment{Body: String("b")} |
|||
|
|||
mux.HandleFunc("/repos/o/r/issues/1/comments", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(IssueComment) |
|||
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.Issues.CreateComment("o", "r", 1, input) |
|||
if err != nil { |
|||
t.Errorf("Issues.CreateComment returned error: %v", err) |
|||
} |
|||
|
|||
want := &IssueComment{ID: Int(1)} |
|||
if !reflect.DeepEqual(comment, want) { |
|||
t.Errorf("Issues.CreateComment returned %+v, want %+v", comment, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_CreateComment_invalidOrg(t *testing.T) { |
|||
_, _, err := client.Issues.CreateComment("%", "r", 1, nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestIssuesService_EditComment(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &IssueComment{Body: String("b")} |
|||
|
|||
mux.HandleFunc("/repos/o/r/issues/comments/1", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(IssueComment) |
|||
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.Issues.EditComment("o", "r", 1, input) |
|||
if err != nil { |
|||
t.Errorf("Issues.EditComment returned error: %v", err) |
|||
} |
|||
|
|||
want := &IssueComment{ID: Int(1)} |
|||
if !reflect.DeepEqual(comment, want) { |
|||
t.Errorf("Issues.EditComment returned %+v, want %+v", comment, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_EditComment_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Issues.EditComment("%", "r", 1, nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestIssuesService_DeleteComment(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/issues/comments/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
}) |
|||
|
|||
_, err := client.Issues.DeleteComment("o", "r", 1) |
|||
if err != nil { |
|||
t.Errorf("Issues.DeleteComments returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_DeleteComment_invalidOwner(t *testing.T) { |
|||
_, err := client.Issues.DeleteComment("%", "r", 1) |
|||
testURLParseError(t, err) |
|||
} |
@ -0,0 +1,149 @@ |
|||
// Copyright 2014 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 ( |
|||
"fmt" |
|||
"time" |
|||
) |
|||
|
|||
// IssueEvent represents an event that occurred around an Issue or Pull Request.
|
|||
type IssueEvent struct { |
|||
ID *int `json:"id,omitempty"` |
|||
URL *string `json:"url,omitempty"` |
|||
|
|||
// The User that generated this event.
|
|||
Actor *User `json:"actor,omitempty"` |
|||
|
|||
// Event identifies the actual type of Event that occurred. Possible
|
|||
// values are:
|
|||
//
|
|||
// closed
|
|||
// The Actor closed the issue.
|
|||
// If the issue was closed by commit message, CommitID holds the SHA1 hash of the commit.
|
|||
//
|
|||
// merged
|
|||
// The Actor merged into master a branch containing a commit mentioning the issue.
|
|||
// CommitID holds the SHA1 of the merge commit.
|
|||
//
|
|||
// referenced
|
|||
// The Actor committed to master a commit mentioning the issue in its commit message.
|
|||
// CommitID holds the SHA1 of the commit.
|
|||
//
|
|||
// reopened, locked, unlocked
|
|||
// The Actor did that to the issue.
|
|||
//
|
|||
// renamed
|
|||
// The Actor changed the issue title from Rename.From to Rename.To.
|
|||
//
|
|||
// mentioned
|
|||
// Someone unspecified @mentioned the Actor [sic] in an issue comment body.
|
|||
//
|
|||
// assigned, unassigned
|
|||
// The Actor assigned the issue to or removed the assignment from the Assignee.
|
|||
//
|
|||
// labeled, unlabeled
|
|||
// The Actor added or removed the Label from the issue.
|
|||
//
|
|||
// milestoned, demilestoned
|
|||
// The Actor added or removed the issue from the Milestone.
|
|||
//
|
|||
// subscribed, unsubscribed
|
|||
// The Actor subscribed to or unsubscribed from notifications for an issue.
|
|||
//
|
|||
// head_ref_deleted, head_ref_restored
|
|||
// The pull request’s branch was deleted or restored.
|
|||
//
|
|||
Event *string `json:"event,omitempty"` |
|||
|
|||
CreatedAt *time.Time `json:"created_at,omitempty"` |
|||
Issue *Issue `json:"issue,omitempty"` |
|||
|
|||
// Only present on certain events; see above.
|
|||
Assignee *User `json:"assignee,omitempty"` |
|||
CommitID *string `json:"commit_id,omitempty"` |
|||
Milestone *Milestone `json:"milestone,omitempty"` |
|||
Label *Label `json:"label,omitempty"` |
|||
Rename *Rename `json:"rename,omitempty"` |
|||
} |
|||
|
|||
// ListIssueEvents lists events for the specified issue.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/issues/events/#list-events-for-an-issue
|
|||
func (s *IssuesService) ListIssueEvents(owner, repo string, number int, opt *ListOptions) ([]*IssueEvent, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/issues/%v/events", owner, repo, number) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
var events []*IssueEvent |
|||
resp, err := s.client.Do(req, &events) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return events, resp, err |
|||
} |
|||
|
|||
// ListRepositoryEvents lists events for the specified repository.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/issues/events/#list-events-for-a-repository
|
|||
func (s *IssuesService) ListRepositoryEvents(owner, repo string, opt *ListOptions) ([]*IssueEvent, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/issues/events", owner, repo) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
var events []*IssueEvent |
|||
resp, err := s.client.Do(req, &events) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return events, resp, err |
|||
} |
|||
|
|||
// GetEvent returns the specified issue event.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/issues/events/#get-a-single-event
|
|||
func (s *IssuesService) GetEvent(owner, repo string, id int) (*IssueEvent, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/issues/events/%v", owner, repo, id) |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
event := new(IssueEvent) |
|||
resp, err := s.client.Do(req, event) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return event, resp, err |
|||
} |
|||
|
|||
// Rename contains details for 'renamed' events.
|
|||
type Rename struct { |
|||
From *string `json:"from,omitempty"` |
|||
To *string `json:"to,omitempty"` |
|||
} |
|||
|
|||
func (r Rename) String() string { |
|||
return Stringify(r) |
|||
} |
@ -0,0 +1,83 @@ |
|||
// Copyright 2014 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 ( |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestIssuesService_ListIssueEvents(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/issues/1/events", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{ |
|||
"page": "1", |
|||
"per_page": "2", |
|||
}) |
|||
fmt.Fprint(w, `[{"id":1}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 1, PerPage: 2} |
|||
events, _, err := client.Issues.ListIssueEvents("o", "r", 1, opt) |
|||
if err != nil { |
|||
t.Errorf("Issues.ListIssueEvents returned error: %v", err) |
|||
} |
|||
|
|||
want := []*IssueEvent{{ID: Int(1)}} |
|||
if !reflect.DeepEqual(events, want) { |
|||
t.Errorf("Issues.ListIssueEvents returned %+v, want %+v", events, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_ListRepositoryEvents(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/issues/events", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{ |
|||
"page": "1", |
|||
"per_page": "2", |
|||
}) |
|||
fmt.Fprint(w, `[{"id":1}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 1, PerPage: 2} |
|||
events, _, err := client.Issues.ListRepositoryEvents("o", "r", opt) |
|||
if err != nil { |
|||
t.Errorf("Issues.ListRepositoryEvents returned error: %v", err) |
|||
} |
|||
|
|||
want := []*IssueEvent{{ID: Int(1)}} |
|||
if !reflect.DeepEqual(events, want) { |
|||
t.Errorf("Issues.ListRepositoryEvents returned %+v, want %+v", events, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_GetEvent(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/issues/events/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{"id":1}`) |
|||
}) |
|||
|
|||
event, _, err := client.Issues.GetEvent("o", "r", 1) |
|||
if err != nil { |
|||
t.Errorf("Issues.GetEvent returned error: %v", err) |
|||
} |
|||
|
|||
want := &IssueEvent{ID: Int(1)} |
|||
if !reflect.DeepEqual(event, want) { |
|||
t.Errorf("Issues.GetEvent returned %+v, want %+v", event, want) |
|||
} |
|||
} |
@ -0,0 +1,222 @@ |
|||
// Copyright 2013 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 "fmt" |
|||
|
|||
// Label represents a GitHub label on an Issue
|
|||
type Label struct { |
|||
URL *string `json:"url,omitempty"` |
|||
Name *string `json:"name,omitempty"` |
|||
Color *string `json:"color,omitempty"` |
|||
} |
|||
|
|||
func (l Label) String() string { |
|||
return fmt.Sprint(*l.Name) |
|||
} |
|||
|
|||
// ListLabels lists all labels for a repository.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/issues/labels/#list-all-labels-for-this-repository
|
|||
func (s *IssuesService) ListLabels(owner string, repo string, opt *ListOptions) ([]*Label, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/labels", owner, repo) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
labels := new([]*Label) |
|||
resp, err := s.client.Do(req, labels) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *labels, resp, err |
|||
} |
|||
|
|||
// GetLabel gets a single label.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/issues/labels/#get-a-single-label
|
|||
func (s *IssuesService) GetLabel(owner string, repo string, name string) (*Label, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/labels/%v", owner, repo, name) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
label := new(Label) |
|||
resp, err := s.client.Do(req, label) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return label, resp, err |
|||
} |
|||
|
|||
// CreateLabel creates a new label on the specified repository.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/issues/labels/#create-a-label
|
|||
func (s *IssuesService) CreateLabel(owner string, repo string, label *Label) (*Label, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/labels", owner, repo) |
|||
req, err := s.client.NewRequest("POST", u, label) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
l := new(Label) |
|||
resp, err := s.client.Do(req, l) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return l, resp, err |
|||
} |
|||
|
|||
// EditLabel edits a label.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/issues/labels/#update-a-label
|
|||
func (s *IssuesService) EditLabel(owner string, repo string, name string, label *Label) (*Label, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/labels/%v", owner, repo, name) |
|||
req, err := s.client.NewRequest("PATCH", u, label) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
l := new(Label) |
|||
resp, err := s.client.Do(req, l) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return l, resp, err |
|||
} |
|||
|
|||
// DeleteLabel deletes a label.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/issues/labels/#delete-a-label
|
|||
func (s *IssuesService) DeleteLabel(owner string, repo string, name string) (*Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/labels/%v", owner, repo, name) |
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return s.client.Do(req, nil) |
|||
} |
|||
|
|||
// ListLabelsByIssue lists all labels for an issue.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/issues/labels/#list-all-labels-for-this-repository
|
|||
func (s *IssuesService) ListLabelsByIssue(owner string, repo string, number int, opt *ListOptions) ([]*Label, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/issues/%d/labels", owner, repo, number) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
labels := new([]*Label) |
|||
resp, err := s.client.Do(req, labels) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *labels, resp, err |
|||
} |
|||
|
|||
// AddLabelsToIssue adds labels to an issue.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/issues/labels/#list-all-labels-for-this-repository
|
|||
func (s *IssuesService) AddLabelsToIssue(owner string, repo string, number int, labels []string) ([]*Label, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/issues/%d/labels", owner, repo, number) |
|||
req, err := s.client.NewRequest("POST", u, labels) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
l := new([]*Label) |
|||
resp, err := s.client.Do(req, l) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *l, resp, err |
|||
} |
|||
|
|||
// RemoveLabelForIssue removes a label for an issue.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/issues/labels/#remove-a-label-from-an-issue
|
|||
func (s *IssuesService) RemoveLabelForIssue(owner string, repo string, number int, label string) (*Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/issues/%d/labels/%v", owner, repo, number, label) |
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return s.client.Do(req, nil) |
|||
} |
|||
|
|||
// ReplaceLabelsForIssue replaces all labels for an issue.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/issues/labels/#replace-all-labels-for-an-issue
|
|||
func (s *IssuesService) ReplaceLabelsForIssue(owner string, repo string, number int, labels []string) ([]*Label, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/issues/%d/labels", owner, repo, number) |
|||
req, err := s.client.NewRequest("PUT", u, labels) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
l := new([]*Label) |
|||
resp, err := s.client.Do(req, l) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *l, resp, err |
|||
} |
|||
|
|||
// RemoveLabelsForIssue removes all labels for an issue.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/issues/labels/#remove-all-labels-from-an-issue
|
|||
func (s *IssuesService) RemoveLabelsForIssue(owner string, repo string, number int) (*Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/issues/%d/labels", owner, repo, number) |
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return s.client.Do(req, nil) |
|||
} |
|||
|
|||
// ListLabelsForMilestone lists labels for every issue in a milestone.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/issues/labels/#get-labels-for-every-issue-in-a-milestone
|
|||
func (s *IssuesService) ListLabelsForMilestone(owner string, repo string, number int, opt *ListOptions) ([]*Label, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/milestones/%d/labels", owner, repo, number) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
labels := new([]*Label) |
|||
resp, err := s.client.Do(req, labels) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *labels, resp, err |
|||
} |
@ -0,0 +1,313 @@ |
|||
// Copyright 2013 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestIssuesService_ListLabels(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/labels", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{"page": "2"}) |
|||
fmt.Fprint(w, `[{"name": "a"},{"name": "b"}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 2} |
|||
labels, _, err := client.Issues.ListLabels("o", "r", opt) |
|||
if err != nil { |
|||
t.Errorf("Issues.ListLabels returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Label{{Name: String("a")}, {Name: String("b")}} |
|||
if !reflect.DeepEqual(labels, want) { |
|||
t.Errorf("Issues.ListLabels returned %+v, want %+v", labels, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_ListLabels_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Issues.ListLabels("%", "%", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestIssuesService_GetLabel(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/labels/n", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{"url":"u", "name": "n", "color": "c"}`) |
|||
}) |
|||
|
|||
label, _, err := client.Issues.GetLabel("o", "r", "n") |
|||
if err != nil { |
|||
t.Errorf("Issues.GetLabel returned error: %v", err) |
|||
} |
|||
|
|||
want := &Label{URL: String("u"), Name: String("n"), Color: String("c")} |
|||
if !reflect.DeepEqual(label, want) { |
|||
t.Errorf("Issues.GetLabel returned %+v, want %+v", label, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_GetLabel_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Issues.GetLabel("%", "%", "%") |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestIssuesService_CreateLabel(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &Label{Name: String("n")} |
|||
|
|||
mux.HandleFunc("/repos/o/r/labels", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(Label) |
|||
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, `{"url":"u"}`) |
|||
}) |
|||
|
|||
label, _, err := client.Issues.CreateLabel("o", "r", input) |
|||
if err != nil { |
|||
t.Errorf("Issues.CreateLabel returned error: %v", err) |
|||
} |
|||
|
|||
want := &Label{URL: String("u")} |
|||
if !reflect.DeepEqual(label, want) { |
|||
t.Errorf("Issues.CreateLabel returned %+v, want %+v", label, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_CreateLabel_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Issues.CreateLabel("%", "%", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestIssuesService_EditLabel(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &Label{Name: String("z")} |
|||
|
|||
mux.HandleFunc("/repos/o/r/labels/n", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(Label) |
|||
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, `{"url":"u"}`) |
|||
}) |
|||
|
|||
label, _, err := client.Issues.EditLabel("o", "r", "n", input) |
|||
if err != nil { |
|||
t.Errorf("Issues.EditLabel returned error: %v", err) |
|||
} |
|||
|
|||
want := &Label{URL: String("u")} |
|||
if !reflect.DeepEqual(label, want) { |
|||
t.Errorf("Issues.EditLabel returned %+v, want %+v", label, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_EditLabel_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Issues.EditLabel("%", "%", "%", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestIssuesService_DeleteLabel(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/labels/n", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
}) |
|||
|
|||
_, err := client.Issues.DeleteLabel("o", "r", "n") |
|||
if err != nil { |
|||
t.Errorf("Issues.DeleteLabel returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_DeleteLabel_invalidOwner(t *testing.T) { |
|||
_, err := client.Issues.DeleteLabel("%", "%", "%") |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestIssuesService_ListLabelsByIssue(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/issues/1/labels", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{"page": "2"}) |
|||
fmt.Fprint(w, `[{"name": "a"},{"name": "b"}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 2} |
|||
labels, _, err := client.Issues.ListLabelsByIssue("o", "r", 1, opt) |
|||
if err != nil { |
|||
t.Errorf("Issues.ListLabelsByIssue returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Label{{Name: String("a")}, {Name: String("b")}} |
|||
if !reflect.DeepEqual(labels, want) { |
|||
t.Errorf("Issues.ListLabelsByIssue returned %+v, want %+v", labels, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_ListLabelsByIssue_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Issues.ListLabelsByIssue("%", "%", 1, nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestIssuesService_AddLabelsToIssue(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := []string{"a", "b"} |
|||
|
|||
mux.HandleFunc("/repos/o/r/issues/1/labels", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new([]string) |
|||
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, `[{"url":"u"}]`) |
|||
}) |
|||
|
|||
labels, _, err := client.Issues.AddLabelsToIssue("o", "r", 1, input) |
|||
if err != nil { |
|||
t.Errorf("Issues.AddLabelsToIssue returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Label{{URL: String("u")}} |
|||
if !reflect.DeepEqual(labels, want) { |
|||
t.Errorf("Issues.AddLabelsToIssue returned %+v, want %+v", labels, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_AddLabelsToIssue_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Issues.AddLabelsToIssue("%", "%", 1, nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestIssuesService_RemoveLabelForIssue(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/issues/1/labels/l", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
}) |
|||
|
|||
_, err := client.Issues.RemoveLabelForIssue("o", "r", 1, "l") |
|||
if err != nil { |
|||
t.Errorf("Issues.RemoveLabelForIssue returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_RemoveLabelForIssue_invalidOwner(t *testing.T) { |
|||
_, err := client.Issues.RemoveLabelForIssue("%", "%", 1, "%") |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestIssuesService_ReplaceLabelsForIssue(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := []string{"a", "b"} |
|||
|
|||
mux.HandleFunc("/repos/o/r/issues/1/labels", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new([]string) |
|||
json.NewDecoder(r.Body).Decode(v) |
|||
|
|||
testMethod(t, r, "PUT") |
|||
if !reflect.DeepEqual(*v, input) { |
|||
t.Errorf("Request body = %+v, want %+v", v, input) |
|||
} |
|||
|
|||
fmt.Fprint(w, `[{"url":"u"}]`) |
|||
}) |
|||
|
|||
labels, _, err := client.Issues.ReplaceLabelsForIssue("o", "r", 1, input) |
|||
if err != nil { |
|||
t.Errorf("Issues.ReplaceLabelsForIssue returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Label{{URL: String("u")}} |
|||
if !reflect.DeepEqual(labels, want) { |
|||
t.Errorf("Issues.ReplaceLabelsForIssue returned %+v, want %+v", labels, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_ReplaceLabelsForIssue_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Issues.ReplaceLabelsForIssue("%", "%", 1, nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestIssuesService_RemoveLabelsForIssue(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/issues/1/labels", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
}) |
|||
|
|||
_, err := client.Issues.RemoveLabelsForIssue("o", "r", 1) |
|||
if err != nil { |
|||
t.Errorf("Issues.RemoveLabelsForIssue returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_RemoveLabelsForIssue_invalidOwner(t *testing.T) { |
|||
_, err := client.Issues.RemoveLabelsForIssue("%", "%", 1) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestIssuesService_ListLabelsForMilestone(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/milestones/1/labels", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{"page": "2"}) |
|||
fmt.Fprint(w, `[{"name": "a"},{"name": "b"}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 2} |
|||
labels, _, err := client.Issues.ListLabelsForMilestone("o", "r", 1, opt) |
|||
if err != nil { |
|||
t.Errorf("Issues.ListLabelsForMilestone returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Label{{Name: String("a")}, {Name: String("b")}} |
|||
if !reflect.DeepEqual(labels, want) { |
|||
t.Errorf("Issues.ListLabelsForMilestone returned %+v, want %+v", labels, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_ListLabelsForMilestone_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Issues.ListLabelsForMilestone("%", "%", 1, nil) |
|||
testURLParseError(t, err) |
|||
} |
@ -0,0 +1,146 @@ |
|||
// Copyright 2014 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 ( |
|||
"fmt" |
|||
"time" |
|||
) |
|||
|
|||
// Milestone represents a Github repository milestone.
|
|||
type Milestone struct { |
|||
URL *string `json:"url,omitempty"` |
|||
HTMLURL *string `json:"html_url,omitempty"` |
|||
LabelsURL *string `json:"labels_url,omitempty"` |
|||
ID *int `json:"id,omitempty"` |
|||
Number *int `json:"number,omitempty"` |
|||
State *string `json:"state,omitempty"` |
|||
Title *string `json:"title,omitempty"` |
|||
Description *string `json:"description,omitempty"` |
|||
Creator *User `json:"creator,omitempty"` |
|||
OpenIssues *int `json:"open_issues,omitempty"` |
|||
ClosedIssues *int `json:"closed_issues,omitempty"` |
|||
CreatedAt *time.Time `json:"created_at,omitempty"` |
|||
UpdatedAt *time.Time `json:"updated_at,omitempty"` |
|||
ClosedAt *time.Time `json:"closed_at,omitempty"` |
|||
DueOn *time.Time `json:"due_on,omitempty"` |
|||
} |
|||
|
|||
func (m Milestone) String() string { |
|||
return Stringify(m) |
|||
} |
|||
|
|||
// MilestoneListOptions specifies the optional parameters to the
|
|||
// IssuesService.ListMilestones method.
|
|||
type MilestoneListOptions struct { |
|||
// State filters milestones based on their state. Possible values are:
|
|||
// open, closed. Default is "open".
|
|||
State string `url:"state,omitempty"` |
|||
|
|||
// Sort specifies how to sort milestones. Possible values are: due_date, completeness.
|
|||
// Default value is "due_date".
|
|||
Sort string `url:"sort,omitempty"` |
|||
|
|||
// Direction in which to sort milestones. Possible values are: asc, desc.
|
|||
// Default is "asc".
|
|||
Direction string `url:"direction,omitempty"` |
|||
|
|||
ListOptions |
|||
} |
|||
|
|||
// ListMilestones lists all milestones for a repository.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/issues/milestones/#list-milestones-for-a-repository
|
|||
func (s *IssuesService) ListMilestones(owner string, repo string, opt *MilestoneListOptions) ([]*Milestone, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/milestones", owner, repo) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
milestones := new([]*Milestone) |
|||
resp, err := s.client.Do(req, milestones) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *milestones, resp, err |
|||
} |
|||
|
|||
// GetMilestone gets a single milestone.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/issues/milestones/#get-a-single-milestone
|
|||
func (s *IssuesService) GetMilestone(owner string, repo string, number int) (*Milestone, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/milestones/%d", owner, repo, number) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
milestone := new(Milestone) |
|||
resp, err := s.client.Do(req, milestone) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return milestone, resp, err |
|||
} |
|||
|
|||
// CreateMilestone creates a new milestone on the specified repository.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/issues/milestones/#create-a-milestone
|
|||
func (s *IssuesService) CreateMilestone(owner string, repo string, milestone *Milestone) (*Milestone, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/milestones", owner, repo) |
|||
req, err := s.client.NewRequest("POST", u, milestone) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
m := new(Milestone) |
|||
resp, err := s.client.Do(req, m) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return m, resp, err |
|||
} |
|||
|
|||
// EditMilestone edits a milestone.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/issues/milestones/#update-a-milestone
|
|||
func (s *IssuesService) EditMilestone(owner string, repo string, number int, milestone *Milestone) (*Milestone, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/milestones/%d", owner, repo, number) |
|||
req, err := s.client.NewRequest("PATCH", u, milestone) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
m := new(Milestone) |
|||
resp, err := s.client.Do(req, m) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return m, resp, err |
|||
} |
|||
|
|||
// DeleteMilestone deletes a milestone.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/issues/milestones/#delete-a-milestone
|
|||
func (s *IssuesService) DeleteMilestone(owner string, repo string, number int) (*Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/milestones/%d", owner, repo, number) |
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
return s.client.Do(req, nil) |
|||
} |
@ -0,0 +1,158 @@ |
|||
// Copyright 2014 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestIssuesService_ListMilestones(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/milestones", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{ |
|||
"state": "closed", |
|||
"sort": "due_date", |
|||
"direction": "asc", |
|||
"page": "2", |
|||
}) |
|||
fmt.Fprint(w, `[{"number":1}]`) |
|||
}) |
|||
|
|||
opt := &MilestoneListOptions{"closed", "due_date", "asc", ListOptions{Page: 2}} |
|||
milestones, _, err := client.Issues.ListMilestones("o", "r", opt) |
|||
if err != nil { |
|||
t.Errorf("IssuesService.ListMilestones returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Milestone{{Number: Int(1)}} |
|||
if !reflect.DeepEqual(milestones, want) { |
|||
t.Errorf("IssuesService.ListMilestones returned %+v, want %+v", milestones, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_ListMilestones_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Issues.ListMilestones("%", "r", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestIssuesService_GetMilestone(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/milestones/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{"number":1}`) |
|||
}) |
|||
|
|||
milestone, _, err := client.Issues.GetMilestone("o", "r", 1) |
|||
if err != nil { |
|||
t.Errorf("IssuesService.GetMilestone returned error: %v", err) |
|||
} |
|||
|
|||
want := &Milestone{Number: Int(1)} |
|||
if !reflect.DeepEqual(milestone, want) { |
|||
t.Errorf("IssuesService.GetMilestone returned %+v, want %+v", milestone, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_GetMilestone_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Issues.GetMilestone("%", "r", 1) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestIssuesService_CreateMilestone(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &Milestone{Title: String("t")} |
|||
|
|||
mux.HandleFunc("/repos/o/r/milestones", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(Milestone) |
|||
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}`) |
|||
}) |
|||
|
|||
milestone, _, err := client.Issues.CreateMilestone("o", "r", input) |
|||
if err != nil { |
|||
t.Errorf("IssuesService.CreateMilestone returned error: %v", err) |
|||
} |
|||
|
|||
want := &Milestone{Number: Int(1)} |
|||
if !reflect.DeepEqual(milestone, want) { |
|||
t.Errorf("IssuesService.CreateMilestone returned %+v, want %+v", milestone, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_CreateMilestone_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Issues.CreateMilestone("%", "r", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestIssuesService_EditMilestone(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &Milestone{Title: String("t")} |
|||
|
|||
mux.HandleFunc("/repos/o/r/milestones/1", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(Milestone) |
|||
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}`) |
|||
}) |
|||
|
|||
milestone, _, err := client.Issues.EditMilestone("o", "r", 1, input) |
|||
if err != nil { |
|||
t.Errorf("IssuesService.EditMilestone returned error: %v", err) |
|||
} |
|||
|
|||
want := &Milestone{Number: Int(1)} |
|||
if !reflect.DeepEqual(milestone, want) { |
|||
t.Errorf("IssuesService.EditMilestone returned %+v, want %+v", milestone, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_EditMilestone_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Issues.EditMilestone("%", "r", 1, nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestIssuesService_DeleteMilestone(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/milestones/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
}) |
|||
|
|||
_, err := client.Issues.DeleteMilestone("o", "r", 1) |
|||
if err != nil { |
|||
t.Errorf("IssuesService.DeleteMilestone returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_DeleteMilestone_invalidOwner(t *testing.T) { |
|||
_, err := client.Issues.DeleteMilestone("%", "r", 1) |
|||
testURLParseError(t, err) |
|||
} |
@ -0,0 +1,276 @@ |
|||
// Copyright 2013 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
"time" |
|||
) |
|||
|
|||
func TestIssuesService_List_all(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/issues", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeReactionsPreview) |
|||
testFormValues(t, r, values{ |
|||
"filter": "all", |
|||
"state": "closed", |
|||
"labels": "a,b", |
|||
"sort": "updated", |
|||
"direction": "asc", |
|||
"since": "2002-02-10T15:30:00Z", |
|||
"page": "1", |
|||
"per_page": "2", |
|||
}) |
|||
fmt.Fprint(w, `[{"number":1}]`) |
|||
}) |
|||
|
|||
opt := &IssueListOptions{ |
|||
"all", "closed", []string{"a", "b"}, "updated", "asc", |
|||
time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC), |
|||
ListOptions{Page: 1, PerPage: 2}, |
|||
} |
|||
issues, _, err := client.Issues.List(true, opt) |
|||
if err != nil { |
|||
t.Errorf("Issues.List returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Issue{{Number: Int(1)}} |
|||
if !reflect.DeepEqual(issues, want) { |
|||
t.Errorf("Issues.List returned %+v, want %+v", issues, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_List_owned(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/user/issues", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeReactionsPreview) |
|||
fmt.Fprint(w, `[{"number":1}]`) |
|||
}) |
|||
|
|||
issues, _, err := client.Issues.List(false, nil) |
|||
if err != nil { |
|||
t.Errorf("Issues.List returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Issue{{Number: Int(1)}} |
|||
if !reflect.DeepEqual(issues, want) { |
|||
t.Errorf("Issues.List returned %+v, want %+v", issues, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_ListByOrg(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/orgs/o/issues", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeReactionsPreview) |
|||
fmt.Fprint(w, `[{"number":1}]`) |
|||
}) |
|||
|
|||
issues, _, err := client.Issues.ListByOrg("o", nil) |
|||
if err != nil { |
|||
t.Errorf("Issues.ListByOrg returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Issue{{Number: Int(1)}} |
|||
if !reflect.DeepEqual(issues, want) { |
|||
t.Errorf("Issues.List returned %+v, want %+v", issues, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_ListByOrg_invalidOrg(t *testing.T) { |
|||
_, _, err := client.Issues.ListByOrg("%", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestIssuesService_ListByRepo(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/issues", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeReactionsPreview) |
|||
testFormValues(t, r, values{ |
|||
"milestone": "*", |
|||
"state": "closed", |
|||
"assignee": "a", |
|||
"creator": "c", |
|||
"mentioned": "m", |
|||
"labels": "a,b", |
|||
"sort": "updated", |
|||
"direction": "asc", |
|||
"since": "2002-02-10T15:30:00Z", |
|||
}) |
|||
fmt.Fprint(w, `[{"number":1}]`) |
|||
}) |
|||
|
|||
opt := &IssueListByRepoOptions{ |
|||
"*", "closed", "a", "c", "m", []string{"a", "b"}, "updated", "asc", |
|||
time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC), |
|||
ListOptions{0, 0}, |
|||
} |
|||
issues, _, err := client.Issues.ListByRepo("o", "r", opt) |
|||
if err != nil { |
|||
t.Errorf("Issues.ListByOrg returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Issue{{Number: Int(1)}} |
|||
if !reflect.DeepEqual(issues, want) { |
|||
t.Errorf("Issues.List returned %+v, want %+v", issues, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_ListByRepo_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Issues.ListByRepo("%", "r", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestIssuesService_Get(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/issues/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeReactionsPreview) |
|||
fmt.Fprint(w, `{"number":1, "labels": [{"url": "u", "name": "n", "color": "c"}]}`) |
|||
}) |
|||
|
|||
issue, _, err := client.Issues.Get("o", "r", 1) |
|||
if err != nil { |
|||
t.Errorf("Issues.Get returned error: %v", err) |
|||
} |
|||
|
|||
want := &Issue{ |
|||
Number: Int(1), |
|||
Labels: []Label{{ |
|||
URL: String("u"), |
|||
Name: String("n"), |
|||
Color: String("c"), |
|||
}}, |
|||
} |
|||
if !reflect.DeepEqual(issue, want) { |
|||
t.Errorf("Issues.Get returned %+v, want %+v", issue, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_Get_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Issues.Get("%", "r", 1) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestIssuesService_Create(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &IssueRequest{ |
|||
Title: String("t"), |
|||
Body: String("b"), |
|||
Assignee: String("a"), |
|||
Labels: &[]string{"l1", "l2"}, |
|||
} |
|||
|
|||
mux.HandleFunc("/repos/o/r/issues", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(IssueRequest) |
|||
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}`) |
|||
}) |
|||
|
|||
issue, _, err := client.Issues.Create("o", "r", input) |
|||
if err != nil { |
|||
t.Errorf("Issues.Create returned error: %v", err) |
|||
} |
|||
|
|||
want := &Issue{Number: Int(1)} |
|||
if !reflect.DeepEqual(issue, want) { |
|||
t.Errorf("Issues.Create returned %+v, want %+v", issue, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_Create_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Issues.Create("%", "r", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestIssuesService_Edit(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &IssueRequest{Title: String("t")} |
|||
|
|||
mux.HandleFunc("/repos/o/r/issues/1", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(IssueRequest) |
|||
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}`) |
|||
}) |
|||
|
|||
issue, _, err := client.Issues.Edit("o", "r", 1, input) |
|||
if err != nil { |
|||
t.Errorf("Issues.Edit returned error: %v", err) |
|||
} |
|||
|
|||
want := &Issue{Number: Int(1)} |
|||
if !reflect.DeepEqual(issue, want) { |
|||
t.Errorf("Issues.Edit returned %+v, want %+v", issue, want) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_Edit_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Issues.Edit("%", "r", 1, nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestIssuesService_Lock(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/issues/1/lock", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "PUT") |
|||
|
|||
w.WriteHeader(http.StatusNoContent) |
|||
}) |
|||
|
|||
if _, err := client.Issues.Lock("o", "r", 1); err != nil { |
|||
t.Errorf("Issues.Lock returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestIssuesService_Unlock(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/issues/1/lock", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
|
|||
w.WriteHeader(http.StatusNoContent) |
|||
}) |
|||
|
|||
if _, err := client.Issues.Unlock("o", "r", 1); err != nil { |
|||
t.Errorf("Issues.Unlock returned error: %v", err) |
|||
} |
|||
} |
@ -0,0 +1,148 @@ |
|||
// Copyright 2016 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 ( |
|||
"fmt" |
|||
"time" |
|||
) |
|||
|
|||
// Timeline represents an event that occurred around an Issue or Pull Request.
|
|||
//
|
|||
// It is similar to an IssueEvent but may contain more information.
|
|||
// GitHub API docs: https://developer.github.com/v3/issues/timeline/
|
|||
type Timeline struct { |
|||
ID *int `json:"id,omitempty"` |
|||
URL *string `json:"url,omitempty"` |
|||
CommitURL *string `json:"commit_url,omitempty"` |
|||
|
|||
// The User object that generated the event.
|
|||
Actor *User `json:"actor,omitempty"` |
|||
|
|||
// Event identifies the actual type of Event that occurred. Possible values
|
|||
// are:
|
|||
//
|
|||
// assigned
|
|||
// The issue was assigned to the assignee.
|
|||
//
|
|||
// closed
|
|||
// The issue was closed by the actor. When the commit_id is present, it
|
|||
// identifies the commit that closed the issue using "closes / fixes #NN"
|
|||
// syntax.
|
|||
//
|
|||
// commented
|
|||
// A comment was added to the issue.
|
|||
//
|
|||
// committed
|
|||
// A commit was added to the pull request's 'HEAD' branch. Only provided
|
|||
// for pull requests.
|
|||
//
|
|||
// cross-referenced
|
|||
// The issue was referenced from another issue. The 'source' attribute
|
|||
// contains the 'id', 'actor', and 'url' of the reference's source.
|
|||
//
|
|||
// demilestoned
|
|||
// The issue was removed from a milestone.
|
|||
//
|
|||
// head_ref_deleted
|
|||
// The pull request's branch was deleted.
|
|||
//
|
|||
// head_ref_restored
|
|||
// The pull request's branch was restored.
|
|||
//
|
|||
// labeled
|
|||
// A label was added to the issue.
|
|||
//
|
|||
// locked
|
|||
// The issue was locked by the actor.
|
|||
//
|
|||
// mentioned
|
|||
// The actor was @mentioned in an issue body.
|
|||
//
|
|||
// merged
|
|||
// The issue was merged by the actor. The 'commit_id' attribute is the
|
|||
// SHA1 of the HEAD commit that was merged.
|
|||
//
|
|||
// milestoned
|
|||
// The issue was added to a milestone.
|
|||
//
|
|||
// referenced
|
|||
// The issue was referenced from a commit message. The 'commit_id'
|
|||
// attribute is the commit SHA1 of where that happened.
|
|||
//
|
|||
// renamed
|
|||
// The issue title was changed.
|
|||
//
|
|||
// reopened
|
|||
// The issue was reopened by the actor.
|
|||
//
|
|||
// subscribed
|
|||
// The actor subscribed to receive notifications for an issue.
|
|||
//
|
|||
// unassigned
|
|||
// The assignee was unassigned from the issue.
|
|||
//
|
|||
// unlabeled
|
|||
// A label was removed from the issue.
|
|||
//
|
|||
// unlocked
|
|||
// The issue was unlocked by the actor.
|
|||
//
|
|||
// unsubscribed
|
|||
// The actor unsubscribed to stop receiving notifications for an issue.
|
|||
//
|
|||
Event *string `json:"event,omitempty"` |
|||
|
|||
// The string SHA of a commit that referenced this Issue or Pull Request.
|
|||
CommitID *string `json:"commit_id,omitempty"` |
|||
// The timestamp indicating when the event occurred.
|
|||
CreatedAt *time.Time `json:"created_at,omitempty"` |
|||
// The Label object including `name` and `color` attributes. Only provided for
|
|||
// 'labeled' and 'unlabeled' events.
|
|||
Label *Label `json:"label,omitempty"` |
|||
// The User object which was assigned to (or unassigned from) this Issue or
|
|||
// Pull Request. Only provided for 'assigned' and 'unassigned' events.
|
|||
Assignee *User `json:"assignee,omitempty"` |
|||
// The Milestone object including a 'title' attribute.
|
|||
// Only provided for 'milestoned' and 'demilestoned' events.
|
|||
Milestone *Milestone `json:"milestone,omitempty"` |
|||
// The 'id', 'actor', and 'url' for the source of a reference from another issue.
|
|||
// Only provided for 'cross-referenced' events.
|
|||
Source *Source `json:"source,omitempty"` |
|||
// An object containing rename details including 'from' and 'to' attributes.
|
|||
// Only provided for 'renamed' events.
|
|||
Rename *Rename `json:"rename,omitempty"` |
|||
} |
|||
|
|||
// Source represents a reference's source.
|
|||
type Source struct { |
|||
ID *int `json:"id,omitempty"` |
|||
URL *string `json:"url,omitempty"` |
|||
Actor *User `json:"actor,omitempty"` |
|||
} |
|||
|
|||
// ListIssueTimeline lists events for the specified issue.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/issues/timeline/#list-events-for-an-issue
|
|||
func (s *IssuesService) ListIssueTimeline(owner, repo string, number int, opt *ListOptions) ([]*Timeline, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/issues/%v/timeline", owner, repo, number) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeTimelinePreview) |
|||
|
|||
var events []*Timeline |
|||
resp, err := s.client.Do(req, &events) |
|||
return events, resp, err |
|||
} |
@ -0,0 +1,39 @@ |
|||
// Copyright 2016 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 ( |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestIssuesService_ListIssueTimeline(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/issues/1/timeline", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeTimelinePreview) |
|||
testFormValues(t, r, values{ |
|||
"page": "1", |
|||
"per_page": "2", |
|||
}) |
|||
fmt.Fprint(w, `[{"id":1}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 1, PerPage: 2} |
|||
events, _, err := client.Issues.ListIssueTimeline("o", "r", 1, opt) |
|||
if err != nil { |
|||
t.Errorf("Issues.ListIssueTimeline returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Timeline{{ID: Int(1)}} |
|||
if !reflect.DeepEqual(events, want) { |
|||
t.Errorf("Issues.ListIssueTimeline = %+v, want %+v", events, want) |
|||
} |
|||
} |
@ -0,0 +1,79 @@ |
|||
// Copyright 2013 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 "fmt" |
|||
|
|||
// LicensesService handles communication with the license related
|
|||
// methods of the GitHub API.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/pulls/
|
|||
type LicensesService service |
|||
|
|||
// License represents an open source license.
|
|||
type License struct { |
|||
Key *string `json:"key,omitempty"` |
|||
Name *string `json:"name,omitempty"` |
|||
URL *string `json:"url,omitempty"` |
|||
|
|||
HTMLURL *string `json:"html_url,omitempty"` |
|||
Featured *bool `json:"featured,omitempty"` |
|||
Description *string `json:"description,omitempty"` |
|||
Category *string `json:"category,omitempty"` |
|||
Implementation *string `json:"implementation,omitempty"` |
|||
Required *[]string `json:"required,omitempty"` |
|||
Permitted *[]string `json:"permitted,omitempty"` |
|||
Forbidden *[]string `json:"forbidden,omitempty"` |
|||
Body *string `json:"body,omitempty"` |
|||
} |
|||
|
|||
func (l License) String() string { |
|||
return Stringify(l) |
|||
} |
|||
|
|||
// List popular open source licenses.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/licenses/#list-all-licenses
|
|||
func (s *LicensesService) List() ([]*License, *Response, error) { |
|||
req, err := s.client.NewRequest("GET", "licenses", nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches
|
|||
req.Header.Set("Accept", mediaTypeLicensesPreview) |
|||
|
|||
licenses := new([]*License) |
|||
resp, err := s.client.Do(req, licenses) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *licenses, resp, err |
|||
} |
|||
|
|||
// Get extended metadata for one license.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/licenses/#get-an-individual-license
|
|||
func (s *LicensesService) Get(licenseName string) (*License, *Response, error) { |
|||
u := fmt.Sprintf("licenses/%s", licenseName) |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches
|
|||
req.Header.Set("Accept", mediaTypeLicensesPreview) |
|||
|
|||
license := new(License) |
|||
resp, err := s.client.Do(req, license) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return license, resp, err |
|||
} |
@ -0,0 +1,64 @@ |
|||
// Copyright 2013 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 ( |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestLicensesService_List(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/licenses", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeLicensesPreview) |
|||
fmt.Fprint(w, `[{"key":"mit","name":"MIT","url":"https://api.github.com/licenses/mit"}]`) |
|||
}) |
|||
|
|||
licenses, _, err := client.Licenses.List() |
|||
if err != nil { |
|||
t.Errorf("Licenses.List returned error: %v", err) |
|||
} |
|||
|
|||
want := []*License{{ |
|||
Key: String("mit"), |
|||
Name: String("MIT"), |
|||
URL: String("https://api.github.com/licenses/mit"), |
|||
}} |
|||
if !reflect.DeepEqual(licenses, want) { |
|||
t.Errorf("Licenses.List returned %+v, want %+v", licenses, want) |
|||
} |
|||
} |
|||
|
|||
func TestLicensesService_Get(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/licenses/mit", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeLicensesPreview) |
|||
fmt.Fprint(w, `{"key":"mit","name":"MIT"}`) |
|||
}) |
|||
|
|||
license, _, err := client.Licenses.Get("mit") |
|||
if err != nil { |
|||
t.Errorf("Licenses.Get returned error: %v", err) |
|||
} |
|||
|
|||
want := &License{Key: String("mit"), Name: String("MIT")} |
|||
if !reflect.DeepEqual(license, want) { |
|||
t.Errorf("Licenses.Get returned %+v, want %+v", license, want) |
|||
} |
|||
} |
|||
|
|||
func TestLicensesService_Get_invalidTemplate(t *testing.T) { |
|||
_, _, err := client.Licenses.Get("%") |
|||
testURLParseError(t, err) |
|||
} |
@ -0,0 +1,119 @@ |
|||
// Copyright 2016 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.
|
|||
|
|||
// This file provides functions for validating payloads from GitHub Webhooks.
|
|||
// GitHub docs: https://developer.github.com/webhooks/securing/#validating-payloads-from-github
|
|||
|
|||
package github |
|||
|
|||
import ( |
|||
"crypto/hmac" |
|||
"crypto/sha1" |
|||
"crypto/sha256" |
|||
"crypto/sha512" |
|||
"encoding/hex" |
|||
"errors" |
|||
"fmt" |
|||
"hash" |
|||
"io/ioutil" |
|||
"net/http" |
|||
"strings" |
|||
) |
|||
|
|||
const ( |
|||
// sha1Prefix is the prefix used by GitHub before the HMAC hexdigest.
|
|||
sha1Prefix = "sha1" |
|||
// sha256Prefix and sha512Prefix are provided for future compatibility.
|
|||
sha256Prefix = "sha256" |
|||
sha512Prefix = "sha512" |
|||
// signatureHeader is the GitHub header key used to pass the HMAC hexdigest.
|
|||
signatureHeader = "X-Hub-Signature" |
|||
) |
|||
|
|||
// genMAC generates the HMAC signature for a message provided the secret key
|
|||
// and hashFunc.
|
|||
func genMAC(message, key []byte, hashFunc func() hash.Hash) []byte { |
|||
mac := hmac.New(hashFunc, key) |
|||
mac.Write(message) |
|||
return mac.Sum(nil) |
|||
} |
|||
|
|||
// checkMAC reports whether messageMAC is a valid HMAC tag for message.
|
|||
func checkMAC(message, messageMAC, key []byte, hashFunc func() hash.Hash) bool { |
|||
expectedMAC := genMAC(message, key, hashFunc) |
|||
return hmac.Equal(messageMAC, expectedMAC) |
|||
} |
|||
|
|||
// messageMAC returns the hex-decoded HMAC tag from the signature and its
|
|||
// corresponding hash function.
|
|||
func messageMAC(signature string) ([]byte, func() hash.Hash, error) { |
|||
if signature == "" { |
|||
return nil, nil, errors.New("missing signature") |
|||
} |
|||
sigParts := strings.SplitN(signature, "=", 2) |
|||
if len(sigParts) != 2 { |
|||
return nil, nil, fmt.Errorf("error parsing signature %q", signature) |
|||
} |
|||
|
|||
var hashFunc func() hash.Hash |
|||
switch sigParts[0] { |
|||
case sha1Prefix: |
|||
hashFunc = sha1.New |
|||
case sha256Prefix: |
|||
hashFunc = sha256.New |
|||
case sha512Prefix: |
|||
hashFunc = sha512.New |
|||
default: |
|||
return nil, nil, fmt.Errorf("unknown hash type prefix: %q", sigParts[0]) |
|||
} |
|||
|
|||
buf, err := hex.DecodeString(sigParts[1]) |
|||
if err != nil { |
|||
return nil, nil, fmt.Errorf("error decoding signature %q: %v", signature, err) |
|||
} |
|||
return buf, hashFunc, nil |
|||
} |
|||
|
|||
// ValidatePayload validates an incoming GitHub Webhook event request
|
|||
// and returns the (JSON) payload.
|
|||
// secretKey is the GitHub Webhook secret message.
|
|||
//
|
|||
// Example usage:
|
|||
//
|
|||
// func (s *GitHubEventMonitor) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
// payload, err := github.ValidatePayload(r, s.webhookSecretKey)
|
|||
// if err != nil { ... }
|
|||
// // Process payload...
|
|||
// }
|
|||
//
|
|||
func ValidatePayload(r *http.Request, secretKey []byte) (payload []byte, err error) { |
|||
payload, err = ioutil.ReadAll(r.Body) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
sig := r.Header.Get(signatureHeader) |
|||
if err := validateSignature(sig, payload, secretKey); err != nil { |
|||
return nil, err |
|||
} |
|||
return payload, nil |
|||
} |
|||
|
|||
// validateSignature validates the signature for the given payload.
|
|||
// signature is the GitHub hash signature delivered in the X-Hub-Signature header.
|
|||
// payload is the JSON payload sent by GitHub Webhooks.
|
|||
// secretKey is the GitHub Webhook secret message.
|
|||
//
|
|||
// GitHub docs: https://developer.github.com/webhooks/securing/#validating-payloads-from-github
|
|||
func validateSignature(signature string, payload, secretKey []byte) error { |
|||
messageMAC, hashFunc, err := messageMAC(signature) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
if !checkMAC(payload, messageMAC, secretKey, hashFunc) { |
|||
return errors.New("payload signature check failed") |
|||
} |
|||
return nil |
|||
} |
@ -0,0 +1,81 @@ |
|||
// Copyright 2016 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 ( |
|||
"bytes" |
|||
"net/http" |
|||
"testing" |
|||
) |
|||
|
|||
func TestValidatePayload(t *testing.T) { |
|||
const defaultBody = `{"yo":true}` // All tests below use the default request body and signature.
|
|||
const defaultSignature = "sha1=126f2c800419c60137ce748d7672e77b65cf16d6" |
|||
secretKey := []byte("0123456789abcdef") |
|||
tests := []struct { |
|||
signature string |
|||
eventID string |
|||
event string |
|||
wantEventID string |
|||
wantEvent string |
|||
wantPayload string |
|||
}{ |
|||
// The following tests generate expected errors:
|
|||
{}, // Missing signature
|
|||
{signature: "yo"}, // Missing signature prefix
|
|||
{signature: "sha1=yo"}, // Signature not hex string
|
|||
{signature: "sha1=012345"}, // Invalid signature
|
|||
// The following tests expect err=nil:
|
|||
{ |
|||
signature: defaultSignature, |
|||
eventID: "dead-beef", |
|||
event: "ping", |
|||
wantEventID: "dead-beef", |
|||
wantEvent: "ping", |
|||
wantPayload: defaultBody, |
|||
}, |
|||
{ |
|||
signature: defaultSignature, |
|||
event: "ping", |
|||
wantEvent: "ping", |
|||
wantPayload: defaultBody, |
|||
}, |
|||
{ |
|||
signature: "sha256=b1f8020f5b4cd42042f807dd939015c4a418bc1ff7f604dd55b0a19b5d953d9b", |
|||
event: "ping", |
|||
wantEvent: "ping", |
|||
wantPayload: defaultBody, |
|||
}, |
|||
{ |
|||
signature: "sha512=8456767023c1195682e182a23b3f5d19150ecea598fde8cb85918f7281b16079471b1329f92b912c4d8bd7455cb159777db8f29608b20c7c87323ba65ae62e1f", |
|||
event: "ping", |
|||
wantEvent: "ping", |
|||
wantPayload: defaultBody, |
|||
}, |
|||
} |
|||
|
|||
for _, test := range tests { |
|||
buf := bytes.NewBufferString(defaultBody) |
|||
req, err := http.NewRequest("GET", "http://localhost/event", buf) |
|||
if err != nil { |
|||
t.Fatalf("NewRequest: %v", err) |
|||
} |
|||
if test.signature != "" { |
|||
req.Header.Set(signatureHeader, test.signature) |
|||
} |
|||
|
|||
got, err := ValidatePayload(req, secretKey) |
|||
if err != nil { |
|||
if test.wantPayload != "" { |
|||
t.Errorf("ValidatePayload(%#v): err = %v, want nil", test, err) |
|||
} |
|||
continue |
|||
} |
|||
if string(got) != test.wantPayload { |
|||
t.Errorf("ValidatePayload = %q, want %q", got, test.wantPayload) |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,223 @@ |
|||
// Copyright 2016 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 ( |
|||
"errors" |
|||
"fmt" |
|||
"net/http" |
|||
"strings" |
|||
) |
|||
|
|||
// MigrationService provides access to the migration related functions
|
|||
// in the GitHub API.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/migration/
|
|||
type MigrationService service |
|||
|
|||
// Migration represents a GitHub migration (archival).
|
|||
type Migration struct { |
|||
ID *int `json:"id,omitempty"` |
|||
GUID *string `json:"guid,omitempty"` |
|||
// State is the current state of a migration.
|
|||
// Possible values are:
|
|||
// "pending" which means the migration hasn't started yet,
|
|||
// "exporting" which means the migration is in progress,
|
|||
// "exported" which means the migration finished successfully, or
|
|||
// "failed" which means the migration failed.
|
|||
State *string `json:"state,omitempty"` |
|||
// LockRepositories indicates whether repositories are locked (to prevent
|
|||
// manipulation) while migrating data.
|
|||
LockRepositories *bool `json:"lock_repositories,omitempty"` |
|||
// ExcludeAttachments indicates whether attachments should be excluded from
|
|||
// the migration (to reduce migration archive file size).
|
|||
ExcludeAttachments *bool `json:"exclude_attachments,omitempty"` |
|||
URL *string `json:"url,omitempty"` |
|||
CreatedAt *string `json:"created_at,omitempty"` |
|||
UpdatedAt *string `json:"updated_at,omitempty"` |
|||
Repositories []*Repository `json:"repositories,omitempty"` |
|||
} |
|||
|
|||
func (m Migration) String() string { |
|||
return Stringify(m) |
|||
} |
|||
|
|||
// MigrationOptions specifies the optional parameters to Migration methods.
|
|||
type MigrationOptions struct { |
|||
// LockRepositories indicates whether repositories should be locked (to prevent
|
|||
// manipulation) while migrating data.
|
|||
LockRepositories bool |
|||
|
|||
// ExcludeAttachments indicates whether attachments should be excluded from
|
|||
// the migration (to reduce migration archive file size).
|
|||
ExcludeAttachments bool |
|||
} |
|||
|
|||
// startMigration represents the body of a StartMigration request.
|
|||
type startMigration struct { |
|||
// Repositories is a slice of repository names to migrate.
|
|||
Repositories []string `json:"repositories,omitempty"` |
|||
|
|||
// LockRepositories indicates whether repositories should be locked (to prevent
|
|||
// manipulation) while migrating data.
|
|||
LockRepositories *bool `json:"lock_repositories,omitempty"` |
|||
|
|||
// ExcludeAttachments indicates whether attachments should be excluded from
|
|||
// the migration (to reduce migration archive file size).
|
|||
ExcludeAttachments *bool `json:"exclude_attachments,omitempty"` |
|||
} |
|||
|
|||
// StartMigration starts the generation of a migration archive.
|
|||
// repos is a slice of repository names to migrate.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/migration/migrations/#start-a-migration
|
|||
func (s *MigrationService) StartMigration(org string, repos []string, opt *MigrationOptions) (*Migration, *Response, error) { |
|||
u := fmt.Sprintf("orgs/%v/migrations", org) |
|||
|
|||
body := &startMigration{Repositories: repos} |
|||
if opt != nil { |
|||
body.LockRepositories = Bool(opt.LockRepositories) |
|||
body.ExcludeAttachments = Bool(opt.ExcludeAttachments) |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("POST", u, body) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeMigrationsPreview) |
|||
|
|||
m := &Migration{} |
|||
resp, err := s.client.Do(req, m) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return m, resp, nil |
|||
} |
|||
|
|||
// ListMigrations lists the most recent migrations.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/migration/migrations/#get-a-list-of-migrations
|
|||
func (s *MigrationService) ListMigrations(org string) ([]*Migration, *Response, error) { |
|||
u := fmt.Sprintf("orgs/%v/migrations", org) |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeMigrationsPreview) |
|||
|
|||
var m []*Migration |
|||
resp, err := s.client.Do(req, &m) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return m, resp, nil |
|||
} |
|||
|
|||
// MigrationStatus gets the status of a specific migration archive.
|
|||
// id is the migration ID.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/migration/migrations/#get-the-status-of-a-migration
|
|||
func (s *MigrationService) MigrationStatus(org string, id int) (*Migration, *Response, error) { |
|||
u := fmt.Sprintf("orgs/%v/migrations/%v", org, id) |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeMigrationsPreview) |
|||
|
|||
m := &Migration{} |
|||
resp, err := s.client.Do(req, m) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return m, resp, nil |
|||
} |
|||
|
|||
// MigrationArchiveURL fetches a migration archive URL.
|
|||
// id is the migration ID.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/migration/migrations/#download-a-migration-archive
|
|||
func (s *MigrationService) MigrationArchiveURL(org string, id int) (url string, err error) { |
|||
u := fmt.Sprintf("orgs/%v/migrations/%v/archive", org, id) |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return "", err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeMigrationsPreview) |
|||
|
|||
s.client.clientMu.Lock() |
|||
defer s.client.clientMu.Unlock() |
|||
|
|||
// Disable the redirect mechanism because AWS fails if the GitHub auth token is provided.
|
|||
var loc string |
|||
saveRedirect := s.client.client.CheckRedirect |
|||
s.client.client.CheckRedirect = func(req *http.Request, via []*http.Request) error { |
|||
loc = req.URL.String() |
|||
return errors.New("disable redirect") |
|||
} |
|||
defer func() { s.client.client.CheckRedirect = saveRedirect }() |
|||
|
|||
_, err = s.client.Do(req, nil) // expect error from disable redirect
|
|||
if err == nil { |
|||
return "", errors.New("expected redirect, none provided") |
|||
} |
|||
if !strings.Contains(err.Error(), "disable redirect") { |
|||
return "", err |
|||
} |
|||
return loc, nil |
|||
} |
|||
|
|||
// DeleteMigration deletes a previous migration archive.
|
|||
// id is the migration ID.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/migration/migrations/#delete-a-migration-archive
|
|||
func (s *MigrationService) DeleteMigration(org string, id int) (*Response, error) { |
|||
u := fmt.Sprintf("orgs/%v/migrations/%v/archive", org, id) |
|||
|
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeMigrationsPreview) |
|||
|
|||
return s.client.Do(req, nil) |
|||
} |
|||
|
|||
// UnlockRepo unlocks a repository that was locked for migration.
|
|||
// id is the migration ID.
|
|||
// You should unlock each migrated repository and delete them when the migration
|
|||
// is complete and you no longer need the source data.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/migration/migrations/#unlock-a-repository
|
|||
func (s *MigrationService) UnlockRepo(org string, id int, repo string) (*Response, error) { |
|||
u := fmt.Sprintf("orgs/%v/migrations/%v/repos/%v/lock", org, id, repo) |
|||
|
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeMigrationsPreview) |
|||
|
|||
return s.client.Do(req, nil) |
|||
} |
@ -0,0 +1,326 @@ |
|||
// Copyright 2016 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 "fmt" |
|||
|
|||
// Import represents a repository import request.
|
|||
type Import struct { |
|||
// The URL of the originating repository.
|
|||
VCSURL *string `json:"vcs_url,omitempty"` |
|||
// The originating VCS type. Can be one of 'subversion', 'git',
|
|||
// 'mercurial', or 'tfvc'. Without this parameter, the import job will
|
|||
// take additional time to detect the VCS type before beginning the
|
|||
// import. This detection step will be reflected in the response.
|
|||
VCS *string `json:"vcs,omitempty"` |
|||
// VCSUsername and VCSPassword are only used for StartImport calls that
|
|||
// are importing a password-protected repository.
|
|||
VCSUsername *string `json:"vcs_username,omitempty"` |
|||
VCSPassword *string `json:"vcs_password,omitempty"` |
|||
// For a tfvc import, the name of the project that is being imported.
|
|||
TFVCProject *string `json:"tfvc_project,omitempty"` |
|||
|
|||
// LFS related fields that may be preset in the Import Progress response
|
|||
|
|||
// Describes whether the import has been opted in or out of using Git
|
|||
// LFS. The value can be 'opt_in', 'opt_out', or 'undecided' if no
|
|||
// action has been taken.
|
|||
UseLFS *string `json:"use_lfs,omitempty"` |
|||
// Describes whether files larger than 100MB were found during the
|
|||
// importing step.
|
|||
HasLargeFiles *bool `json:"has_large_files,omitempty"` |
|||
// The total size in gigabytes of files larger than 100MB found in the
|
|||
// originating repository.
|
|||
LargeFilesSize *int `json:"large_files_size,omitempty"` |
|||
// The total number of files larger than 100MB found in the originating
|
|||
// repository. To see a list of these files, call LargeFiles.
|
|||
LargeFilesCount *int `json:"large_files_count,omitempty"` |
|||
|
|||
// Identifies the current status of an import. An import that does not
|
|||
// have errors will progress through these steps:
|
|||
//
|
|||
// detecting - the "detection" step of the import is in progress
|
|||
// because the request did not include a VCS parameter. The
|
|||
// import is identifying the type of source control present at
|
|||
// the URL.
|
|||
// importing - the "raw" step of the import is in progress. This is
|
|||
// where commit data is fetched from the original repository.
|
|||
// The import progress response will include CommitCount (the
|
|||
// total number of raw commits that will be imported) and
|
|||
// Percent (0 - 100, the current progress through the import).
|
|||
// mapping - the "rewrite" step of the import is in progress. This
|
|||
// is where SVN branches are converted to Git branches, and
|
|||
// where author updates are applied. The import progress
|
|||
// response does not include progress information.
|
|||
// pushing - the "push" step of the import is in progress. This is
|
|||
// where the importer updates the repository on GitHub. The
|
|||
// import progress response will include PushPercent, which is
|
|||
// the percent value reported by git push when it is "Writing
|
|||
// objects".
|
|||
// complete - the import is complete, and the repository is ready
|
|||
// on GitHub.
|
|||
//
|
|||
// If there are problems, you will see one of these in the status field:
|
|||
//
|
|||
// auth_failed - the import requires authentication in order to
|
|||
// connect to the original repository. Make an UpdateImport
|
|||
// request, and include VCSUsername and VCSPassword.
|
|||
// error - the import encountered an error. The import progress
|
|||
// response will include the FailedStep and an error message.
|
|||
// Contact GitHub support for more information.
|
|||
// detection_needs_auth - the importer requires authentication for
|
|||
// the originating repository to continue detection. Make an
|
|||
// UpdatImport request, and include VCSUsername and
|
|||
// VCSPassword.
|
|||
// detection_found_nothing - the importer didn't recognize any
|
|||
// source control at the URL.
|
|||
// detection_found_multiple - the importer found several projects
|
|||
// or repositories at the provided URL. When this is the case,
|
|||
// the Import Progress response will also include a
|
|||
// ProjectChoices field with the possible project choices as
|
|||
// values. Make an UpdateImport request, and include VCS and
|
|||
// (if applicable) TFVCProject.
|
|||
Status *string `json:"status,omitempty"` |
|||
CommitCount *int `json:"commit_count,omitempty"` |
|||
StatusText *string `json:"status_text,omitempty"` |
|||
AuthorsCount *int `json:"authors_count,omitempty"` |
|||
Percent *int `json:"percent,omitempty"` |
|||
PushPercent *int `json:"push_percent,omitempty"` |
|||
URL *string `json:"url,omitempty"` |
|||
HTMLURL *string `json:"html_url,omitempty"` |
|||
AuthorsURL *string `json:"authors_url,omitempty"` |
|||
RepositoryURL *string `json:"repository_url,omitempty"` |
|||
Message *string `json:"message,omitempty"` |
|||
FailedStep *string `json:"failed_step,omitempty"` |
|||
|
|||
// Human readable display name, provided when the Import appears as
|
|||
// part of ProjectChoices.
|
|||
HumanName *string `json:"human_name,omitempty"` |
|||
|
|||
// When the importer finds several projects or repositories at the
|
|||
// provided URLs, this will identify the available choices. Call
|
|||
// UpdateImport with the selected Import value.
|
|||
ProjectChoices []Import `json:"project_choices,omitempty"` |
|||
} |
|||
|
|||
func (i Import) String() string { |
|||
return Stringify(i) |
|||
} |
|||
|
|||
// SourceImportAuthor identifies an author imported from a source repository.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-commit-authors
|
|||
type SourceImportAuthor struct { |
|||
ID *int `json:"id,omitempty"` |
|||
RemoteID *string `json:"remote_id,omitempty"` |
|||
RemoteName *string `json:"remote_name,omitempty"` |
|||
Email *string `json:"email,omitempty"` |
|||
Name *string `json:"name,omitempty"` |
|||
URL *string `json:"url,omitempty"` |
|||
ImportURL *string `json:"import_url,omitempty"` |
|||
} |
|||
|
|||
func (a SourceImportAuthor) String() string { |
|||
return Stringify(a) |
|||
} |
|||
|
|||
// LargeFile identifies a file larger than 100MB found during a repository import.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-large-files
|
|||
type LargeFile struct { |
|||
RefName *string `json:"ref_name,omitempty"` |
|||
Path *string `json:"path,omitempty"` |
|||
OID *string `json:"oid,omitempty"` |
|||
Size *int `json:"size,omitempty"` |
|||
} |
|||
|
|||
func (f LargeFile) String() string { |
|||
return Stringify(f) |
|||
} |
|||
|
|||
// StartImport initiates a repository import.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#start-an-import
|
|||
func (s *MigrationService) StartImport(owner, repo string, in *Import) (*Import, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/import", owner, repo) |
|||
req, err := s.client.NewRequest("PUT", u, in) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches
|
|||
req.Header.Set("Accept", mediaTypeImportPreview) |
|||
|
|||
out := new(Import) |
|||
resp, err := s.client.Do(req, out) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return out, resp, err |
|||
} |
|||
|
|||
// QueryImport queries for the status and progress of an ongoing repository import.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-import-progress
|
|||
func (s *MigrationService) ImportProgress(owner, repo string) (*Import, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/import", owner, repo) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches
|
|||
req.Header.Set("Accept", mediaTypeImportPreview) |
|||
|
|||
out := new(Import) |
|||
resp, err := s.client.Do(req, out) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return out, resp, err |
|||
} |
|||
|
|||
// UpdateImport initiates a repository import.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#update-existing-import
|
|||
func (s *MigrationService) UpdateImport(owner, repo string, in *Import) (*Import, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/import", owner, repo) |
|||
req, err := s.client.NewRequest("PATCH", u, in) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches
|
|||
req.Header.Set("Accept", mediaTypeImportPreview) |
|||
|
|||
out := new(Import) |
|||
resp, err := s.client.Do(req, out) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return out, resp, err |
|||
} |
|||
|
|||
// CommitAuthors gets the authors mapped from the original repository.
|
|||
//
|
|||
// Each type of source control system represents authors in a different way.
|
|||
// For example, a Git commit author has a display name and an email address,
|
|||
// but a Subversion commit author just has a username. The GitHub Importer will
|
|||
// make the author information valid, but the author might not be correct. For
|
|||
// example, it will change the bare Subversion username "hubot" into something
|
|||
// like "hubot <hubot@12341234-abab-fefe-8787-fedcba987654>".
|
|||
//
|
|||
// This method and MapCommitAuthor allow you to provide correct Git author
|
|||
// information.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-commit-authors
|
|||
func (s *MigrationService) CommitAuthors(owner, repo string) ([]*SourceImportAuthor, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/import/authors", owner, repo) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches
|
|||
req.Header.Set("Accept", mediaTypeImportPreview) |
|||
|
|||
authors := new([]*SourceImportAuthor) |
|||
resp, err := s.client.Do(req, authors) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *authors, resp, err |
|||
} |
|||
|
|||
// MapCommitAuthor updates an author's identity for the import. Your
|
|||
// application can continue updating authors any time before you push new
|
|||
// commits to the repository.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#map-a-commit-author
|
|||
func (s *MigrationService) MapCommitAuthor(owner, repo string, id int, author *SourceImportAuthor) (*SourceImportAuthor, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/import/authors/%v", owner, repo, id) |
|||
req, err := s.client.NewRequest("PATCH", u, author) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches
|
|||
req.Header.Set("Accept", mediaTypeImportPreview) |
|||
|
|||
out := new(SourceImportAuthor) |
|||
resp, err := s.client.Do(req, out) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return out, resp, err |
|||
} |
|||
|
|||
// SetLFSPreference sets whether imported repositories should use Git LFS for
|
|||
// files larger than 100MB. Only the UseLFS field on the provided Import is
|
|||
// used.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#set-git-lfs-preference
|
|||
func (s *MigrationService) SetLFSPreference(owner, repo string, in *Import) (*Import, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/import/lfs", owner, repo) |
|||
req, err := s.client.NewRequest("PATCH", u, in) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches
|
|||
req.Header.Set("Accept", mediaTypeImportPreview) |
|||
|
|||
out := new(Import) |
|||
resp, err := s.client.Do(req, out) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return out, resp, err |
|||
} |
|||
|
|||
// LargeFiles lists files larger than 100MB found during the import.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-large-files
|
|||
func (s *MigrationService) LargeFiles(owner, repo string) ([]*LargeFile, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/import/large_files", owner, repo) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches
|
|||
req.Header.Set("Accept", mediaTypeImportPreview) |
|||
|
|||
files := new([]*LargeFile) |
|||
resp, err := s.client.Do(req, files) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *files, resp, err |
|||
} |
|||
|
|||
// CancelImport stops an import for a repository.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#cancel-an-import
|
|||
func (s *MigrationService) CancelImport(owner, repo string) (*Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/import", owner, repo) |
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches
|
|||
req.Header.Set("Accept", mediaTypeImportPreview) |
|||
|
|||
return s.client.Do(req, nil) |
|||
} |
@ -0,0 +1,225 @@ |
|||
// Copyright 2016 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestMigrationService_StartImport(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &Import{ |
|||
VCS: String("git"), |
|||
VCSURL: String("url"), |
|||
VCSUsername: String("u"), |
|||
VCSPassword: String("p"), |
|||
} |
|||
|
|||
mux.HandleFunc("/repos/o/r/import", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(Import) |
|||
json.NewDecoder(r.Body).Decode(v) |
|||
|
|||
testMethod(t, r, "PUT") |
|||
testHeader(t, r, "Accept", mediaTypeImportPreview) |
|||
if !reflect.DeepEqual(v, input) { |
|||
t.Errorf("Request body = %+v, want %+v", v, input) |
|||
} |
|||
|
|||
w.WriteHeader(http.StatusCreated) |
|||
fmt.Fprint(w, `{"status":"importing"}`) |
|||
}) |
|||
|
|||
got, _, err := client.Migrations.StartImport("o", "r", input) |
|||
if err != nil { |
|||
t.Errorf("StartImport returned error: %v", err) |
|||
} |
|||
want := &Import{Status: String("importing")} |
|||
if !reflect.DeepEqual(got, want) { |
|||
t.Errorf("StartImport = %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestMigrationService_ImportProgress(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/import", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeImportPreview) |
|||
fmt.Fprint(w, `{"status":"complete"}`) |
|||
}) |
|||
|
|||
got, _, err := client.Migrations.ImportProgress("o", "r") |
|||
if err != nil { |
|||
t.Errorf("ImportProgress returned error: %v", err) |
|||
} |
|||
want := &Import{Status: String("complete")} |
|||
if !reflect.DeepEqual(got, want) { |
|||
t.Errorf("ImportProgress = %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestMigrationService_UpdateImport(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &Import{ |
|||
VCS: String("git"), |
|||
VCSURL: String("url"), |
|||
VCSUsername: String("u"), |
|||
VCSPassword: String("p"), |
|||
} |
|||
|
|||
mux.HandleFunc("/repos/o/r/import", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(Import) |
|||
json.NewDecoder(r.Body).Decode(v) |
|||
|
|||
testMethod(t, r, "PATCH") |
|||
testHeader(t, r, "Accept", mediaTypeImportPreview) |
|||
if !reflect.DeepEqual(v, input) { |
|||
t.Errorf("Request body = %+v, want %+v", v, input) |
|||
} |
|||
|
|||
w.WriteHeader(http.StatusCreated) |
|||
fmt.Fprint(w, `{"status":"importing"}`) |
|||
}) |
|||
|
|||
got, _, err := client.Migrations.UpdateImport("o", "r", input) |
|||
if err != nil { |
|||
t.Errorf("UpdateImport returned error: %v", err) |
|||
} |
|||
want := &Import{Status: String("importing")} |
|||
if !reflect.DeepEqual(got, want) { |
|||
t.Errorf("UpdateImport = %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestMigrationService_CommitAuthors(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/import/authors", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeImportPreview) |
|||
fmt.Fprint(w, `[{"id":1,"name":"a"},{"id":2,"name":"b"}]`) |
|||
}) |
|||
|
|||
got, _, err := client.Migrations.CommitAuthors("o", "r") |
|||
if err != nil { |
|||
t.Errorf("CommitAuthors returned error: %v", err) |
|||
} |
|||
want := []*SourceImportAuthor{ |
|||
{ID: Int(1), Name: String("a")}, |
|||
{ID: Int(2), Name: String("b")}, |
|||
} |
|||
if !reflect.DeepEqual(got, want) { |
|||
t.Errorf("CommitAuthors = %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestMigrationService_MapCommitAuthor(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &SourceImportAuthor{Name: String("n"), Email: String("e")} |
|||
|
|||
mux.HandleFunc("/repos/o/r/import/authors/1", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(SourceImportAuthor) |
|||
json.NewDecoder(r.Body).Decode(v) |
|||
|
|||
testMethod(t, r, "PATCH") |
|||
testHeader(t, r, "Accept", mediaTypeImportPreview) |
|||
if !reflect.DeepEqual(v, input) { |
|||
t.Errorf("Request body = %+v, want %+v", v, input) |
|||
} |
|||
|
|||
fmt.Fprint(w, `{"id": 1}`) |
|||
}) |
|||
|
|||
got, _, err := client.Migrations.MapCommitAuthor("o", "r", 1, input) |
|||
if err != nil { |
|||
t.Errorf("MapCommitAuthor returned error: %v", err) |
|||
} |
|||
want := &SourceImportAuthor{ID: Int(1)} |
|||
if !reflect.DeepEqual(got, want) { |
|||
t.Errorf("MapCommitAuthor = %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestMigrationService_SetLFSPreference(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &Import{UseLFS: String("opt_in")} |
|||
|
|||
mux.HandleFunc("/repos/o/r/import/lfs", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(Import) |
|||
json.NewDecoder(r.Body).Decode(v) |
|||
|
|||
testMethod(t, r, "PATCH") |
|||
testHeader(t, r, "Accept", mediaTypeImportPreview) |
|||
if !reflect.DeepEqual(v, input) { |
|||
t.Errorf("Request body = %+v, want %+v", v, input) |
|||
} |
|||
|
|||
w.WriteHeader(http.StatusCreated) |
|||
fmt.Fprint(w, `{"status":"importing"}`) |
|||
}) |
|||
|
|||
got, _, err := client.Migrations.SetLFSPreference("o", "r", input) |
|||
if err != nil { |
|||
t.Errorf("SetLFSPreference returned error: %v", err) |
|||
} |
|||
want := &Import{Status: String("importing")} |
|||
if !reflect.DeepEqual(got, want) { |
|||
t.Errorf("SetLFSPreference = %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestMigrationService_LargeFiles(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/import/large_files", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeImportPreview) |
|||
fmt.Fprint(w, `[{"oid":"a"},{"oid":"b"}]`) |
|||
}) |
|||
|
|||
got, _, err := client.Migrations.LargeFiles("o", "r") |
|||
if err != nil { |
|||
t.Errorf("LargeFiles returned error: %v", err) |
|||
} |
|||
want := []*LargeFile{ |
|||
{OID: String("a")}, |
|||
{OID: String("b")}, |
|||
} |
|||
if !reflect.DeepEqual(got, want) { |
|||
t.Errorf("LargeFiles = %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestMigrationService_CancelImport(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/import", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
testHeader(t, r, "Accept", mediaTypeImportPreview) |
|||
w.WriteHeader(http.StatusNoContent) |
|||
}) |
|||
|
|||
_, err := client.Migrations.CancelImport("o", "r") |
|||
if err != nil { |
|||
t.Errorf("CancelImport returned error: %v", err) |
|||
} |
|||
} |
@ -0,0 +1,177 @@ |
|||
// Copyright 2016 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 ( |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"strings" |
|||
"testing" |
|||
) |
|||
|
|||
func TestMigrationService_StartMigration(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/orgs/o/migrations", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "POST") |
|||
testHeader(t, r, "Accept", mediaTypeMigrationsPreview) |
|||
|
|||
w.WriteHeader(http.StatusCreated) |
|||
w.Write(migrationJSON) |
|||
}) |
|||
|
|||
opt := &MigrationOptions{ |
|||
LockRepositories: true, |
|||
ExcludeAttachments: false, |
|||
} |
|||
got, _, err := client.Migrations.StartMigration("o", []string{"r"}, opt) |
|||
if err != nil { |
|||
t.Errorf("StartMigration returned error: %v", err) |
|||
} |
|||
if want := wantMigration; !reflect.DeepEqual(got, want) { |
|||
t.Errorf("StartMigration = %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestMigrationService_ListMigrations(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/orgs/o/migrations", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeMigrationsPreview) |
|||
|
|||
w.WriteHeader(http.StatusOK) |
|||
w.Write([]byte(fmt.Sprintf("[%s]", migrationJSON))) |
|||
}) |
|||
|
|||
got, _, err := client.Migrations.ListMigrations("o") |
|||
if err != nil { |
|||
t.Errorf("ListMigrations returned error: %v", err) |
|||
} |
|||
if want := []*Migration{wantMigration}; !reflect.DeepEqual(got, want) { |
|||
t.Errorf("ListMigrations = %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestMigrationService_MigrationStatus(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/orgs/o/migrations/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeMigrationsPreview) |
|||
|
|||
w.WriteHeader(http.StatusOK) |
|||
w.Write(migrationJSON) |
|||
}) |
|||
|
|||
got, _, err := client.Migrations.MigrationStatus("o", 1) |
|||
if err != nil { |
|||
t.Errorf("MigrationStatus returned error: %v", err) |
|||
} |
|||
if want := wantMigration; !reflect.DeepEqual(got, want) { |
|||
t.Errorf("MigrationStatus = %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestMigrationService_MigrationArchiveURL(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/orgs/o/migrations/1/archive", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeMigrationsPreview) |
|||
|
|||
http.Redirect(w, r, "/yo", http.StatusFound) |
|||
}) |
|||
mux.HandleFunc("/yo", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
|
|||
w.WriteHeader(http.StatusOK) |
|||
w.Write([]byte("0123456789abcdef")) |
|||
}) |
|||
|
|||
got, err := client.Migrations.MigrationArchiveURL("o", 1) |
|||
if err != nil { |
|||
t.Errorf("MigrationStatus returned error: %v", err) |
|||
} |
|||
if want := "/yo"; !strings.HasSuffix(got, want) { |
|||
t.Errorf("MigrationArchiveURL = %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestMigrationService_DeleteMigration(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/orgs/o/migrations/1/archive", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
testHeader(t, r, "Accept", mediaTypeMigrationsPreview) |
|||
|
|||
w.WriteHeader(http.StatusNoContent) |
|||
}) |
|||
|
|||
if _, err := client.Migrations.DeleteMigration("o", 1); err != nil { |
|||
t.Errorf("DeleteMigration returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestMigrationService_UnlockRepo(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/orgs/o/migrations/1/repos/r/lock", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
testHeader(t, r, "Accept", mediaTypeMigrationsPreview) |
|||
|
|||
w.WriteHeader(http.StatusNoContent) |
|||
}) |
|||
|
|||
if _, err := client.Migrations.UnlockRepo("o", 1, "r"); err != nil { |
|||
t.Errorf("UnlockRepo returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
var migrationJSON = []byte(`{ |
|||
"id": 79, |
|||
"guid": "0b989ba4-242f-11e5-81e1-c7b6966d2516", |
|||
"state": "pending", |
|||
"lock_repositories": true, |
|||
"exclude_attachments": false, |
|||
"url": "https://api.github.com/orgs/octo-org/migrations/79", |
|||
"created_at": "2015-07-06T15:33:38-07:00", |
|||
"updated_at": "2015-07-06T15:33:38-07:00", |
|||
"repositories": [ |
|||
{ |
|||
"id": 1296269, |
|||
"name": "Hello-World", |
|||
"full_name": "octocat/Hello-World", |
|||
"description": "This your first repo!" |
|||
} |
|||
] |
|||
}`) |
|||
|
|||
var wantMigration = &Migration{ |
|||
ID: Int(79), |
|||
GUID: String("0b989ba4-242f-11e5-81e1-c7b6966d2516"), |
|||
State: String("pending"), |
|||
LockRepositories: Bool(true), |
|||
ExcludeAttachments: Bool(false), |
|||
URL: String("https://api.github.com/orgs/octo-org/migrations/79"), |
|||
CreatedAt: String("2015-07-06T15:33:38-07:00"), |
|||
UpdatedAt: String("2015-07-06T15:33:38-07:00"), |
|||
Repositories: []*Repository{ |
|||
{ |
|||
ID: Int(1296269), |
|||
Name: String("Hello-World"), |
|||
FullName: String("octocat/Hello-World"), |
|||
Description: String("This your first repo!"), |
|||
}, |
|||
}, |
|||
} |
@ -0,0 +1,197 @@ |
|||
// Copyright 2014 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 ( |
|||
"bytes" |
|||
"fmt" |
|||
"net/url" |
|||
) |
|||
|
|||
// MarkdownOptions specifies optional parameters to the Markdown method.
|
|||
type MarkdownOptions struct { |
|||
// Mode identifies the rendering mode. Possible values are:
|
|||
// markdown - render a document as plain Markdown, just like
|
|||
// README files are rendered.
|
|||
//
|
|||
// gfm - to render a document as user-content, e.g. like user
|
|||
// comments or issues are rendered. In GFM mode, hard line breaks are
|
|||
// always taken into account, and issue and user mentions are linked
|
|||
// accordingly.
|
|||
//
|
|||
// Default is "markdown".
|
|||
Mode string |
|||
|
|||
// Context identifies the repository context. Only taken into account
|
|||
// when rendering as "gfm".
|
|||
Context string |
|||
} |
|||
|
|||
type markdownRequest struct { |
|||
Text *string `json:"text,omitempty"` |
|||
Mode *string `json:"mode,omitempty"` |
|||
Context *string `json:"context,omitempty"` |
|||
} |
|||
|
|||
// Markdown renders an arbitrary Markdown document.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/markdown/
|
|||
func (c *Client) Markdown(text string, opt *MarkdownOptions) (string, *Response, error) { |
|||
request := &markdownRequest{Text: String(text)} |
|||
if opt != nil { |
|||
if opt.Mode != "" { |
|||
request.Mode = String(opt.Mode) |
|||
} |
|||
if opt.Context != "" { |
|||
request.Context = String(opt.Context) |
|||
} |
|||
} |
|||
|
|||
req, err := c.NewRequest("POST", "markdown", request) |
|||
if err != nil { |
|||
return "", nil, err |
|||
} |
|||
|
|||
buf := new(bytes.Buffer) |
|||
resp, err := c.Do(req, buf) |
|||
if err != nil { |
|||
return "", resp, err |
|||
} |
|||
|
|||
return buf.String(), resp, nil |
|||
} |
|||
|
|||
// ListEmojis returns the emojis available to use on GitHub.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/emojis/
|
|||
func (c *Client) ListEmojis() (map[string]string, *Response, error) { |
|||
req, err := c.NewRequest("GET", "emojis", nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
var emoji map[string]string |
|||
resp, err := c.Do(req, &emoji) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return emoji, resp, nil |
|||
} |
|||
|
|||
// APIMeta represents metadata about the GitHub API.
|
|||
type APIMeta struct { |
|||
// An Array of IP addresses in CIDR format specifying the addresses
|
|||
// that incoming service hooks will originate from on GitHub.com.
|
|||
Hooks []string `json:"hooks,omitempty"` |
|||
|
|||
// An Array of IP addresses in CIDR format specifying the Git servers
|
|||
// for GitHub.com.
|
|||
Git []string `json:"git,omitempty"` |
|||
|
|||
// Whether authentication with username and password is supported.
|
|||
// (GitHub Enterprise instances using CAS or OAuth for authentication
|
|||
// will return false. Features like Basic Authentication with a
|
|||
// username and password, sudo mode, and two-factor authentication are
|
|||
// not supported on these servers.)
|
|||
VerifiablePasswordAuthentication *bool `json:"verifiable_password_authentication,omitempty"` |
|||
|
|||
// An array of IP addresses in CIDR format specifying the addresses
|
|||
// which serve GitHub Pages websites.
|
|||
Pages []string `json:"pages,omitempty"` |
|||
} |
|||
|
|||
// APIMeta returns information about GitHub.com, the service. Or, if you access
|
|||
// this endpoint on your organization’s GitHub Enterprise installation, this
|
|||
// endpoint provides information about that installation.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/meta/
|
|||
func (c *Client) APIMeta() (*APIMeta, *Response, error) { |
|||
req, err := c.NewRequest("GET", "meta", nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
meta := new(APIMeta) |
|||
resp, err := c.Do(req, meta) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return meta, resp, nil |
|||
} |
|||
|
|||
// Octocat returns an ASCII art octocat with the specified message in a speech
|
|||
// bubble. If message is empty, a random zen phrase is used.
|
|||
func (c *Client) Octocat(message string) (string, *Response, error) { |
|||
u := "octocat" |
|||
if message != "" { |
|||
u = fmt.Sprintf("%s?s=%s", u, url.QueryEscape(message)) |
|||
} |
|||
|
|||
req, err := c.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return "", nil, err |
|||
} |
|||
|
|||
buf := new(bytes.Buffer) |
|||
resp, err := c.Do(req, buf) |
|||
if err != nil { |
|||
return "", resp, err |
|||
} |
|||
|
|||
return buf.String(), resp, nil |
|||
} |
|||
|
|||
// Zen returns a random line from The Zen of GitHub.
|
|||
//
|
|||
// see also: http://warpspire.com/posts/taste/
|
|||
func (c *Client) Zen() (string, *Response, error) { |
|||
req, err := c.NewRequest("GET", "zen", nil) |
|||
if err != nil { |
|||
return "", nil, err |
|||
} |
|||
|
|||
buf := new(bytes.Buffer) |
|||
resp, err := c.Do(req, buf) |
|||
if err != nil { |
|||
return "", resp, err |
|||
} |
|||
|
|||
return buf.String(), resp, nil |
|||
} |
|||
|
|||
// ServiceHook represents a hook that has configuration settings, a list of
|
|||
// available events, and default events.
|
|||
type ServiceHook struct { |
|||
Name *string `json:"name,omitempty"` |
|||
Events []string `json:"events,omitempty"` |
|||
SupportedEvents []string `json:"supported_events,omitempty"` |
|||
Schema [][]string `json:"schema,omitempty"` |
|||
} |
|||
|
|||
func (s *ServiceHook) String() string { |
|||
return Stringify(s) |
|||
} |
|||
|
|||
// ListServiceHooks lists all of the available service hooks.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/webhooks/#services
|
|||
func (c *Client) ListServiceHooks() ([]*ServiceHook, *Response, error) { |
|||
u := "hooks" |
|||
req, err := c.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
hooks := new([]*ServiceHook) |
|||
resp, err := c.Do(req, hooks) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *hooks, resp, err |
|||
} |
@ -0,0 +1,170 @@ |
|||
// Copyright 2014 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestMarkdown(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &markdownRequest{ |
|||
Text: String("# text #"), |
|||
Mode: String("gfm"), |
|||
Context: String("google/go-github"), |
|||
} |
|||
mux.HandleFunc("/markdown", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(markdownRequest) |
|||
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, `<h1>text</h1>`) |
|||
}) |
|||
|
|||
md, _, err := client.Markdown("# text #", &MarkdownOptions{ |
|||
Mode: "gfm", |
|||
Context: "google/go-github", |
|||
}) |
|||
if err != nil { |
|||
t.Errorf("Markdown returned error: %v", err) |
|||
} |
|||
|
|||
if want := "<h1>text</h1>"; want != md { |
|||
t.Errorf("Markdown returned %+v, want %+v", md, want) |
|||
} |
|||
} |
|||
|
|||
func TestListEmojis(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/emojis", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{"+1": "+1.png"}`) |
|||
}) |
|||
|
|||
emoji, _, err := client.ListEmojis() |
|||
if err != nil { |
|||
t.Errorf("ListEmojis returned error: %v", err) |
|||
} |
|||
|
|||
want := map[string]string{"+1": "+1.png"} |
|||
if !reflect.DeepEqual(want, emoji) { |
|||
t.Errorf("ListEmojis returned %+v, want %+v", emoji, want) |
|||
} |
|||
} |
|||
|
|||
func TestAPIMeta(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/meta", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{"hooks":["h"], "git":["g"], "pages":["p"], "verifiable_password_authentication": true}`) |
|||
}) |
|||
|
|||
meta, _, err := client.APIMeta() |
|||
if err != nil { |
|||
t.Errorf("APIMeta returned error: %v", err) |
|||
} |
|||
|
|||
want := &APIMeta{ |
|||
Hooks: []string{"h"}, |
|||
Git: []string{"g"}, |
|||
Pages: []string{"p"}, |
|||
VerifiablePasswordAuthentication: Bool(true), |
|||
} |
|||
if !reflect.DeepEqual(want, meta) { |
|||
t.Errorf("APIMeta returned %+v, want %+v", meta, want) |
|||
} |
|||
} |
|||
|
|||
func TestOctocat(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := "input" |
|||
output := "sample text" |
|||
|
|||
mux.HandleFunc("/octocat", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{"s": input}) |
|||
w.Header().Set("Content-Type", "application/octocat-stream") |
|||
fmt.Fprint(w, output) |
|||
}) |
|||
|
|||
got, _, err := client.Octocat(input) |
|||
if err != nil { |
|||
t.Errorf("Octocat returned error: %v", err) |
|||
} |
|||
|
|||
if want := output; got != want { |
|||
t.Errorf("Octocat returned %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestZen(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
output := "sample text" |
|||
|
|||
mux.HandleFunc("/zen", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
w.Header().Set("Content-Type", "text/plain;charset=utf-8") |
|||
fmt.Fprint(w, output) |
|||
}) |
|||
|
|||
got, _, err := client.Zen() |
|||
if err != nil { |
|||
t.Errorf("Zen returned error: %v", err) |
|||
} |
|||
|
|||
if want := output; got != want { |
|||
t.Errorf("Zen returned %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_ListServiceHooks(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/hooks", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `[{ |
|||
"name":"n", |
|||
"events":["e"], |
|||
"supported_events":["s"], |
|||
"schema":[ |
|||
["a", "b"] |
|||
] |
|||
}]`) |
|||
}) |
|||
|
|||
hooks, _, err := client.Repositories.ListServiceHooks() |
|||
if err != nil { |
|||
t.Errorf("Repositories.ListHooks returned error: %v", err) |
|||
} |
|||
|
|||
want := []*ServiceHook{{ |
|||
Name: String("n"), |
|||
Events: []string{"e"}, |
|||
SupportedEvents: []string{"s"}, |
|||
Schema: [][]string{{"a", "b"}}, |
|||
}} |
|||
if !reflect.DeepEqual(hooks, want) { |
|||
t.Errorf("Repositories.ListServiceHooks returned %+v, want %+v", hooks, want) |
|||
} |
|||
} |
@ -0,0 +1,170 @@ |
|||
// Copyright 2013 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 ( |
|||
"fmt" |
|||
"time" |
|||
) |
|||
|
|||
// OrganizationsService provides access to the organization related functions
|
|||
// in the GitHub API.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/orgs/
|
|||
type OrganizationsService service |
|||
|
|||
// Organization represents a GitHub organization account.
|
|||
type Organization struct { |
|||
Login *string `json:"login,omitempty"` |
|||
ID *int `json:"id,omitempty"` |
|||
AvatarURL *string `json:"avatar_url,omitempty"` |
|||
HTMLURL *string `json:"html_url,omitempty"` |
|||
Name *string `json:"name,omitempty"` |
|||
Company *string `json:"company,omitempty"` |
|||
Blog *string `json:"blog,omitempty"` |
|||
Location *string `json:"location,omitempty"` |
|||
Email *string `json:"email,omitempty"` |
|||
PublicRepos *int `json:"public_repos,omitempty"` |
|||
PublicGists *int `json:"public_gists,omitempty"` |
|||
Followers *int `json:"followers,omitempty"` |
|||
Following *int `json:"following,omitempty"` |
|||
CreatedAt *time.Time `json:"created_at,omitempty"` |
|||
UpdatedAt *time.Time `json:"updated_at,omitempty"` |
|||
TotalPrivateRepos *int `json:"total_private_repos,omitempty"` |
|||
OwnedPrivateRepos *int `json:"owned_private_repos,omitempty"` |
|||
PrivateGists *int `json:"private_gists,omitempty"` |
|||
DiskUsage *int `json:"disk_usage,omitempty"` |
|||
Collaborators *int `json:"collaborators,omitempty"` |
|||
BillingEmail *string `json:"billing_email,omitempty"` |
|||
Type *string `json:"type,omitempty"` |
|||
Plan *Plan `json:"plan,omitempty"` |
|||
|
|||
// API URLs
|
|||
URL *string `json:"url,omitempty"` |
|||
EventsURL *string `json:"events_url,omitempty"` |
|||
MembersURL *string `json:"members_url,omitempty"` |
|||
PublicMembersURL *string `json:"public_members_url,omitempty"` |
|||
ReposURL *string `json:"repos_url,omitempty"` |
|||
} |
|||
|
|||
func (o Organization) String() string { |
|||
return Stringify(o) |
|||
} |
|||
|
|||
// Plan represents the payment plan for an account. See plans at https://github.com/plans.
|
|||
type Plan struct { |
|||
Name *string `json:"name,omitempty"` |
|||
Space *int `json:"space,omitempty"` |
|||
Collaborators *int `json:"collaborators,omitempty"` |
|||
PrivateRepos *int `json:"private_repos,omitempty"` |
|||
} |
|||
|
|||
func (p Plan) String() string { |
|||
return Stringify(p) |
|||
} |
|||
|
|||
// OrganizationsListOptions specifies the optional parameters to the
|
|||
// OrganizationsService.ListAll method.
|
|||
type OrganizationsListOptions struct { |
|||
// Since filters Organizations by ID.
|
|||
Since int `url:"since,omitempty"` |
|||
|
|||
ListOptions |
|||
} |
|||
|
|||
// ListAll lists all organizations, in the order that they were created on GitHub.
|
|||
//
|
|||
// Note: Pagination is powered exclusively by the since parameter. To continue
|
|||
// listing the next set of organizations, use the ID of the last-returned organization
|
|||
// as the opts.Since parameter for the next call.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/orgs/#list-all-organizations
|
|||
func (s *OrganizationsService) ListAll(opt *OrganizationsListOptions) ([]*Organization, *Response, error) { |
|||
u, err := addOptions("organizations", opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
orgs := []*Organization{} |
|||
resp, err := s.client.Do(req, &orgs) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
return orgs, resp, err |
|||
} |
|||
|
|||
// List the organizations for a user. Passing the empty string will list
|
|||
// organizations for the authenticated user.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/orgs/#list-user-organizations
|
|||
func (s *OrganizationsService) List(user string, opt *ListOptions) ([]*Organization, *Response, error) { |
|||
var u string |
|||
if user != "" { |
|||
u = fmt.Sprintf("users/%v/orgs", user) |
|||
} else { |
|||
u = "user/orgs" |
|||
} |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
orgs := new([]*Organization) |
|||
resp, err := s.client.Do(req, orgs) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *orgs, resp, err |
|||
} |
|||
|
|||
// Get fetches an organization by name.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/orgs/#get-an-organization
|
|||
func (s *OrganizationsService) Get(org string) (*Organization, *Response, error) { |
|||
u := fmt.Sprintf("orgs/%v", org) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
organization := new(Organization) |
|||
resp, err := s.client.Do(req, organization) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return organization, resp, err |
|||
} |
|||
|
|||
// Edit an organization.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/orgs/#edit-an-organization
|
|||
func (s *OrganizationsService) Edit(name string, org *Organization) (*Organization, *Response, error) { |
|||
u := fmt.Sprintf("orgs/%v", name) |
|||
req, err := s.client.NewRequest("PATCH", u, org) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
o := new(Organization) |
|||
resp, err := s.client.Do(req, o) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return o, resp, err |
|||
} |
@ -0,0 +1,104 @@ |
|||
// Copyright 2015 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 "fmt" |
|||
|
|||
// ListHooks lists all Hooks for the specified organization.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#list-hooks
|
|||
func (s *OrganizationsService) ListHooks(org string, opt *ListOptions) ([]*Hook, *Response, error) { |
|||
u := fmt.Sprintf("orgs/%v/hooks", org) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
hooks := new([]*Hook) |
|||
resp, err := s.client.Do(req, hooks) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *hooks, resp, err |
|||
} |
|||
|
|||
// GetHook returns a single specified Hook.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#get-single-hook
|
|||
func (s *OrganizationsService) GetHook(org string, id int) (*Hook, *Response, error) { |
|||
u := fmt.Sprintf("orgs/%v/hooks/%d", org, id) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
hook := new(Hook) |
|||
resp, err := s.client.Do(req, hook) |
|||
return hook, resp, err |
|||
} |
|||
|
|||
// CreateHook creates a Hook for the specified org.
|
|||
// Name and Config are required fields.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#create-a-hook
|
|||
func (s *OrganizationsService) CreateHook(org string, hook *Hook) (*Hook, *Response, error) { |
|||
u := fmt.Sprintf("orgs/%v/hooks", org) |
|||
req, err := s.client.NewRequest("POST", u, hook) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
h := new(Hook) |
|||
resp, err := s.client.Do(req, h) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return h, resp, err |
|||
} |
|||
|
|||
// EditHook updates a specified Hook.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#edit-a-hook
|
|||
func (s *OrganizationsService) EditHook(org string, id int, hook *Hook) (*Hook, *Response, error) { |
|||
u := fmt.Sprintf("orgs/%v/hooks/%d", org, id) |
|||
req, err := s.client.NewRequest("PATCH", u, hook) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
h := new(Hook) |
|||
resp, err := s.client.Do(req, h) |
|||
return h, resp, err |
|||
} |
|||
|
|||
// PingHook triggers a 'ping' event to be sent to the Hook.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#ping-a-hook
|
|||
func (s *OrganizationsService) PingHook(org string, id int) (*Response, error) { |
|||
u := fmt.Sprintf("orgs/%v/hooks/%d/pings", org, id) |
|||
req, err := s.client.NewRequest("POST", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return s.client.Do(req, nil) |
|||
} |
|||
|
|||
// DeleteHook deletes a specified Hook.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#delete-a-hook
|
|||
func (s *OrganizationsService) DeleteHook(org string, id int) (*Response, error) { |
|||
u := fmt.Sprintf("orgs/%v/hooks/%d", org, id) |
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return s.client.Do(req, nil) |
|||
} |
@ -0,0 +1,134 @@ |
|||
// Copyright 2015 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestOrganizationsService_ListHooks(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/orgs/o/hooks", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{"page": "2"}) |
|||
fmt.Fprint(w, `[{"id":1}, {"id":2}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 2} |
|||
|
|||
hooks, _, err := client.Organizations.ListHooks("o", opt) |
|||
if err != nil { |
|||
t.Errorf("Organizations.ListHooks returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Hook{{ID: Int(1)}, {ID: Int(2)}} |
|||
if !reflect.DeepEqual(hooks, want) { |
|||
t.Errorf("Organizations.ListHooks returned %+v, want %+v", hooks, want) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_ListHooks_invalidOrg(t *testing.T) { |
|||
_, _, err := client.Organizations.ListHooks("%", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestOrganizationsService_GetHook(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/orgs/o/hooks/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{"id":1}`) |
|||
}) |
|||
|
|||
hook, _, err := client.Organizations.GetHook("o", 1) |
|||
if err != nil { |
|||
t.Errorf("Organizations.GetHook returned error: %v", err) |
|||
} |
|||
|
|||
want := &Hook{ID: Int(1)} |
|||
if !reflect.DeepEqual(hook, want) { |
|||
t.Errorf("Organizations.GetHook returned %+v, want %+v", hook, want) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_GetHook_invalidOrg(t *testing.T) { |
|||
_, _, err := client.Organizations.GetHook("%", 1) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestOrganizationsService_EditHook(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &Hook{Name: String("t")} |
|||
|
|||
mux.HandleFunc("/orgs/o/hooks/1", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(Hook) |
|||
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}`) |
|||
}) |
|||
|
|||
hook, _, err := client.Organizations.EditHook("o", 1, input) |
|||
if err != nil { |
|||
t.Errorf("Organizations.EditHook returned error: %v", err) |
|||
} |
|||
|
|||
want := &Hook{ID: Int(1)} |
|||
if !reflect.DeepEqual(hook, want) { |
|||
t.Errorf("Organizations.EditHook returned %+v, want %+v", hook, want) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_EditHook_invalidOrg(t *testing.T) { |
|||
_, _, err := client.Organizations.EditHook("%", 1, nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestOrganizationsService_PingHook(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/orgs/o/hooks/1/pings", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "POST") |
|||
}) |
|||
|
|||
_, err := client.Organizations.PingHook("o", 1) |
|||
if err != nil { |
|||
t.Errorf("Organizations.PingHook returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_DeleteHook(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/orgs/o/hooks/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
}) |
|||
|
|||
_, err := client.Organizations.DeleteHook("o", 1) |
|||
if err != nil { |
|||
t.Errorf("Organizations.DeleteHook returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_DeleteHook_invalidOrg(t *testing.T) { |
|||
_, err := client.Organizations.DeleteHook("%", 1) |
|||
testURLParseError(t, err) |
|||
} |
@ -0,0 +1,272 @@ |
|||
// Copyright 2013 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 "fmt" |
|||
|
|||
// Membership represents the status of a user's membership in an organization or team.
|
|||
type Membership struct { |
|||
URL *string `json:"url,omitempty"` |
|||
|
|||
// State is the user's status within the organization or team.
|
|||
// Possible values are: "active", "pending"
|
|||
State *string `json:"state,omitempty"` |
|||
|
|||
// Role identifies the user's role within the organization or team.
|
|||
// Possible values for organization membership:
|
|||
// member - non-owner organization member
|
|||
// admin - organization owner
|
|||
//
|
|||
// Possible values for team membership are:
|
|||
// member - a normal member of the team
|
|||
// maintainer - a team maintainer. Able to add/remove other team
|
|||
// members, promote other team members to team
|
|||
// maintainer, and edit the team’s name and description
|
|||
Role *string `json:"role,omitempty"` |
|||
|
|||
// For organization membership, the API URL of the organization.
|
|||
OrganizationURL *string `json:"organization_url,omitempty"` |
|||
|
|||
// For organization membership, the organization the membership is for.
|
|||
Organization *Organization `json:"organization,omitempty"` |
|||
|
|||
// For organization membership, the user the membership is for.
|
|||
User *User `json:"user,omitempty"` |
|||
} |
|||
|
|||
func (m Membership) String() string { |
|||
return Stringify(m) |
|||
} |
|||
|
|||
// ListMembersOptions specifies optional parameters to the
|
|||
// OrganizationsService.ListMembers method.
|
|||
type ListMembersOptions struct { |
|||
// If true (or if the authenticated user is not an owner of the
|
|||
// organization), list only publicly visible members.
|
|||
PublicOnly bool `url:"-"` |
|||
|
|||
// Filter members returned in the list. Possible values are:
|
|||
// 2fa_disabled, all. Default is "all".
|
|||
Filter string `url:"filter,omitempty"` |
|||
|
|||
// Role filters members returned by their role in the organization.
|
|||
// Possible values are:
|
|||
// all - all members of the organization, regardless of role
|
|||
// admin - organization owners
|
|||
// member - non-organization members
|
|||
//
|
|||
// Default is "all".
|
|||
Role string `url:"role,omitempty"` |
|||
|
|||
ListOptions |
|||
} |
|||
|
|||
// ListMembers lists the members for an organization. If the authenticated
|
|||
// user is an owner of the organization, this will return both concealed and
|
|||
// public members, otherwise it will only return public members.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/orgs/members/#members-list
|
|||
func (s *OrganizationsService) ListMembers(org string, opt *ListMembersOptions) ([]*User, *Response, error) { |
|||
var u string |
|||
if opt != nil && opt.PublicOnly { |
|||
u = fmt.Sprintf("orgs/%v/public_members", org) |
|||
} else { |
|||
u = fmt.Sprintf("orgs/%v/members", org) |
|||
} |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
members := new([]*User) |
|||
resp, err := s.client.Do(req, members) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *members, resp, err |
|||
} |
|||
|
|||
// IsMember checks if a user is a member of an organization.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/orgs/members/#check-membership
|
|||
func (s *OrganizationsService) IsMember(org, user string) (bool, *Response, error) { |
|||
u := fmt.Sprintf("orgs/%v/members/%v", org, user) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return false, nil, err |
|||
} |
|||
|
|||
resp, err := s.client.Do(req, nil) |
|||
member, err := parseBoolResponse(err) |
|||
return member, resp, err |
|||
} |
|||
|
|||
// IsPublicMember checks if a user is a public member of an organization.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/orgs/members/#check-public-membership
|
|||
func (s *OrganizationsService) IsPublicMember(org, user string) (bool, *Response, error) { |
|||
u := fmt.Sprintf("orgs/%v/public_members/%v", org, user) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return false, nil, err |
|||
} |
|||
|
|||
resp, err := s.client.Do(req, nil) |
|||
member, err := parseBoolResponse(err) |
|||
return member, resp, err |
|||
} |
|||
|
|||
// RemoveMember removes a user from all teams of an organization.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/orgs/members/#remove-a-member
|
|||
func (s *OrganizationsService) RemoveMember(org, user string) (*Response, error) { |
|||
u := fmt.Sprintf("orgs/%v/members/%v", org, user) |
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
return s.client.Do(req, nil) |
|||
} |
|||
|
|||
// PublicizeMembership publicizes a user's membership in an organization. (A
|
|||
// user cannot publicize the membership for another user.)
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/orgs/members/#publicize-a-users-membership
|
|||
func (s *OrganizationsService) PublicizeMembership(org, user string) (*Response, error) { |
|||
u := fmt.Sprintf("orgs/%v/public_members/%v", org, user) |
|||
req, err := s.client.NewRequest("PUT", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
return s.client.Do(req, nil) |
|||
} |
|||
|
|||
// ConcealMembership conceals a user's membership in an organization.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/orgs/members/#conceal-a-users-membership
|
|||
func (s *OrganizationsService) ConcealMembership(org, user string) (*Response, error) { |
|||
u := fmt.Sprintf("orgs/%v/public_members/%v", org, user) |
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
return s.client.Do(req, nil) |
|||
} |
|||
|
|||
// ListOrgMembershipsOptions specifies optional parameters to the
|
|||
// OrganizationsService.ListOrgMemberships method.
|
|||
type ListOrgMembershipsOptions struct { |
|||
// Filter memberships to include only those with the specified state.
|
|||
// Possible values are: "active", "pending".
|
|||
State string `url:"state,omitempty"` |
|||
|
|||
ListOptions |
|||
} |
|||
|
|||
// ListOrgMemberships lists the organization memberships for the authenticated user.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/orgs/members/#list-your-organization-memberships
|
|||
func (s *OrganizationsService) ListOrgMemberships(opt *ListOrgMembershipsOptions) ([]*Membership, *Response, error) { |
|||
u := "user/memberships/orgs" |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
var memberships []*Membership |
|||
resp, err := s.client.Do(req, &memberships) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return memberships, resp, err |
|||
} |
|||
|
|||
// GetOrgMembership gets the membership for a user in a specified organization.
|
|||
// Passing an empty string for user will get the membership for the
|
|||
// authenticated user.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/orgs/members/#get-organization-membership
|
|||
// GitHub API docs: https://developer.github.com/v3/orgs/members/#get-your-organization-membership
|
|||
func (s *OrganizationsService) GetOrgMembership(user, org string) (*Membership, *Response, error) { |
|||
var u string |
|||
if user != "" { |
|||
u = fmt.Sprintf("orgs/%v/memberships/%v", org, user) |
|||
} else { |
|||
u = fmt.Sprintf("user/memberships/orgs/%v", org) |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
membership := new(Membership) |
|||
resp, err := s.client.Do(req, membership) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return membership, resp, err |
|||
} |
|||
|
|||
// EditOrgMembership edits the membership for user in specified organization.
|
|||
// Passing an empty string for user will edit the membership for the
|
|||
// authenticated user.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/orgs/members/#add-or-update-organization-membership
|
|||
// GitHub API docs: https://developer.github.com/v3/orgs/members/#edit-your-organization-membership
|
|||
func (s *OrganizationsService) EditOrgMembership(user, org string, membership *Membership) (*Membership, *Response, error) { |
|||
var u, method string |
|||
if user != "" { |
|||
u = fmt.Sprintf("orgs/%v/memberships/%v", org, user) |
|||
method = "PUT" |
|||
} else { |
|||
u = fmt.Sprintf("user/memberships/orgs/%v", org) |
|||
method = "PATCH" |
|||
} |
|||
|
|||
req, err := s.client.NewRequest(method, u, membership) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
m := new(Membership) |
|||
resp, err := s.client.Do(req, m) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return m, resp, err |
|||
} |
|||
|
|||
// RemoveOrgMembership removes user from the specified organization. If the
|
|||
// user has been invited to the organization, this will cancel their invitation.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/orgs/members/#remove-organization-membership
|
|||
func (s *OrganizationsService) RemoveOrgMembership(user, org string) (*Response, error) { |
|||
u := fmt.Sprintf("orgs/%v/memberships/%v", org, user) |
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
return s.client.Do(req, nil) |
|||
} |
@ -0,0 +1,355 @@ |
|||
// Copyright 2013 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestOrganizationsService_ListMembers(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/orgs/o/members", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{ |
|||
"filter": "2fa_disabled", |
|||
"role": "admin", |
|||
"page": "2", |
|||
}) |
|||
fmt.Fprint(w, `[{"id":1}]`) |
|||
}) |
|||
|
|||
opt := &ListMembersOptions{ |
|||
PublicOnly: false, |
|||
Filter: "2fa_disabled", |
|||
Role: "admin", |
|||
ListOptions: ListOptions{Page: 2}, |
|||
} |
|||
members, _, err := client.Organizations.ListMembers("o", opt) |
|||
if err != nil { |
|||
t.Errorf("Organizations.ListMembers returned error: %v", err) |
|||
} |
|||
|
|||
want := []*User{{ID: Int(1)}} |
|||
if !reflect.DeepEqual(members, want) { |
|||
t.Errorf("Organizations.ListMembers returned %+v, want %+v", members, want) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_ListMembers_invalidOrg(t *testing.T) { |
|||
_, _, err := client.Organizations.ListMembers("%", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestOrganizationsService_ListMembers_public(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/orgs/o/public_members", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `[{"id":1}]`) |
|||
}) |
|||
|
|||
opt := &ListMembersOptions{PublicOnly: true} |
|||
members, _, err := client.Organizations.ListMembers("o", opt) |
|||
if err != nil { |
|||
t.Errorf("Organizations.ListMembers returned error: %v", err) |
|||
} |
|||
|
|||
want := []*User{{ID: Int(1)}} |
|||
if !reflect.DeepEqual(members, want) { |
|||
t.Errorf("Organizations.ListMembers returned %+v, want %+v", members, want) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_IsMember(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/orgs/o/members/u", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
w.WriteHeader(http.StatusNoContent) |
|||
}) |
|||
|
|||
member, _, err := client.Organizations.IsMember("o", "u") |
|||
if err != nil { |
|||
t.Errorf("Organizations.IsMember returned error: %v", err) |
|||
} |
|||
if want := true; member != want { |
|||
t.Errorf("Organizations.IsMember returned %+v, want %+v", member, want) |
|||
} |
|||
} |
|||
|
|||
// ensure that a 404 response is interpreted as "false" and not an error
|
|||
func TestOrganizationsService_IsMember_notMember(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/orgs/o/members/u", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
w.WriteHeader(http.StatusNotFound) |
|||
}) |
|||
|
|||
member, _, err := client.Organizations.IsMember("o", "u") |
|||
if err != nil { |
|||
t.Errorf("Organizations.IsMember returned error: %+v", err) |
|||
} |
|||
if want := false; member != want { |
|||
t.Errorf("Organizations.IsMember returned %+v, want %+v", member, want) |
|||
} |
|||
} |
|||
|
|||
// ensure that a 400 response is interpreted as an actual error, and not simply
|
|||
// as "false" like the above case of a 404
|
|||
func TestOrganizationsService_IsMember_error(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/orgs/o/members/u", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
http.Error(w, "BadRequest", http.StatusBadRequest) |
|||
}) |
|||
|
|||
member, _, err := client.Organizations.IsMember("o", "u") |
|||
if err == nil { |
|||
t.Errorf("Expected HTTP 400 response") |
|||
} |
|||
if want := false; member != want { |
|||
t.Errorf("Organizations.IsMember returned %+v, want %+v", member, want) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_IsMember_invalidOrg(t *testing.T) { |
|||
_, _, err := client.Organizations.IsMember("%", "u") |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestOrganizationsService_IsPublicMember(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/orgs/o/public_members/u", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
w.WriteHeader(http.StatusNoContent) |
|||
}) |
|||
|
|||
member, _, err := client.Organizations.IsPublicMember("o", "u") |
|||
if err != nil { |
|||
t.Errorf("Organizations.IsPublicMember returned error: %v", err) |
|||
} |
|||
if want := true; member != want { |
|||
t.Errorf("Organizations.IsPublicMember returned %+v, want %+v", member, want) |
|||
} |
|||
} |
|||
|
|||
// ensure that a 404 response is interpreted as "false" and not an error
|
|||
func TestOrganizationsService_IsPublicMember_notMember(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/orgs/o/public_members/u", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
w.WriteHeader(http.StatusNotFound) |
|||
}) |
|||
|
|||
member, _, err := client.Organizations.IsPublicMember("o", "u") |
|||
if err != nil { |
|||
t.Errorf("Organizations.IsPublicMember returned error: %v", err) |
|||
} |
|||
if want := false; member != want { |
|||
t.Errorf("Organizations.IsPublicMember returned %+v, want %+v", member, want) |
|||
} |
|||
} |
|||
|
|||
// ensure that a 400 response is interpreted as an actual error, and not simply
|
|||
// as "false" like the above case of a 404
|
|||
func TestOrganizationsService_IsPublicMember_error(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/orgs/o/public_members/u", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
http.Error(w, "BadRequest", http.StatusBadRequest) |
|||
}) |
|||
|
|||
member, _, err := client.Organizations.IsPublicMember("o", "u") |
|||
if err == nil { |
|||
t.Errorf("Expected HTTP 400 response") |
|||
} |
|||
if want := false; member != want { |
|||
t.Errorf("Organizations.IsPublicMember returned %+v, want %+v", member, want) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_IsPublicMember_invalidOrg(t *testing.T) { |
|||
_, _, err := client.Organizations.IsPublicMember("%", "u") |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestOrganizationsService_RemoveMember(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/orgs/o/members/u", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
}) |
|||
|
|||
_, err := client.Organizations.RemoveMember("o", "u") |
|||
if err != nil { |
|||
t.Errorf("Organizations.RemoveMember returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_RemoveMember_invalidOrg(t *testing.T) { |
|||
_, err := client.Organizations.RemoveMember("%", "u") |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestOrganizationsService_ListOrgMemberships(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/user/memberships/orgs", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{ |
|||
"state": "active", |
|||
"page": "2", |
|||
}) |
|||
fmt.Fprint(w, `[{"url":"u"}]`) |
|||
}) |
|||
|
|||
opt := &ListOrgMembershipsOptions{ |
|||
State: "active", |
|||
ListOptions: ListOptions{Page: 2}, |
|||
} |
|||
memberships, _, err := client.Organizations.ListOrgMemberships(opt) |
|||
if err != nil { |
|||
t.Errorf("Organizations.ListOrgMemberships returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Membership{{URL: String("u")}} |
|||
if !reflect.DeepEqual(memberships, want) { |
|||
t.Errorf("Organizations.ListOrgMemberships returned %+v, want %+v", memberships, want) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_GetOrgMembership_AuthenticatedUser(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/user/memberships/orgs/o", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{"url":"u"}`) |
|||
}) |
|||
|
|||
membership, _, err := client.Organizations.GetOrgMembership("", "o") |
|||
if err != nil { |
|||
t.Errorf("Organizations.GetOrgMembership returned error: %v", err) |
|||
} |
|||
|
|||
want := &Membership{URL: String("u")} |
|||
if !reflect.DeepEqual(membership, want) { |
|||
t.Errorf("Organizations.GetOrgMembership returned %+v, want %+v", membership, want) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_GetOrgMembership_SpecifiedUser(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/orgs/o/memberships/u", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{"url":"u"}`) |
|||
}) |
|||
|
|||
membership, _, err := client.Organizations.GetOrgMembership("u", "o") |
|||
if err != nil { |
|||
t.Errorf("Organizations.GetOrgMembership returned error: %v", err) |
|||
} |
|||
|
|||
want := &Membership{URL: String("u")} |
|||
if !reflect.DeepEqual(membership, want) { |
|||
t.Errorf("Organizations.GetOrgMembership returned %+v, want %+v", membership, want) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_EditOrgMembership_AuthenticatedUser(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &Membership{State: String("active")} |
|||
|
|||
mux.HandleFunc("/user/memberships/orgs/o", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(Membership) |
|||
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, `{"url":"u"}`) |
|||
}) |
|||
|
|||
membership, _, err := client.Organizations.EditOrgMembership("", "o", input) |
|||
if err != nil { |
|||
t.Errorf("Organizations.EditOrgMembership returned error: %v", err) |
|||
} |
|||
|
|||
want := &Membership{URL: String("u")} |
|||
if !reflect.DeepEqual(membership, want) { |
|||
t.Errorf("Organizations.EditOrgMembership returned %+v, want %+v", membership, want) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_EditOrgMembership_SpecifiedUser(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &Membership{State: String("active")} |
|||
|
|||
mux.HandleFunc("/orgs/o/memberships/u", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(Membership) |
|||
json.NewDecoder(r.Body).Decode(v) |
|||
|
|||
testMethod(t, r, "PUT") |
|||
if !reflect.DeepEqual(v, input) { |
|||
t.Errorf("Request body = %+v, want %+v", v, input) |
|||
} |
|||
|
|||
fmt.Fprint(w, `{"url":"u"}`) |
|||
}) |
|||
|
|||
membership, _, err := client.Organizations.EditOrgMembership("u", "o", input) |
|||
if err != nil { |
|||
t.Errorf("Organizations.EditOrgMembership returned error: %v", err) |
|||
} |
|||
|
|||
want := &Membership{URL: String("u")} |
|||
if !reflect.DeepEqual(membership, want) { |
|||
t.Errorf("Organizations.EditOrgMembership returned %+v, want %+v", membership, want) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_RemoveOrgMembership(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/orgs/o/memberships/u", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
w.WriteHeader(http.StatusNoContent) |
|||
}) |
|||
|
|||
_, err := client.Organizations.RemoveOrgMembership("u", "o") |
|||
if err != nil { |
|||
t.Errorf("Organizations.RemoveOrgMembership returned error: %v", err) |
|||
} |
|||
} |
@ -0,0 +1,379 @@ |
|||
// Copyright 2013 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 "fmt" |
|||
|
|||
// Team represents a team within a GitHub organization. Teams are used to
|
|||
// manage access to an organization's repositories.
|
|||
type Team struct { |
|||
ID *int `json:"id,omitempty"` |
|||
Name *string `json:"name,omitempty"` |
|||
Description *string `json:"description,omitempty"` |
|||
URL *string `json:"url,omitempty"` |
|||
Slug *string `json:"slug,omitempty"` |
|||
|
|||
// Permission is deprecated when creating or editing a team in an org
|
|||
// using the new GitHub permission model. It no longer identifies the
|
|||
// permission a team has on its repos, but only specifies the default
|
|||
// permission a repo is initially added with. Avoid confusion by
|
|||
// specifying a permission value when calling AddTeamRepo.
|
|||
Permission *string `json:"permission,omitempty"` |
|||
|
|||
// Privacy identifies the level of privacy this team should have.
|
|||
// Possible values are:
|
|||
// secret - only visible to organization owners and members of this team
|
|||
// closed - visible to all members of this organization
|
|||
// Default is "secret".
|
|||
Privacy *string `json:"privacy,omitempty"` |
|||
|
|||
MembersCount *int `json:"members_count,omitempty"` |
|||
ReposCount *int `json:"repos_count,omitempty"` |
|||
Organization *Organization `json:"organization,omitempty"` |
|||
MembersURL *string `json:"members_url,omitempty"` |
|||
RepositoriesURL *string `json:"repositories_url,omitempty"` |
|||
} |
|||
|
|||
func (t Team) String() string { |
|||
return Stringify(t) |
|||
} |
|||
|
|||
// ListTeams lists all of the teams for an organization.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/orgs/teams/#list-teams
|
|||
func (s *OrganizationsService) ListTeams(org string, opt *ListOptions) ([]*Team, *Response, error) { |
|||
u := fmt.Sprintf("orgs/%v/teams", org) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
teams := new([]*Team) |
|||
resp, err := s.client.Do(req, teams) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *teams, resp, err |
|||
} |
|||
|
|||
// GetTeam fetches a team by ID.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/orgs/teams/#get-team
|
|||
func (s *OrganizationsService) GetTeam(team int) (*Team, *Response, error) { |
|||
u := fmt.Sprintf("teams/%v", team) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
t := new(Team) |
|||
resp, err := s.client.Do(req, t) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return t, resp, err |
|||
} |
|||
|
|||
// CreateTeam creates a new team within an organization.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/orgs/teams/#create-team
|
|||
func (s *OrganizationsService) CreateTeam(org string, team *Team) (*Team, *Response, error) { |
|||
u := fmt.Sprintf("orgs/%v/teams", org) |
|||
req, err := s.client.NewRequest("POST", u, team) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
t := new(Team) |
|||
resp, err := s.client.Do(req, t) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return t, resp, err |
|||
} |
|||
|
|||
// EditTeam edits a team.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/orgs/teams/#edit-team
|
|||
func (s *OrganizationsService) EditTeam(id int, team *Team) (*Team, *Response, error) { |
|||
u := fmt.Sprintf("teams/%v", id) |
|||
req, err := s.client.NewRequest("PATCH", u, team) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
t := new(Team) |
|||
resp, err := s.client.Do(req, t) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return t, resp, err |
|||
} |
|||
|
|||
// DeleteTeam deletes a team.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/orgs/teams/#delete-team
|
|||
func (s *OrganizationsService) DeleteTeam(team int) (*Response, error) { |
|||
u := fmt.Sprintf("teams/%v", team) |
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
return s.client.Do(req, nil) |
|||
} |
|||
|
|||
// OrganizationListTeamMembersOptions specifies the optional parameters to the
|
|||
// OrganizationsService.ListTeamMembers method.
|
|||
type OrganizationListTeamMembersOptions struct { |
|||
// Role filters members returned by their role in the team. Possible
|
|||
// values are "all", "member", "maintainer". Default is "all".
|
|||
Role string `url:"role,omitempty"` |
|||
|
|||
ListOptions |
|||
} |
|||
|
|||
// ListTeamMembers lists all of the users who are members of the specified
|
|||
// team.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/orgs/teams/#list-team-members
|
|||
func (s *OrganizationsService) ListTeamMembers(team int, opt *OrganizationListTeamMembersOptions) ([]*User, *Response, error) { |
|||
u := fmt.Sprintf("teams/%v/members", team) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
members := new([]*User) |
|||
resp, err := s.client.Do(req, members) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *members, resp, err |
|||
} |
|||
|
|||
// IsTeamMember checks if a user is a member of the specified team.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/orgs/teams/#get-team-member
|
|||
func (s *OrganizationsService) IsTeamMember(team int, user string) (bool, *Response, error) { |
|||
u := fmt.Sprintf("teams/%v/members/%v", team, user) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return false, nil, err |
|||
} |
|||
|
|||
resp, err := s.client.Do(req, nil) |
|||
member, err := parseBoolResponse(err) |
|||
return member, resp, err |
|||
} |
|||
|
|||
// ListTeamRepos lists the repositories that the specified team has access to.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/orgs/teams/#list-team-repos
|
|||
func (s *OrganizationsService) ListTeamRepos(team int, opt *ListOptions) ([]*Repository, *Response, error) { |
|||
u := fmt.Sprintf("teams/%v/repos", team) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
repos := new([]*Repository) |
|||
resp, err := s.client.Do(req, repos) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *repos, resp, err |
|||
} |
|||
|
|||
// IsTeamRepo checks if a team manages the specified repository. If the
|
|||
// repository is managed by team, a Repository is returned which includes the
|
|||
// permissions team has for that repo.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/orgs/teams/#check-if-a-team-manages-a-repository
|
|||
func (s *OrganizationsService) IsTeamRepo(team int, owner string, repo string) (*Repository, *Response, error) { |
|||
u := fmt.Sprintf("teams/%v/repos/%v/%v", team, owner, repo) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req.Header.Set("Accept", mediaTypeOrgPermissionRepo) |
|||
|
|||
repository := new(Repository) |
|||
resp, err := s.client.Do(req, repository) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return repository, resp, err |
|||
} |
|||
|
|||
// OrganizationAddTeamRepoOptions specifies the optional parameters to the
|
|||
// OrganizationsService.AddTeamRepo method.
|
|||
type OrganizationAddTeamRepoOptions struct { |
|||
// Permission specifies the permission to grant the team on this repository.
|
|||
// Possible values are:
|
|||
// pull - team members can pull, but not push to or administer this repository
|
|||
// push - team members can pull and push, but not administer this repository
|
|||
// admin - team members can pull, push and administer this repository
|
|||
//
|
|||
// If not specified, the team's permission attribute will be used.
|
|||
Permission string `json:"permission,omitempty"` |
|||
} |
|||
|
|||
// AddTeamRepo adds a repository to be managed by the specified team. The
|
|||
// specified repository must be owned by the organization to which the team
|
|||
// belongs, or a direct fork of a repository owned by the organization.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/orgs/teams/#add-team-repo
|
|||
func (s *OrganizationsService) AddTeamRepo(team int, owner string, repo string, opt *OrganizationAddTeamRepoOptions) (*Response, error) { |
|||
u := fmt.Sprintf("teams/%v/repos/%v/%v", team, owner, repo) |
|||
req, err := s.client.NewRequest("PUT", u, opt) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
return s.client.Do(req, nil) |
|||
} |
|||
|
|||
// RemoveTeamRepo removes a repository from being managed by the specified
|
|||
// team. Note that this does not delete the repository, it just removes it
|
|||
// from the team.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/orgs/teams/#remove-team-repo
|
|||
func (s *OrganizationsService) RemoveTeamRepo(team int, owner string, repo string) (*Response, error) { |
|||
u := fmt.Sprintf("teams/%v/repos/%v/%v", team, owner, repo) |
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
return s.client.Do(req, nil) |
|||
} |
|||
|
|||
// ListUserTeams lists a user's teams
|
|||
// GitHub API docs: https://developer.github.com/v3/orgs/teams/#list-user-teams
|
|||
func (s *OrganizationsService) ListUserTeams(opt *ListOptions) ([]*Team, *Response, error) { |
|||
u := "user/teams" |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
teams := new([]*Team) |
|||
resp, err := s.client.Do(req, teams) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *teams, resp, err |
|||
} |
|||
|
|||
// GetTeamMembership returns the membership status for a user in a team.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/orgs/teams/#get-team-membership
|
|||
func (s *OrganizationsService) GetTeamMembership(team int, user string) (*Membership, *Response, error) { |
|||
u := fmt.Sprintf("teams/%v/memberships/%v", team, user) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
t := new(Membership) |
|||
resp, err := s.client.Do(req, t) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return t, resp, err |
|||
} |
|||
|
|||
// OrganizationAddTeamMembershipOptions does stuff specifies the optional
|
|||
// parameters to the OrganizationsService.AddTeamMembership method.
|
|||
type OrganizationAddTeamMembershipOptions struct { |
|||
// Role specifies the role the user should have in the team. Possible
|
|||
// values are:
|
|||
// member - a normal member of the team
|
|||
// maintainer - a team maintainer. Able to add/remove other team
|
|||
// members, promote other team members to team
|
|||
// maintainer, and edit the team’s name and description
|
|||
//
|
|||
// Default value is "member".
|
|||
Role string `json:"role,omitempty"` |
|||
} |
|||
|
|||
// AddTeamMembership adds or invites a user to a team.
|
|||
//
|
|||
// In order to add a membership between a user and a team, the authenticated
|
|||
// user must have 'admin' permissions to the team or be an owner of the
|
|||
// organization that the team is associated with.
|
|||
//
|
|||
// If the user is already a part of the team's organization (meaning they're on
|
|||
// at least one other team in the organization), this endpoint will add the
|
|||
// user to the team.
|
|||
//
|
|||
// If the user is completely unaffiliated with the team's organization (meaning
|
|||
// they're on none of the organization's teams), this endpoint will send an
|
|||
// invitation to the user via email. This newly-created membership will be in
|
|||
// the "pending" state until the user accepts the invitation, at which point
|
|||
// the membership will transition to the "active" state and the user will be
|
|||
// added as a member of the team.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/orgs/teams/#add-team-membership
|
|||
func (s *OrganizationsService) AddTeamMembership(team int, user string, opt *OrganizationAddTeamMembershipOptions) (*Membership, *Response, error) { |
|||
u := fmt.Sprintf("teams/%v/memberships/%v", team, user) |
|||
req, err := s.client.NewRequest("PUT", u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
t := new(Membership) |
|||
resp, err := s.client.Do(req, t) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return t, resp, err |
|||
} |
|||
|
|||
// RemoveTeamMembership removes a user from a team.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/orgs/teams/#remove-team-membership
|
|||
func (s *OrganizationsService) RemoveTeamMembership(team int, user string) (*Response, error) { |
|||
u := fmt.Sprintf("teams/%v/memberships/%v", team, user) |
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
return s.client.Do(req, nil) |
|||
} |
@ -0,0 +1,501 @@ |
|||
// Copyright 2013 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestOrganizationsService_ListTeams(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/orgs/o/teams", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{"page": "2"}) |
|||
fmt.Fprint(w, `[{"id":1}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 2} |
|||
teams, _, err := client.Organizations.ListTeams("o", opt) |
|||
if err != nil { |
|||
t.Errorf("Organizations.ListTeams returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Team{{ID: Int(1)}} |
|||
if !reflect.DeepEqual(teams, want) { |
|||
t.Errorf("Organizations.ListTeams returned %+v, want %+v", teams, want) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_ListTeams_invalidOrg(t *testing.T) { |
|||
_, _, err := client.Organizations.ListTeams("%", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestOrganizationsService_GetTeam(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/teams/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{"id":1, "name":"n", "description": "d", "url":"u", "slug": "s", "permission":"p"}`) |
|||
}) |
|||
|
|||
team, _, err := client.Organizations.GetTeam(1) |
|||
if err != nil { |
|||
t.Errorf("Organizations.GetTeam returned error: %v", err) |
|||
} |
|||
|
|||
want := &Team{ID: Int(1), Name: String("n"), Description: String("d"), URL: String("u"), Slug: String("s"), Permission: String("p")} |
|||
if !reflect.DeepEqual(team, want) { |
|||
t.Errorf("Organizations.GetTeam returned %+v, want %+v", team, want) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_CreateTeam(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &Team{Name: String("n"), Privacy: String("closed")} |
|||
|
|||
mux.HandleFunc("/orgs/o/teams", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(Team) |
|||
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}`) |
|||
}) |
|||
|
|||
team, _, err := client.Organizations.CreateTeam("o", input) |
|||
if err != nil { |
|||
t.Errorf("Organizations.CreateTeam returned error: %v", err) |
|||
} |
|||
|
|||
want := &Team{ID: Int(1)} |
|||
if !reflect.DeepEqual(team, want) { |
|||
t.Errorf("Organizations.CreateTeam returned %+v, want %+v", team, want) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_CreateTeam_invalidOrg(t *testing.T) { |
|||
_, _, err := client.Organizations.CreateTeam("%", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestOrganizationsService_EditTeam(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &Team{Name: String("n"), Privacy: String("closed")} |
|||
|
|||
mux.HandleFunc("/teams/1", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(Team) |
|||
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}`) |
|||
}) |
|||
|
|||
team, _, err := client.Organizations.EditTeam(1, input) |
|||
if err != nil { |
|||
t.Errorf("Organizations.EditTeam returned error: %v", err) |
|||
} |
|||
|
|||
want := &Team{ID: Int(1)} |
|||
if !reflect.DeepEqual(team, want) { |
|||
t.Errorf("Organizations.EditTeam returned %+v, want %+v", team, want) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_DeleteTeam(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/teams/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
}) |
|||
|
|||
_, err := client.Organizations.DeleteTeam(1) |
|||
if err != nil { |
|||
t.Errorf("Organizations.DeleteTeam returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_ListTeamMembers(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/teams/1/members", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{"role": "member", "page": "2"}) |
|||
fmt.Fprint(w, `[{"id":1}]`) |
|||
}) |
|||
|
|||
opt := &OrganizationListTeamMembersOptions{Role: "member", ListOptions: ListOptions{Page: 2}} |
|||
members, _, err := client.Organizations.ListTeamMembers(1, opt) |
|||
if err != nil { |
|||
t.Errorf("Organizations.ListTeamMembers returned error: %v", err) |
|||
} |
|||
|
|||
want := []*User{{ID: Int(1)}} |
|||
if !reflect.DeepEqual(members, want) { |
|||
t.Errorf("Organizations.ListTeamMembers returned %+v, want %+v", members, want) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_IsTeamMember_true(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/teams/1/members/u", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
}) |
|||
|
|||
member, _, err := client.Organizations.IsTeamMember(1, "u") |
|||
if err != nil { |
|||
t.Errorf("Organizations.IsTeamMember returned error: %v", err) |
|||
} |
|||
if want := true; member != want { |
|||
t.Errorf("Organizations.IsTeamMember returned %+v, want %+v", member, want) |
|||
} |
|||
} |
|||
|
|||
// ensure that a 404 response is interpreted as "false" and not an error
|
|||
func TestOrganizationsService_IsTeamMember_false(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/teams/1/members/u", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
w.WriteHeader(http.StatusNotFound) |
|||
}) |
|||
|
|||
member, _, err := client.Organizations.IsTeamMember(1, "u") |
|||
if err != nil { |
|||
t.Errorf("Organizations.IsTeamMember returned error: %+v", err) |
|||
} |
|||
if want := false; member != want { |
|||
t.Errorf("Organizations.IsTeamMember returned %+v, want %+v", member, want) |
|||
} |
|||
} |
|||
|
|||
// ensure that a 400 response is interpreted as an actual error, and not simply
|
|||
// as "false" like the above case of a 404
|
|||
func TestOrganizationsService_IsTeamMember_error(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/teams/1/members/u", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
http.Error(w, "BadRequest", http.StatusBadRequest) |
|||
}) |
|||
|
|||
member, _, err := client.Organizations.IsTeamMember(1, "u") |
|||
if err == nil { |
|||
t.Errorf("Expected HTTP 400 response") |
|||
} |
|||
if want := false; member != want { |
|||
t.Errorf("Organizations.IsTeamMember returned %+v, want %+v", member, want) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_IsTeamMember_invalidUser(t *testing.T) { |
|||
_, _, err := client.Organizations.IsTeamMember(1, "%") |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestOrganizationsService_PublicizeMembership(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/orgs/o/public_members/u", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "PUT") |
|||
w.WriteHeader(http.StatusNoContent) |
|||
}) |
|||
|
|||
_, err := client.Organizations.PublicizeMembership("o", "u") |
|||
if err != nil { |
|||
t.Errorf("Organizations.PublicizeMembership returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_PublicizeMembership_invalidOrg(t *testing.T) { |
|||
_, err := client.Organizations.PublicizeMembership("%", "u") |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestOrganizationsService_ConcealMembership(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/orgs/o/public_members/u", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
w.WriteHeader(http.StatusNoContent) |
|||
}) |
|||
|
|||
_, err := client.Organizations.ConcealMembership("o", "u") |
|||
if err != nil { |
|||
t.Errorf("Organizations.ConcealMembership returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_ConcealMembership_invalidOrg(t *testing.T) { |
|||
_, err := client.Organizations.ConcealMembership("%", "u") |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestOrganizationsService_ListTeamRepos(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/teams/1/repos", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{"page": "2"}) |
|||
fmt.Fprint(w, `[{"id":1}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 2} |
|||
members, _, err := client.Organizations.ListTeamRepos(1, opt) |
|||
if err != nil { |
|||
t.Errorf("Organizations.ListTeamRepos returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Repository{{ID: Int(1)}} |
|||
if !reflect.DeepEqual(members, want) { |
|||
t.Errorf("Organizations.ListTeamRepos returned %+v, want %+v", members, want) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_IsTeamRepo_true(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/teams/1/repos/o/r", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeOrgPermissionRepo) |
|||
fmt.Fprint(w, `{"id":1}`) |
|||
}) |
|||
|
|||
repo, _, err := client.Organizations.IsTeamRepo(1, "o", "r") |
|||
if err != nil { |
|||
t.Errorf("Organizations.IsTeamRepo returned error: %v", err) |
|||
} |
|||
|
|||
want := &Repository{ID: Int(1)} |
|||
if !reflect.DeepEqual(repo, want) { |
|||
t.Errorf("Organizations.IsTeamRepo returned %+v, want %+v", repo, want) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_IsTeamRepo_false(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/teams/1/repos/o/r", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
w.WriteHeader(http.StatusNotFound) |
|||
}) |
|||
|
|||
repo, resp, err := client.Organizations.IsTeamRepo(1, "o", "r") |
|||
if err == nil { |
|||
t.Errorf("Expected HTTP 404 response") |
|||
} |
|||
if got, want := resp.Response.StatusCode, http.StatusNotFound; got != want { |
|||
t.Errorf("Organizations.IsTeamRepo returned status %d, want %d", got, want) |
|||
} |
|||
if repo != nil { |
|||
t.Errorf("Organizations.IsTeamRepo returned %+v, want nil", repo) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_IsTeamRepo_error(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/teams/1/repos/o/r", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
http.Error(w, "BadRequest", http.StatusBadRequest) |
|||
}) |
|||
|
|||
repo, resp, err := client.Organizations.IsTeamRepo(1, "o", "r") |
|||
if err == nil { |
|||
t.Errorf("Expected HTTP 400 response") |
|||
} |
|||
if got, want := resp.Response.StatusCode, http.StatusBadRequest; got != want { |
|||
t.Errorf("Organizations.IsTeamRepo returned status %d, want %d", got, want) |
|||
} |
|||
if repo != nil { |
|||
t.Errorf("Organizations.IsTeamRepo returned %+v, want nil", repo) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_IsTeamRepo_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Organizations.IsTeamRepo(1, "%", "r") |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestOrganizationsService_AddTeamRepo(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
opt := &OrganizationAddTeamRepoOptions{Permission: "admin"} |
|||
|
|||
mux.HandleFunc("/teams/1/repos/o/r", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(OrganizationAddTeamRepoOptions) |
|||
json.NewDecoder(r.Body).Decode(v) |
|||
|
|||
testMethod(t, r, "PUT") |
|||
if !reflect.DeepEqual(v, opt) { |
|||
t.Errorf("Request body = %+v, want %+v", v, opt) |
|||
} |
|||
|
|||
w.WriteHeader(http.StatusNoContent) |
|||
}) |
|||
|
|||
_, err := client.Organizations.AddTeamRepo(1, "o", "r", opt) |
|||
if err != nil { |
|||
t.Errorf("Organizations.AddTeamRepo returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_AddTeamRepo_noAccess(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/teams/1/repos/o/r", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "PUT") |
|||
w.WriteHeader(StatusUnprocessableEntity) |
|||
}) |
|||
|
|||
_, err := client.Organizations.AddTeamRepo(1, "o", "r", nil) |
|||
if err == nil { |
|||
t.Errorf("Expcted error to be returned") |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_AddTeamRepo_invalidOwner(t *testing.T) { |
|||
_, err := client.Organizations.AddTeamRepo(1, "%", "r", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestOrganizationsService_RemoveTeamRepo(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/teams/1/repos/o/r", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
w.WriteHeader(http.StatusNoContent) |
|||
}) |
|||
|
|||
_, err := client.Organizations.RemoveTeamRepo(1, "o", "r") |
|||
if err != nil { |
|||
t.Errorf("Organizations.RemoveTeamRepo returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_RemoveTeamRepo_invalidOwner(t *testing.T) { |
|||
_, err := client.Organizations.RemoveTeamRepo(1, "%", "r") |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestOrganizationsService_GetTeamMembership(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/teams/1/memberships/u", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{"url":"u", "state":"active"}`) |
|||
}) |
|||
|
|||
membership, _, err := client.Organizations.GetTeamMembership(1, "u") |
|||
if err != nil { |
|||
t.Errorf("Organizations.GetTeamMembership returned error: %v", err) |
|||
} |
|||
|
|||
want := &Membership{URL: String("u"), State: String("active")} |
|||
if !reflect.DeepEqual(membership, want) { |
|||
t.Errorf("Organizations.GetTeamMembership returned %+v, want %+v", membership, want) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_AddTeamMembership(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
opt := &OrganizationAddTeamMembershipOptions{Role: "maintainer"} |
|||
|
|||
mux.HandleFunc("/teams/1/memberships/u", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(OrganizationAddTeamMembershipOptions) |
|||
json.NewDecoder(r.Body).Decode(v) |
|||
|
|||
testMethod(t, r, "PUT") |
|||
if !reflect.DeepEqual(v, opt) { |
|||
t.Errorf("Request body = %+v, want %+v", v, opt) |
|||
} |
|||
|
|||
fmt.Fprint(w, `{"url":"u", "state":"pending"}`) |
|||
}) |
|||
|
|||
membership, _, err := client.Organizations.AddTeamMembership(1, "u", opt) |
|||
if err != nil { |
|||
t.Errorf("Organizations.AddTeamMembership returned error: %v", err) |
|||
} |
|||
|
|||
want := &Membership{URL: String("u"), State: String("pending")} |
|||
if !reflect.DeepEqual(membership, want) { |
|||
t.Errorf("Organizations.AddTeamMembership returned %+v, want %+v", membership, want) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_RemoveTeamMembership(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/teams/1/memberships/u", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
w.WriteHeader(http.StatusNoContent) |
|||
}) |
|||
|
|||
_, err := client.Organizations.RemoveTeamMembership(1, "u") |
|||
if err != nil { |
|||
t.Errorf("Organizations.RemoveTeamMembership returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_ListUserTeams(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/user/teams", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{"page": "1"}) |
|||
fmt.Fprint(w, `[{"id":1}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 1} |
|||
teams, _, err := client.Organizations.ListUserTeams(opt) |
|||
if err != nil { |
|||
t.Errorf("Organizations.ListUserTeams returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Team{{ID: Int(1)}} |
|||
if !reflect.DeepEqual(teams, want) { |
|||
t.Errorf("Organizations.ListUserTeams returned %+v, want %+v", teams, want) |
|||
} |
|||
} |
@ -0,0 +1,143 @@ |
|||
// Copyright 2013 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestOrganizationsService_ListAll(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
since := 1342004 |
|||
mux.HandleFunc("/organizations", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{"since": "1342004"}) |
|||
fmt.Fprint(w, `[{"id":4314092}]`) |
|||
}) |
|||
|
|||
opt := &OrganizationsListOptions{Since: since} |
|||
orgs, _, err := client.Organizations.ListAll(opt) |
|||
if err != nil { |
|||
t.Errorf("Organizations.ListAll returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Organization{{ID: Int(4314092)}} |
|||
if !reflect.DeepEqual(orgs, want) { |
|||
t.Errorf("Organizations.ListAll returned %+v, want %+v", orgs, want) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_List_authenticatedUser(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/user/orgs", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `[{"id":1},{"id":2}]`) |
|||
}) |
|||
|
|||
orgs, _, err := client.Organizations.List("", nil) |
|||
if err != nil { |
|||
t.Errorf("Organizations.List returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Organization{{ID: Int(1)}, {ID: Int(2)}} |
|||
if !reflect.DeepEqual(orgs, want) { |
|||
t.Errorf("Organizations.List returned %+v, want %+v", orgs, want) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_List_specifiedUser(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/users/u/orgs", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{"page": "2"}) |
|||
fmt.Fprint(w, `[{"id":1},{"id":2}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 2} |
|||
orgs, _, err := client.Organizations.List("u", opt) |
|||
if err != nil { |
|||
t.Errorf("Organizations.List returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Organization{{ID: Int(1)}, {ID: Int(2)}} |
|||
if !reflect.DeepEqual(orgs, want) { |
|||
t.Errorf("Organizations.List returned %+v, want %+v", orgs, want) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_List_invalidUser(t *testing.T) { |
|||
_, _, err := client.Organizations.List("%", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestOrganizationsService_Get(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/orgs/o", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{"id":1, "login":"l", "url":"u", "avatar_url": "a", "location":"l"}`) |
|||
}) |
|||
|
|||
org, _, err := client.Organizations.Get("o") |
|||
if err != nil { |
|||
t.Errorf("Organizations.Get returned error: %v", err) |
|||
} |
|||
|
|||
want := &Organization{ID: Int(1), Login: String("l"), URL: String("u"), AvatarURL: String("a"), Location: String("l")} |
|||
if !reflect.DeepEqual(org, want) { |
|||
t.Errorf("Organizations.Get returned %+v, want %+v", org, want) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_Get_invalidOrg(t *testing.T) { |
|||
_, _, err := client.Organizations.Get("%") |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestOrganizationsService_Edit(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &Organization{Login: String("l")} |
|||
|
|||
mux.HandleFunc("/orgs/o", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(Organization) |
|||
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}`) |
|||
}) |
|||
|
|||
org, _, err := client.Organizations.Edit("o", input) |
|||
if err != nil { |
|||
t.Errorf("Organizations.Edit returned error: %v", err) |
|||
} |
|||
|
|||
want := &Organization{ID: Int(1)} |
|||
if !reflect.DeepEqual(org, want) { |
|||
t.Errorf("Organizations.Edit returned %+v, want %+v", org, want) |
|||
} |
|||
} |
|||
|
|||
func TestOrganizationsService_Edit_invalidOrg(t *testing.T) { |
|||
_, _, err := client.Organizations.Edit("%", nil) |
|||
testURLParseError(t, err) |
|||
} |
@ -0,0 +1,285 @@ |
|||
// Copyright 2013 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 ( |
|||
"fmt" |
|||
"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 service |
|||
|
|||
// PullRequest represents a GitHub pull request on a repository.
|
|||
type PullRequest struct { |
|||
ID *int `json:"id,omitempty"` |
|||
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"` |
|||
URL *string `json:"url,omitempty"` |
|||
HTMLURL *string `json:"html_url,omitempty"` |
|||
IssueURL *string `json:"issue_url,omitempty"` |
|||
StatusesURL *string `json:"statuses_url,omitempty"` |
|||
DiffURL *string `json:"diff_url,omitempty"` |
|||
PatchURL *string `json:"patch_url,omitempty"` |
|||
Assignee *User `json:"assignee,omitempty"` // probably only in webhooks
|
|||
|
|||
Head *PullRequestBranch `json:"head,omitempty"` |
|||
Base *PullRequestBranch `json:"base,omitempty"` |
|||
} |
|||
|
|||
func (p PullRequest) String() string { |
|||
return Stringify(p) |
|||
} |
|||
|
|||
// PullRequestBranch represents a base or head branch in a GitHub pull request.
|
|||
type PullRequestBranch struct { |
|||
Label *string `json:"label,omitempty"` |
|||
Ref *string `json:"ref,omitempty"` |
|||
SHA *string `json:"sha,omitempty"` |
|||
Repo *Repository `json:"repo,omitempty"` |
|||
User *User `json:"user,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 `url:"state,omitempty"` |
|||
|
|||
// Head filters pull requests by head user and branch name in the format of:
|
|||
// "user:ref-name".
|
|||
Head string `url:"head,omitempty"` |
|||
|
|||
// Base filters pull requests by base branch name.
|
|||
Base string `url:"base,omitempty"` |
|||
|
|||
// Sort specifies how to sort pull requests. Possible values are: created,
|
|||
// updated, popularity, long-running. Default is "created".
|
|||
Sort string `url:"sort,omitempty"` |
|||
|
|||
// Direction in which to sort pull requests. Possible values are: asc, desc.
|
|||
// If Sort is "created" or not specified, Default is "desc", otherwise Default
|
|||
// is "asc"
|
|||
Direction string `url:"direction,omitempty"` |
|||
|
|||
ListOptions |
|||
} |
|||
|
|||
// 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, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/pulls", owner, repo) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
pulls := new([]*PullRequest) |
|||
resp, err := s.client.Do(req, pulls) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *pulls, resp, 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, *Response, 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, nil, err |
|||
} |
|||
|
|||
pull := new(PullRequest) |
|||
resp, err := s.client.Do(req, pull) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return pull, resp, err |
|||
} |
|||
|
|||
// NewPullRequest represents a new pull request to be created.
|
|||
type NewPullRequest struct { |
|||
Title *string `json:"title,omitempty"` |
|||
Head *string `json:"head,omitempty"` |
|||
Base *string `json:"base,omitempty"` |
|||
Body *string `json:"body,omitempty"` |
|||
Issue *int `json:"issue,omitempty"` |
|||
} |
|||
|
|||
// 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 *NewPullRequest) (*PullRequest, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/pulls", owner, repo) |
|||
req, err := s.client.NewRequest("POST", u, pull) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
p := new(PullRequest) |
|||
resp, err := s.client.Do(req, p) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return p, resp, 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, *Response, 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, nil, err |
|||
} |
|||
|
|||
p := new(PullRequest) |
|||
resp, err := s.client.Do(req, p) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return p, resp, err |
|||
} |
|||
|
|||
// ListCommits lists the commits in a pull request.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/pulls/#list-commits-on-a-pull-request
|
|||
func (s *PullRequestsService) ListCommits(owner string, repo string, number int, opt *ListOptions) ([]*RepositoryCommit, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/pulls/%d/commits", owner, repo, number) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
commits := new([]*RepositoryCommit) |
|||
resp, err := s.client.Do(req, commits) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *commits, resp, err |
|||
} |
|||
|
|||
// ListFiles lists the files in a pull request.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/pulls/#list-pull-requests-files
|
|||
func (s *PullRequestsService) ListFiles(owner string, repo string, number int, opt *ListOptions) ([]*CommitFile, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/pulls/%d/files", owner, repo, number) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
commitFiles := new([]*CommitFile) |
|||
resp, err := s.client.Do(req, commitFiles) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *commitFiles, resp, err |
|||
} |
|||
|
|||
// IsMerged checks if a pull request has been merged.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/pulls/#get-if-a-pull-request-has-been-merged
|
|||
func (s *PullRequestsService) IsMerged(owner string, repo string, number int) (bool, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/pulls/%d/merge", owner, repo, number) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return false, nil, err |
|||
} |
|||
|
|||
resp, err := s.client.Do(req, nil) |
|||
merged, err := parseBoolResponse(err) |
|||
return merged, resp, err |
|||
} |
|||
|
|||
// PullRequestMergeResult represents the result of merging a pull request.
|
|||
type PullRequestMergeResult struct { |
|||
SHA *string `json:"sha,omitempty"` |
|||
Merged *bool `json:"merged,omitempty"` |
|||
Message *string `json:"message,omitempty"` |
|||
} |
|||
|
|||
// PullRequestOptions lets you define how a pull request will be merged.
|
|||
type PullRequestOptions struct { |
|||
Squash bool |
|||
} |
|||
|
|||
type pullRequestMergeRequest struct { |
|||
CommitMessage *string `json:"commit_message"` |
|||
Squash *bool `json:"squash,omitempty"` |
|||
} |
|||
|
|||
// Merge a pull request (Merge Button™).
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/pulls/#merge-a-pull-request-merge-buttontrade
|
|||
func (s *PullRequestsService) Merge(owner string, repo string, number int, commitMessage string, options *PullRequestOptions) (*PullRequestMergeResult, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/pulls/%d/merge", owner, repo, number) |
|||
|
|||
pullRequestBody := &pullRequestMergeRequest{CommitMessage: &commitMessage} |
|||
if options != nil { |
|||
pullRequestBody.Squash = &options.Squash |
|||
} |
|||
req, err := s.client.NewRequest("PUT", u, pullRequestBody) |
|||
|
|||
// TODO: This header will be unnecessary when the API is no longer in preview.
|
|||
req.Header.Set("Accept", mediaTypeSquashPreview) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
mergeResult := new(PullRequestMergeResult) |
|||
resp, err := s.client.Do(req, mergeResult) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return mergeResult, resp, err |
|||
} |
@ -0,0 +1,156 @@ |
|||
// Copyright 2013 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 ( |
|||
"fmt" |
|||
"time" |
|||
) |
|||
|
|||
// PullRequestComment represents a comment left on a pull request.
|
|||
type PullRequestComment struct { |
|||
ID *int `json:"id,omitempty"` |
|||
InReplyTo *int `json:"in_reply_to,omitempty"` |
|||
Body *string `json:"body,omitempty"` |
|||
Path *string `json:"path,omitempty"` |
|||
DiffHunk *string `json:"diff_hunk,omitempty"` |
|||
Position *int `json:"position,omitempty"` |
|||
OriginalPosition *int `json:"original_position,omitempty"` |
|||
CommitID *string `json:"commit_id,omitempty"` |
|||
OriginalCommitID *string `json:"original_commit_id,omitempty"` |
|||
User *User `json:"user,omitempty"` |
|||
Reactions *Reactions `json:"reactions,omitempty"` |
|||
CreatedAt *time.Time `json:"created_at,omitempty"` |
|||
UpdatedAt *time.Time `json:"updated_at,omitempty"` |
|||
URL *string `json:"url,omitempty"` |
|||
HTMLURL *string `json:"html_url,omitempty"` |
|||
PullRequestURL *string `json:"pull_request_url,omitempty"` |
|||
} |
|||
|
|||
func (p PullRequestComment) String() string { |
|||
return Stringify(p) |
|||
} |
|||
|
|||
// 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 `url:"sort,omitempty"` |
|||
|
|||
// Direction in which to sort comments. Possible values are: asc, desc.
|
|||
Direction string `url:"direction,omitempty"` |
|||
|
|||
// Since filters comments by time.
|
|||
Since time.Time `url:"since,omitempty"` |
|||
|
|||
ListOptions |
|||
} |
|||
|
|||
// 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, *Response, 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) |
|||
} |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeReactionsPreview) |
|||
|
|||
comments := new([]*PullRequestComment) |
|||
resp, err := s.client.Do(req, comments) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *comments, resp, 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, *Response, 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, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeReactionsPreview) |
|||
|
|||
comment := new(PullRequestComment) |
|||
resp, err := s.client.Do(req, comment) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return comment, resp, err |
|||
} |
|||
|
|||
// CreateComment creates a new comment on the specified pull request.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/pulls/comments/#create-a-comment
|
|||
func (s *PullRequestsService) CreateComment(owner string, repo string, number int, comment *PullRequestComment) (*PullRequestComment, *Response, 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, nil, err |
|||
} |
|||
|
|||
c := new(PullRequestComment) |
|||
resp, err := s.client.Do(req, c) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return c, resp, 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, *Response, 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, nil, err |
|||
} |
|||
|
|||
c := new(PullRequestComment) |
|||
resp, err := s.client.Do(req, c) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return c, resp, 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) (*Response, 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 nil, err |
|||
} |
|||
return s.client.Do(req, nil) |
|||
} |
@ -0,0 +1,187 @@ |
|||
// Copyright 2013 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
"time" |
|||
) |
|||
|
|||
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") |
|||
testHeader(t, r, "Accept", mediaTypeReactionsPreview) |
|||
testFormValues(t, r, values{ |
|||
"sort": "updated", |
|||
"direction": "desc", |
|||
"since": "2002-02-10T15:30:00Z", |
|||
"page": "2", |
|||
}) |
|||
fmt.Fprint(w, `[{"id":1}]`) |
|||
}) |
|||
|
|||
opt := &PullRequestListCommentsOptions{ |
|||
Sort: "updated", |
|||
Direction: "desc", |
|||
Since: time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC), |
|||
ListOptions: ListOptions{Page: 2}, |
|||
} |
|||
pulls, _, err := client.PullRequests.ListComments("o", "r", 0, opt) |
|||
if err != nil { |
|||
t.Errorf("PullRequests.ListComments returned error: %v", err) |
|||
} |
|||
|
|||
want := []*PullRequestComment{{ID: Int(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") |
|||
testHeader(t, r, "Accept", mediaTypeReactionsPreview) |
|||
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{{ID: Int(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") |
|||
testHeader(t, r, "Accept", mediaTypeReactionsPreview) |
|||
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: Int(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: String("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: Int(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: String("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: Int(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) |
|||
} |
@ -0,0 +1,363 @@ |
|||
// Copyright 2013 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
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", |
|||
"sort": "created", |
|||
"direction": "desc", |
|||
"page": "2", |
|||
}) |
|||
fmt.Fprint(w, `[{"number":1}]`) |
|||
}) |
|||
|
|||
opt := &PullRequestListOptions{"closed", "h", "b", "created", "desc", ListOptions{Page: 2}} |
|||
pulls, _, err := client.PullRequests.List("o", "r", opt) |
|||
if err != nil { |
|||
t.Errorf("PullRequests.List returned error: %v", err) |
|||
} |
|||
|
|||
want := []*PullRequest{{Number: Int(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: Int(1)} |
|||
if !reflect.DeepEqual(pull, want) { |
|||
t.Errorf("PullRequests.Get returned %+v, want %+v", pull, want) |
|||
} |
|||
} |
|||
|
|||
func TestPullRequestsService_Get_headAndBase(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,"head":{"ref":"r2","repo":{"id":2}},"base":{"ref":"r1","repo":{"id":1}}}`) |
|||
}) |
|||
|
|||
pull, _, err := client.PullRequests.Get("o", "r", 1) |
|||
if err != nil { |
|||
t.Errorf("PullRequests.Get returned error: %v", err) |
|||
} |
|||
|
|||
want := &PullRequest{ |
|||
Number: Int(1), |
|||
Head: &PullRequestBranch{ |
|||
Ref: String("r2"), |
|||
Repo: &Repository{ID: Int(2)}, |
|||
}, |
|||
Base: &PullRequestBranch{ |
|||
Ref: String("r1"), |
|||
Repo: &Repository{ID: Int(1)}, |
|||
}, |
|||
} |
|||
if !reflect.DeepEqual(pull, want) { |
|||
t.Errorf("PullRequests.Get returned %+v, want %+v", pull, want) |
|||
} |
|||
} |
|||
|
|||
func TestPullRequestService_Get_DiffURLAndPatchURL(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, |
|||
"diff_url": "https://github.com/octocat/Hello-World/pull/1347.diff", |
|||
"patch_url": "https://github.com/octocat/Hello-World/pull/1347.patch"}`) |
|||
}) |
|||
|
|||
pull, _, err := client.PullRequests.Get("o", "r", 1) |
|||
if err != nil { |
|||
t.Errorf("PullRequests.Get returned error: %v", err) |
|||
} |
|||
|
|||
want := &PullRequest{Number: Int(1), DiffURL: String("https://github.com/octocat/Hello-World/pull/1347.diff"), PatchURL: String("https://github.com/octocat/Hello-World/pull/1347.patch")} |
|||
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 := &NewPullRequest{Title: String("t")} |
|||
|
|||
mux.HandleFunc("/repos/o/r/pulls", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(NewPullRequest) |
|||
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: Int(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: String("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: Int(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_ListCommits(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/pulls/1/commits", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{"page": "2"}) |
|||
fmt.Fprint(w, ` |
|||
[ |
|||
{ |
|||
"sha": "3", |
|||
"parents": [ |
|||
{ |
|||
"sha": "2" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"sha": "2", |
|||
"parents": [ |
|||
{ |
|||
"sha": "1" |
|||
} |
|||
] |
|||
} |
|||
]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 2} |
|||
commits, _, err := client.PullRequests.ListCommits("o", "r", 1, opt) |
|||
if err != nil { |
|||
t.Errorf("PullRequests.ListCommits returned error: %v", err) |
|||
} |
|||
|
|||
want := []*RepositoryCommit{ |
|||
{ |
|||
SHA: String("3"), |
|||
Parents: []Commit{ |
|||
{ |
|||
SHA: String("2"), |
|||
}, |
|||
}, |
|||
}, |
|||
{ |
|||
SHA: String("2"), |
|||
Parents: []Commit{ |
|||
{ |
|||
SHA: String("1"), |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
if !reflect.DeepEqual(commits, want) { |
|||
t.Errorf("PullRequests.ListCommits returned %+v, want %+v", commits, want) |
|||
} |
|||
} |
|||
|
|||
func TestPullRequestsService_ListFiles(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/pulls/1/files", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{"page": "2"}) |
|||
fmt.Fprint(w, ` |
|||
[ |
|||
{ |
|||
"sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e", |
|||
"filename": "file1.txt", |
|||
"status": "added", |
|||
"additions": 103, |
|||
"deletions": 21, |
|||
"changes": 124, |
|||
"patch": "@@ -132,7 +132,7 @@ module Test @@ -1000,7 +1000,7 @@ module Test" |
|||
}, |
|||
{ |
|||
"sha": "f61aebed695e2e4193db5e6dcb09b5b57875f334", |
|||
"filename": "file2.txt", |
|||
"status": "modified", |
|||
"additions": 5, |
|||
"deletions": 3, |
|||
"changes": 103, |
|||
"patch": "@@ -132,7 +132,7 @@ module Test @@ -1000,7 +1000,7 @@ module Test" |
|||
} |
|||
]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 2} |
|||
commitFiles, _, err := client.PullRequests.ListFiles("o", "r", 1, opt) |
|||
if err != nil { |
|||
t.Errorf("PullRequests.ListFiles returned error: %v", err) |
|||
} |
|||
|
|||
want := []*CommitFile{ |
|||
{ |
|||
SHA: String("6dcb09b5b57875f334f61aebed695e2e4193db5e"), |
|||
Filename: String("file1.txt"), |
|||
Additions: Int(103), |
|||
Deletions: Int(21), |
|||
Changes: Int(124), |
|||
Status: String("added"), |
|||
Patch: String("@@ -132,7 +132,7 @@ module Test @@ -1000,7 +1000,7 @@ module Test"), |
|||
}, |
|||
{ |
|||
SHA: String("f61aebed695e2e4193db5e6dcb09b5b57875f334"), |
|||
Filename: String("file2.txt"), |
|||
Additions: Int(5), |
|||
Deletions: Int(3), |
|||
Changes: Int(103), |
|||
Status: String("modified"), |
|||
Patch: String("@@ -132,7 +132,7 @@ module Test @@ -1000,7 +1000,7 @@ module Test"), |
|||
}, |
|||
} |
|||
|
|||
if !reflect.DeepEqual(commitFiles, want) { |
|||
t.Errorf("PullRequests.ListFiles returned %+v, want %+v", commitFiles, want) |
|||
} |
|||
} |
|||
|
|||
func TestPullRequestsService_IsMerged(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/pulls/1/merge", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
w.WriteHeader(http.StatusNoContent) |
|||
}) |
|||
|
|||
isMerged, _, err := client.PullRequests.IsMerged("o", "r", 1) |
|||
if err != nil { |
|||
t.Errorf("PullRequests.IsMerged returned error: %v", err) |
|||
} |
|||
|
|||
want := true |
|||
if !reflect.DeepEqual(isMerged, want) { |
|||
t.Errorf("PullRequests.IsMerged returned %+v, want %+v", isMerged, want) |
|||
} |
|||
} |
|||
|
|||
func TestPullRequestsService_Merge(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/pulls/1/merge", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "PUT") |
|||
testHeader(t, r, "Accept", mediaTypeSquashPreview) |
|||
fmt.Fprint(w, ` |
|||
{ |
|||
"sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e", |
|||
"merged": true, |
|||
"message": "Pull Request successfully merged" |
|||
}`) |
|||
}) |
|||
|
|||
options := &PullRequestOptions{Squash: true} |
|||
merge, _, err := client.PullRequests.Merge("o", "r", 1, "merging pull request", options) |
|||
if err != nil { |
|||
t.Errorf("PullRequests.Merge returned error: %v", err) |
|||
} |
|||
|
|||
want := &PullRequestMergeResult{ |
|||
SHA: String("6dcb09b5b57875f334f61aebed695e2e4193db5e"), |
|||
Merged: Bool(true), |
|||
Message: String("Pull Request successfully merged"), |
|||
} |
|||
if !reflect.DeepEqual(merge, want) { |
|||
t.Errorf("PullRequests.Merge returned %+v, want %+v", merge, want) |
|||
} |
|||
} |
@ -0,0 +1,270 @@ |
|||
// Copyright 2016 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 "fmt" |
|||
|
|||
// ReactionsService provides access to the reactions-related functions in the
|
|||
// GitHub API.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/reactions/
|
|||
type ReactionsService service |
|||
|
|||
// Reaction represents a GitHub reaction.
|
|||
type Reaction struct { |
|||
// ID is the Reaction ID.
|
|||
ID *int `json:"id,omitempty"` |
|||
User *User `json:"user,omitempty"` |
|||
// Content is the type of reaction.
|
|||
// Possible values are:
|
|||
// "+1", "-1", "laugh", "confused", "heart", "hooray".
|
|||
Content *string `json:"content,omitempty"` |
|||
} |
|||
|
|||
// Reactions represents a summary of GitHub reactions.
|
|||
type Reactions struct { |
|||
TotalCount *int `json:"total_count,omitempty"` |
|||
PlusOne *int `json:"+1,omitempty"` |
|||
MinusOne *int `json:"-1,omitempty"` |
|||
Laugh *int `json:"laugh,omitempty"` |
|||
Confused *int `json:"confused,omitempty"` |
|||
Heart *int `json:"heart,omitempty"` |
|||
Hooray *int `json:"hooray,omitempty"` |
|||
URL *string `json:"url,omitempty"` |
|||
} |
|||
|
|||
func (r Reaction) String() string { |
|||
return Stringify(r) |
|||
} |
|||
|
|||
// ListCommentReactions lists the reactions for a commit comment.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-a-commit-comment
|
|||
func (s *ReactionsService) ListCommentReactions(owner, repo string, id int, opt *ListOptions) ([]*Reaction, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/comments/%v/reactions", owner, repo, id) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeReactionsPreview) |
|||
|
|||
var m []*Reaction |
|||
resp, err := s.client.Do(req, &m) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return m, resp, nil |
|||
} |
|||
|
|||
// CreateCommentReaction creates a reaction for a commit comment.
|
|||
// Note that if you have already created a reaction of type content, the
|
|||
// previously created reaction will be returned with Status: 200 OK.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-a-commit-comment
|
|||
func (s ReactionsService) CreateCommentReaction(owner, repo string, id int, content string) (*Reaction, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/comments/%v/reactions", owner, repo, id) |
|||
|
|||
body := &Reaction{Content: String(content)} |
|||
req, err := s.client.NewRequest("POST", u, body) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeReactionsPreview) |
|||
|
|||
m := &Reaction{} |
|||
resp, err := s.client.Do(req, m) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return m, resp, nil |
|||
} |
|||
|
|||
// ListIssueReactions lists the reactions for an issue.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-an-issue
|
|||
func (s *ReactionsService) ListIssueReactions(owner, repo string, number int, opt *ListOptions) ([]*Reaction, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/issues/%v/reactions", owner, repo, number) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeReactionsPreview) |
|||
|
|||
var m []*Reaction |
|||
resp, err := s.client.Do(req, &m) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return m, resp, nil |
|||
} |
|||
|
|||
// CreateIssueReaction creates a reaction for an issue.
|
|||
// Note that if you have already created a reaction of type content, the
|
|||
// previously created reaction will be returned with Status: 200 OK.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-an-issue
|
|||
func (s ReactionsService) CreateIssueReaction(owner, repo string, number int, content string) (*Reaction, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/issues/%v/reactions", owner, repo, number) |
|||
|
|||
body := &Reaction{Content: String(content)} |
|||
req, err := s.client.NewRequest("POST", u, body) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeReactionsPreview) |
|||
|
|||
m := &Reaction{} |
|||
resp, err := s.client.Do(req, m) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return m, resp, nil |
|||
} |
|||
|
|||
// ListIssueCommentReactions lists the reactions for an issue comment.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-an-issue-comment
|
|||
func (s *ReactionsService) ListIssueCommentReactions(owner, repo string, id int, opt *ListOptions) ([]*Reaction, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/issues/comments/%v/reactions", owner, repo, id) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeReactionsPreview) |
|||
|
|||
var m []*Reaction |
|||
resp, err := s.client.Do(req, &m) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return m, resp, nil |
|||
} |
|||
|
|||
// CreateIssueCommentReaction creates a reaction for an issue comment.
|
|||
// Note that if you have already created a reaction of type content, the
|
|||
// previously created reaction will be returned with Status: 200 OK.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-an-issue-comment
|
|||
func (s ReactionsService) CreateIssueCommentReaction(owner, repo string, id int, content string) (*Reaction, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/issues/comments/%v/reactions", owner, repo, id) |
|||
|
|||
body := &Reaction{Content: String(content)} |
|||
req, err := s.client.NewRequest("POST", u, body) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeReactionsPreview) |
|||
|
|||
m := &Reaction{} |
|||
resp, err := s.client.Do(req, m) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return m, resp, nil |
|||
} |
|||
|
|||
// ListPullRequestCommentReactions lists the reactions for a pull request review comment.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-an-issue-comment
|
|||
func (s *ReactionsService) ListPullRequestCommentReactions(owner, repo string, id int, opt *ListOptions) ([]*Reaction, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/pulls/comments/%v/reactions", owner, repo, id) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeReactionsPreview) |
|||
|
|||
var m []*Reaction |
|||
resp, err := s.client.Do(req, &m) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return m, resp, nil |
|||
} |
|||
|
|||
// CreatePullRequestCommentReaction creates a reaction for a pull request review comment.
|
|||
// Note that if you have already created a reaction of type content, the
|
|||
// previously created reaction will be returned with Status: 200 OK.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-an-issue-comment
|
|||
func (s ReactionsService) CreatePullRequestCommentReaction(owner, repo string, id int, content string) (*Reaction, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/pulls/comments/%v/reactions", owner, repo, id) |
|||
|
|||
body := &Reaction{Content: String(content)} |
|||
req, err := s.client.NewRequest("POST", u, body) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeReactionsPreview) |
|||
|
|||
m := &Reaction{} |
|||
resp, err := s.client.Do(req, m) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return m, resp, nil |
|||
} |
|||
|
|||
// DeleteReaction deletes a reaction.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/reaction/reactions/#delete-a-reaction-archive
|
|||
func (s *ReactionsService) DeleteReaction(id int) (*Response, error) { |
|||
u := fmt.Sprintf("reactions/%v", id) |
|||
|
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeReactionsPreview) |
|||
|
|||
return s.client.Do(req, nil) |
|||
} |
@ -0,0 +1,200 @@ |
|||
// Copyright 2016 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 ( |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestReactionsService_ListCommentReactions(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/comments/1/reactions", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeReactionsPreview) |
|||
|
|||
w.WriteHeader(http.StatusOK) |
|||
w.Write([]byte(`[{"id":1,"user":{"login":"l","id":2},"content":"+1"}]`)) |
|||
}) |
|||
|
|||
got, _, err := client.Reactions.ListCommentReactions("o", "r", 1, nil) |
|||
if err != nil { |
|||
t.Errorf("ListCommentReactions returned error: %v", err) |
|||
} |
|||
if want := []*Reaction{{ID: Int(1), User: &User{Login: String("l"), ID: Int(2)}, Content: String("+1")}}; !reflect.DeepEqual(got, want) { |
|||
t.Errorf("ListCommentReactions = %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestReactionsService_CreateCommentReaction(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/comments/1/reactions", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "POST") |
|||
testHeader(t, r, "Accept", mediaTypeReactionsPreview) |
|||
|
|||
w.WriteHeader(http.StatusCreated) |
|||
w.Write([]byte(`{"id":1,"user":{"login":"l","id":2},"content":"+1"}`)) |
|||
}) |
|||
|
|||
got, _, err := client.Reactions.CreateCommentReaction("o", "r", 1, "+1") |
|||
if err != nil { |
|||
t.Errorf("CreateCommentReaction returned error: %v", err) |
|||
} |
|||
want := &Reaction{ID: Int(1), User: &User{Login: String("l"), ID: Int(2)}, Content: String("+1")} |
|||
if !reflect.DeepEqual(got, want) { |
|||
t.Errorf("CreateCommentReaction = %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestReactionsService_ListIssueReactions(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/issues/1/reactions", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeReactionsPreview) |
|||
|
|||
w.WriteHeader(http.StatusOK) |
|||
w.Write([]byte(`[{"id":1,"user":{"login":"l","id":2},"content":"+1"}]`)) |
|||
}) |
|||
|
|||
got, _, err := client.Reactions.ListIssueReactions("o", "r", 1, nil) |
|||
if err != nil { |
|||
t.Errorf("ListIssueReactions returned error: %v", err) |
|||
} |
|||
if want := []*Reaction{{ID: Int(1), User: &User{Login: String("l"), ID: Int(2)}, Content: String("+1")}}; !reflect.DeepEqual(got, want) { |
|||
t.Errorf("ListIssueReactions = %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestReactionsService_CreateIssueReaction(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/issues/1/reactions", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "POST") |
|||
testHeader(t, r, "Accept", mediaTypeReactionsPreview) |
|||
|
|||
w.WriteHeader(http.StatusCreated) |
|||
w.Write([]byte(`{"id":1,"user":{"login":"l","id":2},"content":"+1"}`)) |
|||
}) |
|||
|
|||
got, _, err := client.Reactions.CreateIssueReaction("o", "r", 1, "+1") |
|||
if err != nil { |
|||
t.Errorf("CreateIssueReaction returned error: %v", err) |
|||
} |
|||
want := &Reaction{ID: Int(1), User: &User{Login: String("l"), ID: Int(2)}, Content: String("+1")} |
|||
if !reflect.DeepEqual(got, want) { |
|||
t.Errorf("CreateIssueReaction = %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestReactionsService_ListIssueCommentReactions(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/issues/comments/1/reactions", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeReactionsPreview) |
|||
|
|||
w.WriteHeader(http.StatusOK) |
|||
w.Write([]byte(`[{"id":1,"user":{"login":"l","id":2},"content":"+1"}]`)) |
|||
}) |
|||
|
|||
got, _, err := client.Reactions.ListIssueCommentReactions("o", "r", 1, nil) |
|||
if err != nil { |
|||
t.Errorf("ListIssueCommentReactions returned error: %v", err) |
|||
} |
|||
if want := []*Reaction{{ID: Int(1), User: &User{Login: String("l"), ID: Int(2)}, Content: String("+1")}}; !reflect.DeepEqual(got, want) { |
|||
t.Errorf("ListIssueCommentReactions = %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestReactionsService_CreateIssueCommentReaction(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/issues/comments/1/reactions", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "POST") |
|||
testHeader(t, r, "Accept", mediaTypeReactionsPreview) |
|||
|
|||
w.WriteHeader(http.StatusCreated) |
|||
w.Write([]byte(`{"id":1,"user":{"login":"l","id":2},"content":"+1"}`)) |
|||
}) |
|||
|
|||
got, _, err := client.Reactions.CreateIssueCommentReaction("o", "r", 1, "+1") |
|||
if err != nil { |
|||
t.Errorf("CreateIssueCommentReaction returned error: %v", err) |
|||
} |
|||
want := &Reaction{ID: Int(1), User: &User{Login: String("l"), ID: Int(2)}, Content: String("+1")} |
|||
if !reflect.DeepEqual(got, want) { |
|||
t.Errorf("CreateIssueCommentReaction = %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestReactionsService_ListPullRequestCommentReactions(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/pulls/comments/1/reactions", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeReactionsPreview) |
|||
|
|||
w.WriteHeader(http.StatusOK) |
|||
w.Write([]byte(`[{"id":1,"user":{"login":"l","id":2},"content":"+1"}]`)) |
|||
}) |
|||
|
|||
got, _, err := client.Reactions.ListPullRequestCommentReactions("o", "r", 1, nil) |
|||
if err != nil { |
|||
t.Errorf("ListPullRequestCommentReactions returned error: %v", err) |
|||
} |
|||
if want := []*Reaction{{ID: Int(1), User: &User{Login: String("l"), ID: Int(2)}, Content: String("+1")}}; !reflect.DeepEqual(got, want) { |
|||
t.Errorf("ListPullRequestCommentReactions = %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestReactionsService_CreatePullRequestCommentReaction(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/pulls/comments/1/reactions", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "POST") |
|||
testHeader(t, r, "Accept", mediaTypeReactionsPreview) |
|||
|
|||
w.WriteHeader(http.StatusCreated) |
|||
w.Write([]byte(`{"id":1,"user":{"login":"l","id":2},"content":"+1"}`)) |
|||
}) |
|||
|
|||
got, _, err := client.Reactions.CreatePullRequestCommentReaction("o", "r", 1, "+1") |
|||
if err != nil { |
|||
t.Errorf("CreatePullRequestCommentReaction returned error: %v", err) |
|||
} |
|||
want := &Reaction{ID: Int(1), User: &User{Login: String("l"), ID: Int(2)}, Content: String("+1")} |
|||
if !reflect.DeepEqual(got, want) { |
|||
t.Errorf("CreatePullRequestCommentReaction = %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestReactionsService_DeleteReaction(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/reactions/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
testHeader(t, r, "Accept", mediaTypeReactionsPreview) |
|||
|
|||
w.WriteHeader(http.StatusNoContent) |
|||
}) |
|||
|
|||
if _, err := client.Reactions.DeleteReaction(1); err != nil { |
|||
t.Errorf("DeleteReaction returned error: %v", err) |
|||
} |
|||
} |
@ -0,0 +1,597 @@ |
|||
// Copyright 2013 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 "fmt" |
|||
|
|||
// RepositoriesService handles communication with the repository related
|
|||
// methods of the GitHub API.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/
|
|||
type RepositoriesService service |
|||
|
|||
// Repository represents a GitHub repository.
|
|||
type Repository struct { |
|||
ID *int `json:"id,omitempty"` |
|||
Owner *User `json:"owner,omitempty"` |
|||
Name *string `json:"name,omitempty"` |
|||
FullName *string `json:"full_name,omitempty"` |
|||
Description *string `json:"description,omitempty"` |
|||
Homepage *string `json:"homepage,omitempty"` |
|||
DefaultBranch *string `json:"default_branch,omitempty"` |
|||
MasterBranch *string `json:"master_branch,omitempty"` |
|||
CreatedAt *Timestamp `json:"created_at,omitempty"` |
|||
PushedAt *Timestamp `json:"pushed_at,omitempty"` |
|||
UpdatedAt *Timestamp `json:"updated_at,omitempty"` |
|||
HTMLURL *string `json:"html_url,omitempty"` |
|||
CloneURL *string `json:"clone_url,omitempty"` |
|||
GitURL *string `json:"git_url,omitempty"` |
|||
MirrorURL *string `json:"mirror_url,omitempty"` |
|||
SSHURL *string `json:"ssh_url,omitempty"` |
|||
SVNURL *string `json:"svn_url,omitempty"` |
|||
Language *string `json:"language,omitempty"` |
|||
Fork *bool `json:"fork"` |
|||
ForksCount *int `json:"forks_count,omitempty"` |
|||
NetworkCount *int `json:"network_count,omitempty"` |
|||
OpenIssuesCount *int `json:"open_issues_count,omitempty"` |
|||
StargazersCount *int `json:"stargazers_count,omitempty"` |
|||
SubscribersCount *int `json:"subscribers_count,omitempty"` |
|||
WatchersCount *int `json:"watchers_count,omitempty"` |
|||
Size *int `json:"size,omitempty"` |
|||
AutoInit *bool `json:"auto_init,omitempty"` |
|||
Parent *Repository `json:"parent,omitempty"` |
|||
Source *Repository `json:"source,omitempty"` |
|||
Organization *Organization `json:"organization,omitempty"` |
|||
Permissions *map[string]bool `json:"permissions,omitempty"` |
|||
|
|||
// Only provided when using RepositoriesService.Get while in preview
|
|||
License *License `json:"license,omitempty"` |
|||
|
|||
// Additional mutable fields when creating and editing a repository
|
|||
Private *bool `json:"private"` |
|||
HasIssues *bool `json:"has_issues"` |
|||
HasWiki *bool `json:"has_wiki"` |
|||
HasDownloads *bool `json:"has_downloads"` |
|||
// Creating an organization repository. Required for non-owners.
|
|||
TeamID *int `json:"team_id"` |
|||
|
|||
// API URLs
|
|||
URL *string `json:"url,omitempty"` |
|||
ArchiveURL *string `json:"archive_url,omitempty"` |
|||
AssigneesURL *string `json:"assignees_url,omitempty"` |
|||
BlobsURL *string `json:"blobs_url,omitempty"` |
|||
BranchesURL *string `json:"branches_url,omitempty"` |
|||
CollaboratorsURL *string `json:"collaborators_url,omitempty"` |
|||
CommentsURL *string `json:"comments_url,omitempty"` |
|||
CommitsURL *string `json:"commits_url,omitempty"` |
|||
CompareURL *string `json:"compare_url,omitempty"` |
|||
ContentsURL *string `json:"contents_url,omitempty"` |
|||
ContributorsURL *string `json:"contributors_url,omitempty"` |
|||
DownloadsURL *string `json:"downloads_url,omitempty"` |
|||
EventsURL *string `json:"events_url,omitempty"` |
|||
ForksURL *string `json:"forks_url,omitempty"` |
|||
GitCommitsURL *string `json:"git_commits_url,omitempty"` |
|||
GitRefsURL *string `json:"git_refs_url,omitempty"` |
|||
GitTagsURL *string `json:"git_tags_url,omitempty"` |
|||
HooksURL *string `json:"hooks_url,omitempty"` |
|||
IssueCommentURL *string `json:"issue_comment_url,omitempty"` |
|||
IssueEventsURL *string `json:"issue_events_url,omitempty"` |
|||
IssuesURL *string `json:"issues_url,omitempty"` |
|||
KeysURL *string `json:"keys_url,omitempty"` |
|||
LabelsURL *string `json:"labels_url,omitempty"` |
|||
LanguagesURL *string `json:"languages_url,omitempty"` |
|||
MergesURL *string `json:"merges_url,omitempty"` |
|||
MilestonesURL *string `json:"milestones_url,omitempty"` |
|||
NotificationsURL *string `json:"notifications_url,omitempty"` |
|||
PullsURL *string `json:"pulls_url,omitempty"` |
|||
ReleasesURL *string `json:"releases_url,omitempty"` |
|||
StargazersURL *string `json:"stargazers_url,omitempty"` |
|||
StatusesURL *string `json:"statuses_url,omitempty"` |
|||
SubscribersURL *string `json:"subscribers_url,omitempty"` |
|||
SubscriptionURL *string `json:"subscription_url,omitempty"` |
|||
TagsURL *string `json:"tags_url,omitempty"` |
|||
TreesURL *string `json:"trees_url,omitempty"` |
|||
TeamsURL *string `json:"teams_url,omitempty"` |
|||
|
|||
// 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"` |
|||
} |
|||
|
|||
func (r Repository) String() string { |
|||
return Stringify(r) |
|||
} |
|||
|
|||
// RepositoryListOptions specifies the optional parameters to the
|
|||
// RepositoriesService.List method.
|
|||
type RepositoryListOptions struct { |
|||
// Visibility of repositories to list. Can be one of all, public, or private.
|
|||
// Default: all
|
|||
Visibility string `url:"visibility,omitempty"` |
|||
|
|||
// List repos of given affiliation[s].
|
|||
// Comma-separated list of values. Can include:
|
|||
// * owner: Repositories that are owned by the authenticated user.
|
|||
// * collaborator: Repositories that the user has been added to as a
|
|||
// collaborator.
|
|||
// * organization_member: Repositories that the user has access to through
|
|||
// being a member of an organization. This includes every repository on
|
|||
// every team that the user is on.
|
|||
// Default: owner,collaborator,organization_member
|
|||
Affiliation string `url:"affiliation,omitempty"` |
|||
|
|||
// Type of repositories to list.
|
|||
// Can be one of all, owner, public, private, member. Default: all
|
|||
// Will cause a 422 error if used in the same request as visibility or
|
|||
// affiliation.
|
|||
Type string `url:"type,omitempty"` |
|||
|
|||
// How to sort the repository list. Can be one of created, updated, pushed,
|
|||
// full_name. Default: full_name
|
|||
Sort string `url:"sort,omitempty"` |
|||
|
|||
// Direction in which to sort repositories. Can be one of asc or desc.
|
|||
// Default: when using full_name: asc; otherwise desc
|
|||
Direction string `url:"direction,omitempty"` |
|||
|
|||
ListOptions |
|||
} |
|||
|
|||
// List the repositories for a user. Passing the empty string will list
|
|||
// repositories for the authenticated user.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/#list-user-repositories
|
|||
func (s *RepositoriesService) List(user string, opt *RepositoryListOptions) ([]*Repository, *Response, error) { |
|||
var u string |
|||
if user != "" { |
|||
u = fmt.Sprintf("users/%v/repos", user) |
|||
} else { |
|||
u = "user/repos" |
|||
} |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when license support fully launches
|
|||
req.Header.Set("Accept", mediaTypeLicensesPreview) |
|||
|
|||
repos := new([]*Repository) |
|||
resp, err := s.client.Do(req, repos) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *repos, resp, err |
|||
} |
|||
|
|||
// RepositoryListByOrgOptions specifies the optional parameters to the
|
|||
// RepositoriesService.ListByOrg method.
|
|||
type RepositoryListByOrgOptions struct { |
|||
// Type of repositories to list. Possible values are: all, public, private,
|
|||
// forks, sources, member. Default is "all".
|
|||
Type string `url:"type,omitempty"` |
|||
|
|||
ListOptions |
|||
} |
|||
|
|||
// ListByOrg lists the repositories for an organization.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/#list-organization-repositories
|
|||
func (s *RepositoriesService) ListByOrg(org string, opt *RepositoryListByOrgOptions) ([]*Repository, *Response, error) { |
|||
u := fmt.Sprintf("orgs/%v/repos", org) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when license support fully launches
|
|||
req.Header.Set("Accept", mediaTypeLicensesPreview) |
|||
|
|||
repos := new([]*Repository) |
|||
resp, err := s.client.Do(req, repos) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *repos, resp, err |
|||
} |
|||
|
|||
// RepositoryListAllOptions specifies the optional parameters to the
|
|||
// RepositoriesService.ListAll method.
|
|||
type RepositoryListAllOptions struct { |
|||
// ID of the last repository seen
|
|||
Since int `url:"since,omitempty"` |
|||
|
|||
ListOptions |
|||
} |
|||
|
|||
// ListAll lists all GitHub repositories in the order that they were created.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/#list-all-public-repositories
|
|||
func (s *RepositoriesService) ListAll(opt *RepositoryListAllOptions) ([]*Repository, *Response, error) { |
|||
u, err := addOptions("repositories", opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
repos := new([]*Repository) |
|||
resp, err := s.client.Do(req, repos) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *repos, resp, err |
|||
} |
|||
|
|||
// Create a new repository. If an organization is specified, the new
|
|||
// repository will be created under that org. If the empty string is
|
|||
// specified, it will be created for the authenticated user.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/#create
|
|||
func (s *RepositoriesService) Create(org string, repo *Repository) (*Repository, *Response, error) { |
|||
var u string |
|||
if org != "" { |
|||
u = fmt.Sprintf("orgs/%v/repos", org) |
|||
} else { |
|||
u = "user/repos" |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("POST", u, repo) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
r := new(Repository) |
|||
resp, err := s.client.Do(req, r) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return r, resp, err |
|||
} |
|||
|
|||
// Get fetches a repository.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/#get
|
|||
func (s *RepositoriesService) Get(owner, repo string) (*Repository, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v", owner, repo) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when the license support fully launches
|
|||
// https://developer.github.com/v3/licenses/#get-a-repositorys-license
|
|||
req.Header.Set("Accept", mediaTypeLicensesPreview) |
|||
|
|||
repository := new(Repository) |
|||
resp, err := s.client.Do(req, repository) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return repository, resp, err |
|||
} |
|||
|
|||
// GetByID fetches a repository.
|
|||
//
|
|||
// Note: GetByID uses the undocumented GitHub API endpoint /repositories/:id.
|
|||
func (s *RepositoriesService) GetByID(id int) (*Repository, *Response, error) { |
|||
u := fmt.Sprintf("repositories/%d", id) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when the license support fully launches
|
|||
// https://developer.github.com/v3/licenses/#get-a-repositorys-license
|
|||
req.Header.Set("Accept", mediaTypeLicensesPreview) |
|||
|
|||
repository := new(Repository) |
|||
resp, err := s.client.Do(req, repository) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return repository, resp, err |
|||
} |
|||
|
|||
// Edit updates a repository.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/#edit
|
|||
func (s *RepositoriesService) Edit(owner, repo string, repository *Repository) (*Repository, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v", owner, repo) |
|||
req, err := s.client.NewRequest("PATCH", u, repository) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
r := new(Repository) |
|||
resp, err := s.client.Do(req, r) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return r, resp, err |
|||
} |
|||
|
|||
// Delete a repository.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/repos/#delete-a-repository
|
|||
func (s *RepositoriesService) Delete(owner, repo string) (*Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v", owner, repo) |
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
return s.client.Do(req, nil) |
|||
} |
|||
|
|||
// Contributor represents a repository contributor
|
|||
type Contributor struct { |
|||
Login *string `json:"login,omitempty"` |
|||
ID *int `json:"id,omitempty"` |
|||
AvatarURL *string `json:"avatar_url,omitempty"` |
|||
GravatarID *string `json:"gravatar_id,omitempty"` |
|||
URL *string `json:"url,omitempty"` |
|||
HTMLURL *string `json:"html_url,omitempty"` |
|||
FollowersURL *string `json:"followers_url,omitempty"` |
|||
FollowingURL *string `json:"following_url,omitempty"` |
|||
GistsURL *string `json:"gists_url,omitempty"` |
|||
StarredURL *string `json:"starred_url,omitempty"` |
|||
SubscriptionsURL *string `json:"subscriptions_url,omitempty"` |
|||
OrganizationsURL *string `json:"organizations_url,omitempty"` |
|||
ReposURL *string `json:"repos_url,omitempty"` |
|||
EventsURL *string `json:"events_url,omitempty"` |
|||
ReceivedEventsURL *string `json:"received_events_url,omitempty"` |
|||
Type *string `json:"type,omitempty"` |
|||
SiteAdmin *bool `json:"site_admin"` |
|||
Contributions *int `json:"contributions,omitempty"` |
|||
} |
|||
|
|||
// ListContributorsOptions specifies the optional parameters to the
|
|||
// RepositoriesService.ListContributors method.
|
|||
type ListContributorsOptions struct { |
|||
// Include anonymous contributors in results or not
|
|||
Anon string `url:"anon,omitempty"` |
|||
|
|||
ListOptions |
|||
} |
|||
|
|||
// ListContributors lists contributors for a repository.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/#list-contributors
|
|||
func (s *RepositoriesService) ListContributors(owner string, repository string, opt *ListContributorsOptions) ([]*Contributor, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/contributors", owner, repository) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
contributor := new([]*Contributor) |
|||
resp, err := s.client.Do(req, contributor) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
return *contributor, resp, err |
|||
} |
|||
|
|||
// ListLanguages lists languages for the specified repository. The returned map
|
|||
// specifies the languages and the number of bytes of code written in that
|
|||
// language. For example:
|
|||
//
|
|||
// {
|
|||
// "C": 78769,
|
|||
// "Python": 7769
|
|||
// }
|
|||
//
|
|||
// GitHub API Docs: http://developer.github.com/v3/repos/#list-languages
|
|||
func (s *RepositoriesService) ListLanguages(owner string, repo string) (map[string]int, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/languages", owner, repo) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
languages := make(map[string]int) |
|||
resp, err := s.client.Do(req, &languages) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return languages, resp, err |
|||
} |
|||
|
|||
// ListTeams lists the teams for the specified repository.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/repos/#list-teams
|
|||
func (s *RepositoriesService) ListTeams(owner string, repo string, opt *ListOptions) ([]*Team, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/teams", owner, repo) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
teams := new([]*Team) |
|||
resp, err := s.client.Do(req, teams) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *teams, resp, err |
|||
} |
|||
|
|||
// RepositoryTag represents a repository tag.
|
|||
type RepositoryTag struct { |
|||
Name *string `json:"name,omitempty"` |
|||
Commit *Commit `json:"commit,omitempty"` |
|||
ZipballURL *string `json:"zipball_url,omitempty"` |
|||
TarballURL *string `json:"tarball_url,omitempty"` |
|||
} |
|||
|
|||
// ListTags lists tags for the specified repository.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/repos/#list-tags
|
|||
func (s *RepositoriesService) ListTags(owner string, repo string, opt *ListOptions) ([]*RepositoryTag, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/tags", owner, repo) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
tags := new([]*RepositoryTag) |
|||
resp, err := s.client.Do(req, tags) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *tags, resp, err |
|||
} |
|||
|
|||
// Branch represents a repository branch
|
|||
type Branch struct { |
|||
Name *string `json:"name,omitempty"` |
|||
Commit *Commit `json:"commit,omitempty"` |
|||
Protection *Protection `json:"protection,omitempty"` |
|||
} |
|||
|
|||
// Protection represents a repository branch's protection
|
|||
type Protection struct { |
|||
Enabled *bool `json:"enabled,omitempty"` |
|||
RequiredStatusChecks *RequiredStatusChecks `json:"required_status_checks,omitempty"` |
|||
} |
|||
|
|||
// RequiredStatusChecks represents the protection status of a individual branch
|
|||
type RequiredStatusChecks struct { |
|||
// Who required status checks apply to.
|
|||
// Possible values are:
|
|||
// off
|
|||
// non_admins
|
|||
// everyone
|
|||
EnforcementLevel *string `json:"enforcement_level,omitempty"` |
|||
// The list of status checks which are required
|
|||
Contexts *[]string `json:"contexts,omitempty"` |
|||
} |
|||
|
|||
// ListBranches lists branches for the specified repository.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/#list-branches
|
|||
func (s *RepositoriesService) ListBranches(owner string, repo string, opt *ListOptions) ([]*Branch, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/branches", owner, repo) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) |
|||
|
|||
branches := new([]*Branch) |
|||
resp, err := s.client.Do(req, branches) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *branches, resp, err |
|||
} |
|||
|
|||
// GetBranch gets the specified branch for a repository.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/repos/#get-branch
|
|||
func (s *RepositoriesService) GetBranch(owner, repo, branch string) (*Branch, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/branches/%v", owner, repo, branch) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) |
|||
|
|||
b := new(Branch) |
|||
resp, err := s.client.Do(req, b) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return b, resp, err |
|||
} |
|||
|
|||
// EditBranch edits the branch (currently only Branch Protection)
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/repos/#enabling-and-disabling-branch-protection
|
|||
func (s *RepositoriesService) EditBranch(owner, repo, branchName string, branch *Branch) (*Branch, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/branches/%v", owner, repo, branchName) |
|||
req, err := s.client.NewRequest("PATCH", u, branch) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) |
|||
|
|||
b := new(Branch) |
|||
resp, err := s.client.Do(req, b) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return b, resp, err |
|||
} |
|||
|
|||
// License gets the contents of a repository's license if one is detected.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/licenses/#get-the-contents-of-a-repositorys-license
|
|||
func (s *RepositoriesService) License(owner, repo string) (*License, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/license", owner, repo) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
r := &Repository{} |
|||
resp, err := s.client.Do(req, r) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return r.License, resp, err |
|||
} |
@ -0,0 +1,92 @@ |
|||
// Copyright 2013 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 "fmt" |
|||
|
|||
// ListCollaborators lists the Github users that have access to the repository.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/collaborators/#list
|
|||
func (s *RepositoriesService) ListCollaborators(owner, repo string, opt *ListOptions) ([]*User, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/collaborators", owner, repo) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
users := new([]*User) |
|||
resp, err := s.client.Do(req, users) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *users, resp, err |
|||
} |
|||
|
|||
// IsCollaborator checks whether the specified Github user has collaborator
|
|||
// access to the given repo.
|
|||
// Note: This will return false if the user is not a collaborator OR the user
|
|||
// is not a GitHub user.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/collaborators/#get
|
|||
func (s *RepositoriesService) IsCollaborator(owner, repo, user string) (bool, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/collaborators/%v", owner, repo, user) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return false, nil, err |
|||
} |
|||
|
|||
resp, err := s.client.Do(req, nil) |
|||
isCollab, err := parseBoolResponse(err) |
|||
return isCollab, resp, err |
|||
} |
|||
|
|||
// RepositoryAddCollaboratorOptions specifies the optional parameters to the
|
|||
// RepositoriesService.AddCollaborator method.
|
|||
type RepositoryAddCollaboratorOptions struct { |
|||
// Permission specifies the permission to grant the user on this repository.
|
|||
// Possible values are:
|
|||
// pull - team members can pull, but not push to or administer this repository
|
|||
// push - team members can pull and push, but not administer this repository
|
|||
// admin - team members can pull, push and administer this repository
|
|||
//
|
|||
// Default value is "push". This option is only valid for organization-owned repositories.
|
|||
Permission string `json:"permission,omitempty"` |
|||
} |
|||
|
|||
// AddCollaborator adds the specified Github user as collaborator to the given repo.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/repos/collaborators/#add-user-as-a-collaborator
|
|||
func (s *RepositoriesService) AddCollaborator(owner, repo, user string, opt *RepositoryAddCollaboratorOptions) (*Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/collaborators/%v", owner, repo, user) |
|||
req, err := s.client.NewRequest("PUT", u, opt) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeRepositoryInvitationsPreview) |
|||
|
|||
return s.client.Do(req, nil) |
|||
} |
|||
|
|||
// RemoveCollaborator removes the specified Github user as collaborator from the given repo.
|
|||
// Note: Does not return error if a valid user that is not a collaborator is removed.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/collaborators/#remove-collaborator
|
|||
func (s *RepositoriesService) RemoveCollaborator(owner, repo, user string) (*Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/collaborators/%v", owner, repo, user) |
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return s.client.Do(req, nil) |
|||
} |
@ -0,0 +1,134 @@ |
|||
// Copyright 2013 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestRepositoriesService_ListCollaborators(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/collaborators", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{"page": "2"}) |
|||
fmt.Fprintf(w, `[{"id":1}, {"id":2}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 2} |
|||
users, _, err := client.Repositories.ListCollaborators("o", "r", opt) |
|||
if err != nil { |
|||
t.Errorf("Repositories.ListCollaborators returned error: %v", err) |
|||
} |
|||
|
|||
want := []*User{{ID: Int(1)}, {ID: Int(2)}} |
|||
if !reflect.DeepEqual(users, want) { |
|||
t.Errorf("Repositories.ListCollaborators returned %+v, want %+v", users, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_ListCollaborators_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Repositories.ListCollaborators("%", "%", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestRepositoriesService_IsCollaborator_True(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/collaborators/u", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
w.WriteHeader(http.StatusNoContent) |
|||
}) |
|||
|
|||
isCollab, _, err := client.Repositories.IsCollaborator("o", "r", "u") |
|||
if err != nil { |
|||
t.Errorf("Repositories.IsCollaborator returned error: %v", err) |
|||
} |
|||
|
|||
if !isCollab { |
|||
t.Errorf("Repositories.IsCollaborator returned false, want true") |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_IsCollaborator_False(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/collaborators/u", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
w.WriteHeader(http.StatusNotFound) |
|||
}) |
|||
|
|||
isCollab, _, err := client.Repositories.IsCollaborator("o", "r", "u") |
|||
if err != nil { |
|||
t.Errorf("Repositories.IsCollaborator returned error: %v", err) |
|||
} |
|||
|
|||
if isCollab { |
|||
t.Errorf("Repositories.IsCollaborator returned true, want false") |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_IsCollaborator_invalidUser(t *testing.T) { |
|||
_, _, err := client.Repositories.IsCollaborator("%", "%", "%") |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestRepositoriesService_AddCollaborator(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
opt := &RepositoryAddCollaboratorOptions{Permission: "admin"} |
|||
|
|||
mux.HandleFunc("/repos/o/r/collaborators/u", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(RepositoryAddCollaboratorOptions) |
|||
json.NewDecoder(r.Body).Decode(v) |
|||
|
|||
testMethod(t, r, "PUT") |
|||
testHeader(t, r, "Accept", mediaTypeRepositoryInvitationsPreview) |
|||
if !reflect.DeepEqual(v, opt) { |
|||
t.Errorf("Request body = %+v, want %+v", v, opt) |
|||
} |
|||
|
|||
w.WriteHeader(http.StatusNoContent) |
|||
}) |
|||
|
|||
_, err := client.Repositories.AddCollaborator("o", "r", "u", opt) |
|||
if err != nil { |
|||
t.Errorf("Repositories.AddCollaborator returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_AddCollaborator_invalidUser(t *testing.T) { |
|||
_, err := client.Repositories.AddCollaborator("%", "%", "%", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestRepositoriesService_RemoveCollaborator(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/collaborators/u", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
w.WriteHeader(http.StatusNoContent) |
|||
}) |
|||
|
|||
_, err := client.Repositories.RemoveCollaborator("o", "r", "u") |
|||
if err != nil { |
|||
t.Errorf("Repositories.RemoveCollaborator returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_RemoveCollaborator_invalidUser(t *testing.T) { |
|||
_, err := client.Repositories.RemoveCollaborator("%", "%", "%") |
|||
testURLParseError(t, err) |
|||
} |
@ -0,0 +1,160 @@ |
|||
// Copyright 2013 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 ( |
|||
"fmt" |
|||
"time" |
|||
) |
|||
|
|||
// RepositoryComment represents a comment for a commit, file, or line in a repository.
|
|||
type RepositoryComment struct { |
|||
HTMLURL *string `json:"html_url,omitempty"` |
|||
URL *string `json:"url,omitempty"` |
|||
ID *int `json:"id,omitempty"` |
|||
CommitID *string `json:"commit_id,omitempty"` |
|||
User *User `json:"user,omitempty"` |
|||
Reactions *Reactions `json:"reactions,omitempty"` |
|||
CreatedAt *time.Time `json:"created_at,omitempty"` |
|||
UpdatedAt *time.Time `json:"updated_at,omitempty"` |
|||
|
|||
// User-mutable fields
|
|||
Body *string `json:"body"` |
|||
// User-initialized fields
|
|||
Path *string `json:"path,omitempty"` |
|||
Position *int `json:"position,omitempty"` |
|||
} |
|||
|
|||
func (r RepositoryComment) String() string { |
|||
return Stringify(r) |
|||
} |
|||
|
|||
// ListComments lists all the comments for the repository.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/comments/#list-commit-comments-for-a-repository
|
|||
func (s *RepositoriesService) ListComments(owner, repo string, opt *ListOptions) ([]*RepositoryComment, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/comments", owner, repo) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeReactionsPreview) |
|||
|
|||
comments := new([]*RepositoryComment) |
|||
resp, err := s.client.Do(req, comments) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *comments, resp, err |
|||
} |
|||
|
|||
// ListCommitComments lists all the comments for a given commit SHA.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/comments/#list-comments-for-a-single-commit
|
|||
func (s *RepositoriesService) ListCommitComments(owner, repo, sha string, opt *ListOptions) ([]*RepositoryComment, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/commits/%v/comments", owner, repo, sha) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeReactionsPreview) |
|||
|
|||
comments := new([]*RepositoryComment) |
|||
resp, err := s.client.Do(req, comments) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *comments, resp, err |
|||
} |
|||
|
|||
// CreateComment creates a comment for the given commit.
|
|||
// Note: GitHub allows for comments to be created for non-existing files and positions.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/comments/#create-a-commit-comment
|
|||
func (s *RepositoriesService) CreateComment(owner, repo, sha string, comment *RepositoryComment) (*RepositoryComment, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/commits/%v/comments", owner, repo, sha) |
|||
req, err := s.client.NewRequest("POST", u, comment) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
c := new(RepositoryComment) |
|||
resp, err := s.client.Do(req, c) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return c, resp, err |
|||
} |
|||
|
|||
// GetComment gets a single comment from a repository.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/comments/#get-a-single-commit-comment
|
|||
func (s *RepositoriesService) GetComment(owner, repo string, id int) (*RepositoryComment, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/comments/%v", owner, repo, id) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeReactionsPreview) |
|||
|
|||
c := new(RepositoryComment) |
|||
resp, err := s.client.Do(req, c) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return c, resp, err |
|||
} |
|||
|
|||
// UpdateComment updates the body of a single comment.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/comments/#update-a-commit-comment
|
|||
func (s *RepositoriesService) UpdateComment(owner, repo string, id int, comment *RepositoryComment) (*RepositoryComment, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/comments/%v", owner, repo, id) |
|||
req, err := s.client.NewRequest("PATCH", u, comment) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
c := new(RepositoryComment) |
|||
resp, err := s.client.Do(req, c) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return c, resp, err |
|||
} |
|||
|
|||
// DeleteComment deletes a single comment from a repository.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/comments/#delete-a-commit-comment
|
|||
func (s *RepositoriesService) DeleteComment(owner, repo string, id int) (*Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/comments/%v", owner, repo, id) |
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return s.client.Do(req, nil) |
|||
} |
@ -0,0 +1,183 @@ |
|||
// Copyright 2013 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestRepositoriesService_ListComments(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/comments", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeReactionsPreview) |
|||
testFormValues(t, r, values{"page": "2"}) |
|||
fmt.Fprint(w, `[{"id":1}, {"id":2}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 2} |
|||
comments, _, err := client.Repositories.ListComments("o", "r", opt) |
|||
if err != nil { |
|||
t.Errorf("Repositories.ListComments returned error: %v", err) |
|||
} |
|||
|
|||
want := []*RepositoryComment{{ID: Int(1)}, {ID: Int(2)}} |
|||
if !reflect.DeepEqual(comments, want) { |
|||
t.Errorf("Repositories.ListComments returned %+v, want %+v", comments, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_ListComments_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Repositories.ListComments("%", "%", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestRepositoriesService_ListCommitComments(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/commits/s/comments", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeReactionsPreview) |
|||
testFormValues(t, r, values{"page": "2"}) |
|||
fmt.Fprint(w, `[{"id":1}, {"id":2}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 2} |
|||
comments, _, err := client.Repositories.ListCommitComments("o", "r", "s", opt) |
|||
if err != nil { |
|||
t.Errorf("Repositories.ListCommitComments returned error: %v", err) |
|||
} |
|||
|
|||
want := []*RepositoryComment{{ID: Int(1)}, {ID: Int(2)}} |
|||
if !reflect.DeepEqual(comments, want) { |
|||
t.Errorf("Repositories.ListCommitComments returned %+v, want %+v", comments, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_ListCommitComments_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Repositories.ListCommitComments("%", "%", "%", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestRepositoriesService_CreateComment(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &RepositoryComment{Body: String("b")} |
|||
|
|||
mux.HandleFunc("/repos/o/r/commits/s/comments", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(RepositoryComment) |
|||
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.Repositories.CreateComment("o", "r", "s", input) |
|||
if err != nil { |
|||
t.Errorf("Repositories.CreateComment returned error: %v", err) |
|||
} |
|||
|
|||
want := &RepositoryComment{ID: Int(1)} |
|||
if !reflect.DeepEqual(comment, want) { |
|||
t.Errorf("Repositories.CreateComment returned %+v, want %+v", comment, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_CreateComment_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Repositories.CreateComment("%", "%", "%", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestRepositoriesService_GetComment(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/comments/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeReactionsPreview) |
|||
fmt.Fprint(w, `{"id":1}`) |
|||
}) |
|||
|
|||
comment, _, err := client.Repositories.GetComment("o", "r", 1) |
|||
if err != nil { |
|||
t.Errorf("Repositories.GetComment returned error: %v", err) |
|||
} |
|||
|
|||
want := &RepositoryComment{ID: Int(1)} |
|||
if !reflect.DeepEqual(comment, want) { |
|||
t.Errorf("Repositories.GetComment returned %+v, want %+v", comment, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_GetComment_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Repositories.GetComment("%", "%", 1) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestRepositoriesService_UpdateComment(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &RepositoryComment{Body: String("b")} |
|||
|
|||
mux.HandleFunc("/repos/o/r/comments/1", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(RepositoryComment) |
|||
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.Repositories.UpdateComment("o", "r", 1, input) |
|||
if err != nil { |
|||
t.Errorf("Repositories.UpdateComment returned error: %v", err) |
|||
} |
|||
|
|||
want := &RepositoryComment{ID: Int(1)} |
|||
if !reflect.DeepEqual(comment, want) { |
|||
t.Errorf("Repositories.UpdateComment returned %+v, want %+v", comment, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_UpdateComment_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Repositories.UpdateComment("%", "%", 1, nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestRepositoriesService_DeleteComment(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/comments/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
}) |
|||
|
|||
_, err := client.Repositories.DeleteComment("o", "r", 1) |
|||
if err != nil { |
|||
t.Errorf("Repositories.DeleteComment returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_DeleteComment_invalidOwner(t *testing.T) { |
|||
_, err := client.Repositories.DeleteComment("%", "%", 1) |
|||
testURLParseError(t, err) |
|||
} |
@ -0,0 +1,198 @@ |
|||
// Copyright 2013 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 ( |
|||
"bytes" |
|||
"fmt" |
|||
"time" |
|||
) |
|||
|
|||
// RepositoryCommit represents a commit in a repo.
|
|||
// Note that it's wrapping a Commit, so author/committer information is in two places,
|
|||
// but contain different details about them: in RepositoryCommit "github details", in Commit - "git details".
|
|||
type RepositoryCommit struct { |
|||
SHA *string `json:"sha,omitempty"` |
|||
Commit *Commit `json:"commit,omitempty"` |
|||
Author *User `json:"author,omitempty"` |
|||
Committer *User `json:"committer,omitempty"` |
|||
Parents []Commit `json:"parents,omitempty"` |
|||
Message *string `json:"message,omitempty"` |
|||
HTMLURL *string `json:"html_url,omitempty"` |
|||
|
|||
// Details about how many changes were made in this commit. Only filled in during GetCommit!
|
|||
Stats *CommitStats `json:"stats,omitempty"` |
|||
// Details about which files, and how this commit touched. Only filled in during GetCommit!
|
|||
Files []CommitFile `json:"files,omitempty"` |
|||
} |
|||
|
|||
func (r RepositoryCommit) String() string { |
|||
return Stringify(r) |
|||
} |
|||
|
|||
// CommitStats represents the number of additions / deletions from a file in a given RepositoryCommit or GistCommit.
|
|||
type CommitStats struct { |
|||
Additions *int `json:"additions,omitempty"` |
|||
Deletions *int `json:"deletions,omitempty"` |
|||
Total *int `json:"total,omitempty"` |
|||
} |
|||
|
|||
func (c CommitStats) String() string { |
|||
return Stringify(c) |
|||
} |
|||
|
|||
// CommitFile represents a file modified in a commit.
|
|||
type CommitFile struct { |
|||
SHA *string `json:"sha,omitempty"` |
|||
Filename *string `json:"filename,omitempty"` |
|||
Additions *int `json:"additions,omitempty"` |
|||
Deletions *int `json:"deletions,omitempty"` |
|||
Changes *int `json:"changes,omitempty"` |
|||
Status *string `json:"status,omitempty"` |
|||
Patch *string `json:"patch,omitempty"` |
|||
} |
|||
|
|||
func (c CommitFile) String() string { |
|||
return Stringify(c) |
|||
} |
|||
|
|||
// CommitsComparison is the result of comparing two commits.
|
|||
// See CompareCommits() for details.
|
|||
type CommitsComparison struct { |
|||
BaseCommit *RepositoryCommit `json:"base_commit,omitempty"` |
|||
MergeBaseCommit *RepositoryCommit `json:"merge_base_commit,omitempty"` |
|||
|
|||
// Head can be 'behind' or 'ahead'
|
|||
Status *string `json:"status,omitempty"` |
|||
AheadBy *int `json:"ahead_by,omitempty"` |
|||
BehindBy *int `json:"behind_by,omitempty"` |
|||
TotalCommits *int `json:"total_commits,omitempty"` |
|||
|
|||
Commits []RepositoryCommit `json:"commits,omitempty"` |
|||
|
|||
Files []CommitFile `json:"files,omitempty"` |
|||
} |
|||
|
|||
func (c CommitsComparison) String() string { |
|||
return Stringify(c) |
|||
} |
|||
|
|||
// CommitsListOptions specifies the optional parameters to the
|
|||
// RepositoriesService.ListCommits method.
|
|||
type CommitsListOptions struct { |
|||
// SHA or branch to start listing Commits from.
|
|||
SHA string `url:"sha,omitempty"` |
|||
|
|||
// Path that should be touched by the returned Commits.
|
|||
Path string `url:"path,omitempty"` |
|||
|
|||
// Author of by which to filter Commits.
|
|||
Author string `url:"author,omitempty"` |
|||
|
|||
// Since when should Commits be included in the response.
|
|||
Since time.Time `url:"since,omitempty"` |
|||
|
|||
// Until when should Commits be included in the response.
|
|||
Until time.Time `url:"until,omitempty"` |
|||
|
|||
ListOptions |
|||
} |
|||
|
|||
// ListCommits lists the commits of a repository.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/commits/#list
|
|||
func (s *RepositoriesService) ListCommits(owner, repo string, opt *CommitsListOptions) ([]*RepositoryCommit, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/commits", owner, repo) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
commits := new([]*RepositoryCommit) |
|||
resp, err := s.client.Do(req, commits) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *commits, resp, err |
|||
} |
|||
|
|||
// GetCommit fetches the specified commit, including all details about it.
|
|||
// todo: support media formats - https://github.com/google/go-github/issues/6
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/commits/#get-a-single-commit
|
|||
// See also: http://developer.github.com//v3/git/commits/#get-a-single-commit provides the same functionality
|
|||
func (s *RepositoriesService) GetCommit(owner, repo, sha string) (*RepositoryCommit, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/commits/%v", owner, repo, sha) |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeGitSigningPreview) |
|||
|
|||
commit := new(RepositoryCommit) |
|||
resp, err := s.client.Do(req, commit) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return commit, resp, err |
|||
} |
|||
|
|||
// GetCommitSHA1 gets the SHA-1 of a commit reference. If a last-known SHA1 is
|
|||
// supplied and no new commits have occurred, a 304 Unmodified response is returned.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/repos/commits/#get-the-sha-1-of-a-commit-reference
|
|||
func (s *RepositoriesService) GetCommitSHA1(owner, repo, ref, lastSHA string) (string, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/commits/%v", owner, repo, ref) |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return "", nil, err |
|||
} |
|||
if lastSHA != "" { |
|||
req.Header.Set("If-None-Match", `"`+lastSHA+`"`) |
|||
} |
|||
|
|||
req.Header.Set("Accept", mediaTypeV3SHA) |
|||
|
|||
var buf bytes.Buffer |
|||
resp, err := s.client.Do(req, &buf) |
|||
if err != nil { |
|||
return "", resp, err |
|||
} |
|||
|
|||
return buf.String(), resp, err |
|||
} |
|||
|
|||
// CompareCommits compares a range of commits with each other.
|
|||
// todo: support media formats - https://github.com/google/go-github/issues/6
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/commits/index.html#compare-two-commits
|
|||
func (s *RepositoriesService) CompareCommits(owner, repo string, base, head string) (*CommitsComparison, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/compare/%v...%v", owner, repo, base, head) |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
comp := new(CommitsComparison) |
|||
resp, err := s.client.Do(req, comp) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return comp, resp, err |
|||
} |
@ -0,0 +1,233 @@ |
|||
// Copyright 2013 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 ( |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
"time" |
|||
) |
|||
|
|||
func TestRepositoriesService_ListCommits(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
// given
|
|||
mux.HandleFunc("/repos/o/r/commits", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, |
|||
values{ |
|||
"sha": "s", |
|||
"path": "p", |
|||
"author": "a", |
|||
"since": "2013-08-01T00:00:00Z", |
|||
"until": "2013-09-03T00:00:00Z", |
|||
}) |
|||
fmt.Fprintf(w, `[{"sha": "s"}]`) |
|||
}) |
|||
|
|||
opt := &CommitsListOptions{ |
|||
SHA: "s", |
|||
Path: "p", |
|||
Author: "a", |
|||
Since: time.Date(2013, time.August, 1, 0, 0, 0, 0, time.UTC), |
|||
Until: time.Date(2013, time.September, 3, 0, 0, 0, 0, time.UTC), |
|||
} |
|||
commits, _, err := client.Repositories.ListCommits("o", "r", opt) |
|||
if err != nil { |
|||
t.Errorf("Repositories.ListCommits returned error: %v", err) |
|||
} |
|||
|
|||
want := []*RepositoryCommit{{SHA: String("s")}} |
|||
if !reflect.DeepEqual(commits, want) { |
|||
t.Errorf("Repositories.ListCommits returned %+v, want %+v", commits, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_GetCommit(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/commits/s", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeGitSigningPreview) |
|||
fmt.Fprintf(w, `{ |
|||
"sha": "s", |
|||
"commit": { "message": "m" }, |
|||
"author": { "login": "l" }, |
|||
"committer": { "login": "l" }, |
|||
"parents": [ { "sha": "s" } ], |
|||
"stats": { "additions": 104, "deletions": 4, "total": 108 }, |
|||
"files": [ |
|||
{ |
|||
"filename": "f", |
|||
"additions": 10, |
|||
"deletions": 2, |
|||
"changes": 12, |
|||
"status": "s", |
|||
"raw_url": "r", |
|||
"blob_url": "b", |
|||
"patch": "p" |
|||
} |
|||
] |
|||
}`) |
|||
}) |
|||
|
|||
commit, _, err := client.Repositories.GetCommit("o", "r", "s") |
|||
if err != nil { |
|||
t.Errorf("Repositories.GetCommit returned error: %v", err) |
|||
} |
|||
|
|||
want := &RepositoryCommit{ |
|||
SHA: String("s"), |
|||
Commit: &Commit{ |
|||
Message: String("m"), |
|||
}, |
|||
Author: &User{ |
|||
Login: String("l"), |
|||
}, |
|||
Committer: &User{ |
|||
Login: String("l"), |
|||
}, |
|||
Parents: []Commit{ |
|||
{ |
|||
SHA: String("s"), |
|||
}, |
|||
}, |
|||
Stats: &CommitStats{ |
|||
Additions: Int(104), |
|||
Deletions: Int(4), |
|||
Total: Int(108), |
|||
}, |
|||
Files: []CommitFile{ |
|||
{ |
|||
Filename: String("f"), |
|||
Additions: Int(10), |
|||
Deletions: Int(2), |
|||
Changes: Int(12), |
|||
Status: String("s"), |
|||
Patch: String("p"), |
|||
}, |
|||
}, |
|||
} |
|||
if !reflect.DeepEqual(commit, want) { |
|||
t.Errorf("Repositories.GetCommit returned \n%+v, want \n%+v", commit, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_GetCommitSHA1(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
const sha1 = "01234abcde" |
|||
|
|||
mux.HandleFunc("/repos/o/r/commits/master", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeV3SHA) |
|||
|
|||
fmt.Fprintf(w, sha1) |
|||
}) |
|||
|
|||
got, _, err := client.Repositories.GetCommitSHA1("o", "r", "master", "") |
|||
if err != nil { |
|||
t.Errorf("Repositories.GetCommitSHA1 returned error: %v", err) |
|||
} |
|||
|
|||
want := sha1 |
|||
if got != want { |
|||
t.Errorf("Repositories.GetCommitSHA1 = %v, want %v", got, want) |
|||
} |
|||
|
|||
mux.HandleFunc("/repos/o/r/commits/tag", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeV3SHA) |
|||
testHeader(t, r, "If-None-Match", `"`+sha1+`"`) |
|||
|
|||
w.WriteHeader(http.StatusNotModified) |
|||
}) |
|||
|
|||
got, _, err = client.Repositories.GetCommitSHA1("o", "r", "tag", sha1) |
|||
if err == nil { |
|||
t.Errorf("Expected HTTP 304 response") |
|||
} |
|||
|
|||
want = "" |
|||
if got != want { |
|||
t.Errorf("Repositories.GetCommitSHA1 = %v, want %v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_CompareCommits(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/compare/b...h", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprintf(w, `{ |
|||
"base_commit": { |
|||
"sha": "s", |
|||
"commit": { |
|||
"author": { "name": "n" }, |
|||
"committer": { "name": "n" }, |
|||
"message": "m", |
|||
"tree": { "sha": "t" } |
|||
}, |
|||
"author": { "login": "n" }, |
|||
"committer": { "login": "l" }, |
|||
"parents": [ { "sha": "s" } ] |
|||
}, |
|||
"status": "s", |
|||
"ahead_by": 1, |
|||
"behind_by": 2, |
|||
"total_commits": 1, |
|||
"commits": [ |
|||
{ |
|||
"sha": "s", |
|||
"commit": { "author": { "name": "n" } }, |
|||
"author": { "login": "l" }, |
|||
"committer": { "login": "l" }, |
|||
"parents": [ { "sha": "s" } ] |
|||
} |
|||
], |
|||
"files": [ { "filename": "f" } ] |
|||
}`) |
|||
}) |
|||
|
|||
got, _, err := client.Repositories.CompareCommits("o", "r", "b", "h") |
|||
if err != nil { |
|||
t.Errorf("Repositories.CompareCommits returned error: %v", err) |
|||
} |
|||
|
|||
want := &CommitsComparison{ |
|||
Status: String("s"), |
|||
AheadBy: Int(1), |
|||
BehindBy: Int(2), |
|||
TotalCommits: Int(1), |
|||
BaseCommit: &RepositoryCommit{ |
|||
Commit: &Commit{ |
|||
Author: &CommitAuthor{Name: String("n")}, |
|||
}, |
|||
Author: &User{Login: String("l")}, |
|||
Committer: &User{Login: String("l")}, |
|||
Message: String("m"), |
|||
}, |
|||
Commits: []RepositoryCommit{ |
|||
{ |
|||
SHA: String("s"), |
|||
}, |
|||
}, |
|||
Files: []CommitFile{ |
|||
{ |
|||
Filename: String("f"), |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
if reflect.DeepEqual(got, want) { |
|||
t.Errorf("Repositories.CompareCommits returned \n%+v, want \n%+v", got, want) |
|||
} |
|||
} |
@ -0,0 +1,275 @@ |
|||
// Copyright 2013 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.
|
|||
|
|||
// Repository contents API methods.
|
|||
// http://developer.github.com/v3/repos/contents/
|
|||
|
|||
package github |
|||
|
|||
import ( |
|||
"encoding/base64" |
|||
"encoding/json" |
|||
"errors" |
|||
"fmt" |
|||
"io" |
|||
"net/http" |
|||
"net/url" |
|||
"path" |
|||
) |
|||
|
|||
// RepositoryContent represents a file or directory in a github repository.
|
|||
type RepositoryContent struct { |
|||
Type *string `json:"type,omitempty"` |
|||
Encoding *string `json:"encoding,omitempty"` |
|||
Size *int `json:"size,omitempty"` |
|||
Name *string `json:"name,omitempty"` |
|||
Path *string `json:"path,omitempty"` |
|||
// Content contains the actual file content, which may be encoded.
|
|||
// Callers should call GetContent which will decode the content if
|
|||
// necessary.
|
|||
Content *string `json:"content,omitempty"` |
|||
SHA *string `json:"sha,omitempty"` |
|||
URL *string `json:"url,omitempty"` |
|||
GitURL *string `json:"git_url,omitempty"` |
|||
HTMLURL *string `json:"html_url,omitempty"` |
|||
DownloadURL *string `json:"download_url,omitempty"` |
|||
} |
|||
|
|||
// RepositoryContentResponse holds the parsed response from CreateFile, UpdateFile, and DeleteFile.
|
|||
type RepositoryContentResponse struct { |
|||
Content *RepositoryContent `json:"content,omitempty"` |
|||
Commit `json:"commit,omitempty"` |
|||
} |
|||
|
|||
// RepositoryContentFileOptions specifies optional parameters for CreateFile, UpdateFile, and DeleteFile.
|
|||
type RepositoryContentFileOptions struct { |
|||
Message *string `json:"message,omitempty"` |
|||
Content []byte `json:"content,omitempty"` // unencoded
|
|||
SHA *string `json:"sha,omitempty"` |
|||
Branch *string `json:"branch,omitempty"` |
|||
Author *CommitAuthor `json:"author,omitempty"` |
|||
Committer *CommitAuthor `json:"committer,omitempty"` |
|||
} |
|||
|
|||
// RepositoryContentGetOptions represents an optional ref parameter, which can be a SHA,
|
|||
// branch, or tag
|
|||
type RepositoryContentGetOptions struct { |
|||
Ref string `url:"ref,omitempty"` |
|||
} |
|||
|
|||
// String converts RepositoryContent to a string. It's primarily for testing.
|
|||
func (r RepositoryContent) String() string { |
|||
return Stringify(r) |
|||
} |
|||
|
|||
// Decode decodes the file content if it is base64 encoded.
|
|||
//
|
|||
// Deprecated: Use GetContent instead.
|
|||
func (r *RepositoryContent) Decode() ([]byte, error) { |
|||
if *r.Encoding != "base64" { |
|||
return nil, errors.New("cannot decode non-base64") |
|||
} |
|||
o, err := base64.StdEncoding.DecodeString(*r.Content) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return o, nil |
|||
} |
|||
|
|||
// GetContent returns the content of r, decoding it if necessary.
|
|||
func (r *RepositoryContent) GetContent() (string, error) { |
|||
var encoding string |
|||
if r.Encoding != nil { |
|||
encoding = *r.Encoding |
|||
} |
|||
|
|||
switch encoding { |
|||
case "base64": |
|||
c, err := base64.StdEncoding.DecodeString(*r.Content) |
|||
return string(c), err |
|||
case "": |
|||
if r.Content == nil { |
|||
return "", nil |
|||
} |
|||
return *r.Content, nil |
|||
default: |
|||
return "", fmt.Errorf("unsupported content encoding: %v", encoding) |
|||
} |
|||
} |
|||
|
|||
// GetReadme gets the Readme file for the repository.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/contents/#get-the-readme
|
|||
func (s *RepositoriesService) GetReadme(owner, repo string, opt *RepositoryContentGetOptions) (*RepositoryContent, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/readme", owner, repo) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
readme := new(RepositoryContent) |
|||
resp, err := s.client.Do(req, readme) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
return readme, resp, err |
|||
} |
|||
|
|||
// DownloadContents returns an io.ReadCloser that reads the contents of the
|
|||
// specified file. This function will work with files of any size, as opposed
|
|||
// to GetContents which is limited to 1 Mb files. It is the caller's
|
|||
// responsibility to close the ReadCloser.
|
|||
func (s *RepositoriesService) DownloadContents(owner, repo, filepath string, opt *RepositoryContentGetOptions) (io.ReadCloser, error) { |
|||
dir := path.Dir(filepath) |
|||
filename := path.Base(filepath) |
|||
_, dirContents, _, err := s.GetContents(owner, repo, dir, opt) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
for _, contents := range dirContents { |
|||
if *contents.Name == filename { |
|||
if contents.DownloadURL == nil || *contents.DownloadURL == "" { |
|||
return nil, fmt.Errorf("No download link found for %s", filepath) |
|||
} |
|||
resp, err := s.client.client.Get(*contents.DownloadURL) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return resp.Body, nil |
|||
} |
|||
} |
|||
return nil, fmt.Errorf("No file named %s found in %s", filename, dir) |
|||
} |
|||
|
|||
// GetContents can return either the metadata and content of a single file
|
|||
// (when path references a file) or the metadata of all the files and/or
|
|||
// subdirectories of a directory (when path references a directory). To make it
|
|||
// easy to distinguish between both result types and to mimic the API as much
|
|||
// as possible, both result types will be returned but only one will contain a
|
|||
// value and the other will be nil.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/contents/#get-contents
|
|||
func (s *RepositoriesService) GetContents(owner, repo, path string, opt *RepositoryContentGetOptions) (fileContent *RepositoryContent, directoryContent []*RepositoryContent, resp *Response, err error) { |
|||
escapedPath := (&url.URL{Path: path}).String() |
|||
u := fmt.Sprintf("repos/%s/%s/contents/%s", owner, repo, escapedPath) |
|||
u, err = addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, nil, err |
|||
} |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, nil, err |
|||
} |
|||
var rawJSON json.RawMessage |
|||
resp, err = s.client.Do(req, &rawJSON) |
|||
if err != nil { |
|||
return nil, nil, resp, err |
|||
} |
|||
fileUnmarshalError := json.Unmarshal(rawJSON, &fileContent) |
|||
if fileUnmarshalError == nil { |
|||
return fileContent, nil, resp, fileUnmarshalError |
|||
} |
|||
directoryUnmarshalError := json.Unmarshal(rawJSON, &directoryContent) |
|||
if directoryUnmarshalError == nil { |
|||
return nil, directoryContent, resp, directoryUnmarshalError |
|||
} |
|||
return nil, nil, resp, fmt.Errorf("unmarshalling failed for both file and directory content: %s and %s ", fileUnmarshalError, directoryUnmarshalError) |
|||
} |
|||
|
|||
// CreateFile creates a new file in a repository at the given path and returns
|
|||
// the commit and file metadata.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/contents/#create-a-file
|
|||
func (s *RepositoriesService) CreateFile(owner, repo, path string, opt *RepositoryContentFileOptions) (*RepositoryContentResponse, *Response, error) { |
|||
u := fmt.Sprintf("repos/%s/%s/contents/%s", owner, repo, path) |
|||
req, err := s.client.NewRequest("PUT", u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
createResponse := new(RepositoryContentResponse) |
|||
resp, err := s.client.Do(req, createResponse) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
return createResponse, resp, err |
|||
} |
|||
|
|||
// UpdateFile updates a file in a repository at the given path and returns the
|
|||
// commit and file metadata. Requires the blob SHA of the file being updated.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/contents/#update-a-file
|
|||
func (s *RepositoriesService) UpdateFile(owner, repo, path string, opt *RepositoryContentFileOptions) (*RepositoryContentResponse, *Response, error) { |
|||
u := fmt.Sprintf("repos/%s/%s/contents/%s", owner, repo, path) |
|||
req, err := s.client.NewRequest("PUT", u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
updateResponse := new(RepositoryContentResponse) |
|||
resp, err := s.client.Do(req, updateResponse) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
return updateResponse, resp, err |
|||
} |
|||
|
|||
// DeleteFile deletes a file from a repository and returns the commit.
|
|||
// Requires the blob SHA of the file to be deleted.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/contents/#delete-a-file
|
|||
func (s *RepositoriesService) DeleteFile(owner, repo, path string, opt *RepositoryContentFileOptions) (*RepositoryContentResponse, *Response, error) { |
|||
u := fmt.Sprintf("repos/%s/%s/contents/%s", owner, repo, path) |
|||
req, err := s.client.NewRequest("DELETE", u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
deleteResponse := new(RepositoryContentResponse) |
|||
resp, err := s.client.Do(req, deleteResponse) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
return deleteResponse, resp, err |
|||
} |
|||
|
|||
// archiveFormat is used to define the archive type when calling GetArchiveLink.
|
|||
type archiveFormat string |
|||
|
|||
const ( |
|||
// Tarball specifies an archive in gzipped tar format.
|
|||
Tarball archiveFormat = "tarball" |
|||
|
|||
// Zipball specifies an archive in zip format.
|
|||
Zipball archiveFormat = "zipball" |
|||
) |
|||
|
|||
// GetArchiveLink returns an URL to download a tarball or zipball archive for a
|
|||
// repository. The archiveFormat can be specified by either the github.Tarball
|
|||
// or github.Zipball constant.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/contents/#get-archive-link
|
|||
func (s *RepositoriesService) GetArchiveLink(owner, repo string, archiveformat archiveFormat, opt *RepositoryContentGetOptions) (*url.URL, *Response, error) { |
|||
u := fmt.Sprintf("repos/%s/%s/%s", owner, repo, archiveformat) |
|||
if opt != nil && opt.Ref != "" { |
|||
u += fmt.Sprintf("/%s", opt.Ref) |
|||
} |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
var resp *http.Response |
|||
// Use http.DefaultTransport if no custom Transport is configured
|
|||
if s.client.client.Transport == nil { |
|||
resp, err = http.DefaultTransport.RoundTrip(req) |
|||
} else { |
|||
resp, err = s.client.client.Transport.RoundTrip(req) |
|||
} |
|||
if err != nil || resp.StatusCode != http.StatusFound { |
|||
return nil, newResponse(resp), err |
|||
} |
|||
parsedURL, err := url.Parse(resp.Header.Get("Location")) |
|||
return parsedURL, newResponse(resp), err |
|||
} |
@ -0,0 +1,412 @@ |
|||
// Copyright 2014 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 ( |
|||
"fmt" |
|||
"io/ioutil" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestRepositoryContent_Decode(t *testing.T) { |
|||
tests := []struct { |
|||
encoding, content *string // input encoding and content
|
|||
want string // desired output
|
|||
wantErr bool // whether an error is expected
|
|||
}{ |
|||
{ |
|||
encoding: String("base64"), |
|||
content: String("aGVsbG8="), |
|||
want: "hello", |
|||
wantErr: false, |
|||
}, |
|||
{ |
|||
encoding: String("bad"), |
|||
content: String("aGVsbG8="), |
|||
want: "", |
|||
wantErr: true, |
|||
}, |
|||
} |
|||
|
|||
for _, tt := range tests { |
|||
r := RepositoryContent{Encoding: tt.encoding, Content: tt.content} |
|||
o, err := r.Decode() |
|||
if err != nil && !tt.wantErr { |
|||
t.Errorf("RepositoryContent(%q, %q) returned unexpected error: %v", tt.encoding, tt.content, err) |
|||
} |
|||
if err == nil && tt.wantErr { |
|||
t.Errorf("RepositoryContent(%q, %q) did not return unexpected error", tt.encoding, tt.content) |
|||
} |
|||
if got, want := string(o), tt.want; got != want { |
|||
t.Errorf("RepositoryContent.Decode returned %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
} |
|||
|
|||
func TestRepositoryContent_GetContent(t *testing.T) { |
|||
tests := []struct { |
|||
encoding, content *string // input encoding and content
|
|||
want string // desired output
|
|||
wantErr bool // whether an error is expected
|
|||
}{ |
|||
{ |
|||
encoding: String(""), |
|||
content: String("hello"), |
|||
want: "hello", |
|||
wantErr: false, |
|||
}, |
|||
{ |
|||
encoding: nil, |
|||
content: String("hello"), |
|||
want: "hello", |
|||
wantErr: false, |
|||
}, |
|||
{ |
|||
encoding: nil, |
|||
content: nil, |
|||
want: "", |
|||
wantErr: false, |
|||
}, |
|||
{ |
|||
encoding: String("base64"), |
|||
content: String("aGVsbG8="), |
|||
want: "hello", |
|||
wantErr: false, |
|||
}, |
|||
{ |
|||
encoding: String("bad"), |
|||
content: String("aGVsbG8="), |
|||
want: "", |
|||
wantErr: true, |
|||
}, |
|||
} |
|||
|
|||
for _, tt := range tests { |
|||
r := RepositoryContent{Encoding: tt.encoding, Content: tt.content} |
|||
got, err := r.GetContent() |
|||
if err != nil && !tt.wantErr { |
|||
t.Errorf("RepositoryContent(%q, %q) returned unexpected error: %v", tt.encoding, tt.content, err) |
|||
} |
|||
if err == nil && tt.wantErr { |
|||
t.Errorf("RepositoryContent(%q, %q) did not return unexpected error", tt.encoding, tt.content) |
|||
} |
|||
if want := tt.want; got != want { |
|||
t.Errorf("RepositoryContent.GetContent returned %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_GetReadme(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
mux.HandleFunc("/repos/o/r/readme", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{ |
|||
"type": "file", |
|||
"encoding": "base64", |
|||
"size": 5362, |
|||
"name": "README.md", |
|||
"path": "README.md" |
|||
}`) |
|||
}) |
|||
readme, _, err := client.Repositories.GetReadme("o", "r", &RepositoryContentGetOptions{}) |
|||
if err != nil { |
|||
t.Errorf("Repositories.GetReadme returned error: %v", err) |
|||
} |
|||
want := &RepositoryContent{Type: String("file"), Name: String("README.md"), Size: Int(5362), Encoding: String("base64"), Path: String("README.md")} |
|||
if !reflect.DeepEqual(readme, want) { |
|||
t.Errorf("Repositories.GetReadme returned %+v, want %+v", readme, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_DownloadContents_Success(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `[{ |
|||
"type": "file", |
|||
"name": "f", |
|||
"download_url": "`+server.URL+`/download/f" |
|||
}]`) |
|||
}) |
|||
mux.HandleFunc("/download/f", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, "foo") |
|||
}) |
|||
|
|||
r, err := client.Repositories.DownloadContents("o", "r", "d/f", nil) |
|||
if err != nil { |
|||
t.Errorf("Repositories.DownloadContents returned error: %v", err) |
|||
} |
|||
|
|||
bytes, err := ioutil.ReadAll(r) |
|||
if err != nil { |
|||
t.Errorf("Error reading response body: %v", err) |
|||
} |
|||
r.Close() |
|||
|
|||
if got, want := string(bytes), "foo"; got != want { |
|||
t.Errorf("Repositories.DownloadContents returned %v, want %v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_DownloadContents_NoDownloadURL(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `[{ |
|||
"type": "file", |
|||
"name": "f", |
|||
}]`) |
|||
}) |
|||
|
|||
_, err := client.Repositories.DownloadContents("o", "r", "d/f", nil) |
|||
if err == nil { |
|||
t.Errorf("Repositories.DownloadContents did not return expected error") |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_DownloadContents_NoFile(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `[]`) |
|||
}) |
|||
|
|||
_, err := client.Repositories.DownloadContents("o", "r", "d/f", nil) |
|||
if err == nil { |
|||
t.Errorf("Repositories.DownloadContents did not return expected error") |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_GetContents_File(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{ |
|||
"type": "file", |
|||
"encoding": "base64", |
|||
"size": 20678, |
|||
"name": "LICENSE", |
|||
"path": "LICENSE" |
|||
}`) |
|||
}) |
|||
fileContents, _, _, err := client.Repositories.GetContents("o", "r", "p", &RepositoryContentGetOptions{}) |
|||
if err != nil { |
|||
t.Errorf("Repositories.GetContents returned error: %v", err) |
|||
} |
|||
want := &RepositoryContent{Type: String("file"), Name: String("LICENSE"), Size: Int(20678), Encoding: String("base64"), Path: String("LICENSE")} |
|||
if !reflect.DeepEqual(fileContents, want) { |
|||
t.Errorf("Repositories.GetContents returned %+v, want %+v", fileContents, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_GetContents_FilenameNeedsEscape(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
mux.HandleFunc("/repos/o/r/contents/p#?%/中.go", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{}`) |
|||
}) |
|||
_, _, _, err := client.Repositories.GetContents("o", "r", "p#?%/中.go", &RepositoryContentGetOptions{}) |
|||
if err != nil { |
|||
t.Fatalf("Repositories.GetContents returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_GetContents_DirectoryWithSpaces(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
mux.HandleFunc("/repos/o/r/contents/some directory/file.go", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{}`) |
|||
}) |
|||
_, _, _, err := client.Repositories.GetContents("o", "r", "some directory/file.go", &RepositoryContentGetOptions{}) |
|||
if err != nil { |
|||
t.Fatalf("Repositories.GetContents returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_GetContents_DirectoryWithPlusChars(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
mux.HandleFunc("/repos/o/r/contents/some directory+name/file.go", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{}`) |
|||
}) |
|||
_, _, _, err := client.Repositories.GetContents("o", "r", "some directory+name/file.go", &RepositoryContentGetOptions{}) |
|||
if err != nil { |
|||
t.Fatalf("Repositories.GetContents returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_GetContents_Directory(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `[{ |
|||
"type": "dir", |
|||
"name": "lib", |
|||
"path": "lib" |
|||
}, |
|||
{ |
|||
"type": "file", |
|||
"size": 20678, |
|||
"name": "LICENSE", |
|||
"path": "LICENSE" |
|||
}]`) |
|||
}) |
|||
_, directoryContents, _, err := client.Repositories.GetContents("o", "r", "p", &RepositoryContentGetOptions{}) |
|||
if err != nil { |
|||
t.Errorf("Repositories.GetContents returned error: %v", err) |
|||
} |
|||
want := []*RepositoryContent{{Type: String("dir"), Name: String("lib"), Path: String("lib")}, |
|||
{Type: String("file"), Name: String("LICENSE"), Size: Int(20678), Path: String("LICENSE")}} |
|||
if !reflect.DeepEqual(directoryContents, want) { |
|||
t.Errorf("Repositories.GetContents_Directory returned %+v, want %+v", directoryContents, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_CreateFile(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "PUT") |
|||
fmt.Fprint(w, `{ |
|||
"content":{ |
|||
"name":"p" |
|||
}, |
|||
"commit":{ |
|||
"message":"m", |
|||
"sha":"f5f369044773ff9c6383c087466d12adb6fa0828" |
|||
} |
|||
}`) |
|||
}) |
|||
message := "m" |
|||
content := []byte("c") |
|||
repositoryContentsOptions := &RepositoryContentFileOptions{ |
|||
Message: &message, |
|||
Content: content, |
|||
Committer: &CommitAuthor{Name: String("n"), Email: String("e")}, |
|||
} |
|||
createResponse, _, err := client.Repositories.CreateFile("o", "r", "p", repositoryContentsOptions) |
|||
if err != nil { |
|||
t.Errorf("Repositories.CreateFile returned error: %v", err) |
|||
} |
|||
want := &RepositoryContentResponse{ |
|||
Content: &RepositoryContent{Name: String("p")}, |
|||
Commit: Commit{ |
|||
Message: String("m"), |
|||
SHA: String("f5f369044773ff9c6383c087466d12adb6fa0828"), |
|||
}, |
|||
} |
|||
if !reflect.DeepEqual(createResponse, want) { |
|||
t.Errorf("Repositories.CreateFile returned %+v, want %+v", createResponse, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_UpdateFile(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "PUT") |
|||
fmt.Fprint(w, `{ |
|||
"content":{ |
|||
"name":"p" |
|||
}, |
|||
"commit":{ |
|||
"message":"m", |
|||
"sha":"f5f369044773ff9c6383c087466d12adb6fa0828" |
|||
} |
|||
}`) |
|||
}) |
|||
message := "m" |
|||
content := []byte("c") |
|||
sha := "f5f369044773ff9c6383c087466d12adb6fa0828" |
|||
repositoryContentsOptions := &RepositoryContentFileOptions{ |
|||
Message: &message, |
|||
Content: content, |
|||
SHA: &sha, |
|||
Committer: &CommitAuthor{Name: String("n"), Email: String("e")}, |
|||
} |
|||
updateResponse, _, err := client.Repositories.UpdateFile("o", "r", "p", repositoryContentsOptions) |
|||
if err != nil { |
|||
t.Errorf("Repositories.UpdateFile returned error: %v", err) |
|||
} |
|||
want := &RepositoryContentResponse{ |
|||
Content: &RepositoryContent{Name: String("p")}, |
|||
Commit: Commit{ |
|||
Message: String("m"), |
|||
SHA: String("f5f369044773ff9c6383c087466d12adb6fa0828"), |
|||
}, |
|||
} |
|||
if !reflect.DeepEqual(updateResponse, want) { |
|||
t.Errorf("Repositories.UpdateFile returned %+v, want %+v", updateResponse, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_DeleteFile(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
mux.HandleFunc("/repos/o/r/contents/p", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
fmt.Fprint(w, `{ |
|||
"content": null, |
|||
"commit":{ |
|||
"message":"m", |
|||
"sha":"f5f369044773ff9c6383c087466d12adb6fa0828" |
|||
} |
|||
}`) |
|||
}) |
|||
message := "m" |
|||
sha := "f5f369044773ff9c6383c087466d12adb6fa0828" |
|||
repositoryContentsOptions := &RepositoryContentFileOptions{ |
|||
Message: &message, |
|||
SHA: &sha, |
|||
Committer: &CommitAuthor{Name: String("n"), Email: String("e")}, |
|||
} |
|||
deleteResponse, _, err := client.Repositories.DeleteFile("o", "r", "p", repositoryContentsOptions) |
|||
if err != nil { |
|||
t.Errorf("Repositories.DeleteFile returned error: %v", err) |
|||
} |
|||
want := &RepositoryContentResponse{ |
|||
Content: nil, |
|||
Commit: Commit{ |
|||
Message: String("m"), |
|||
SHA: String("f5f369044773ff9c6383c087466d12adb6fa0828"), |
|||
}, |
|||
} |
|||
if !reflect.DeepEqual(deleteResponse, want) { |
|||
t.Errorf("Repositories.DeleteFile returned %+v, want %+v", deleteResponse, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_GetArchiveLink(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
mux.HandleFunc("/repos/o/r/tarball", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
http.Redirect(w, r, "http://github.com/a", http.StatusFound) |
|||
}) |
|||
url, resp, err := client.Repositories.GetArchiveLink("o", "r", Tarball, &RepositoryContentGetOptions{}) |
|||
if err != nil { |
|||
t.Errorf("Repositories.GetArchiveLink returned error: %v", err) |
|||
} |
|||
if resp.StatusCode != http.StatusFound { |
|||
t.Errorf("Repositories.GetArchiveLink returned status: %d, want %d", resp.StatusCode, http.StatusFound) |
|||
} |
|||
want := "http://github.com/a" |
|||
if url.String() != want { |
|||
t.Errorf("Repositories.GetArchiveLink returned %+v, want %+v", url.String(), want) |
|||
} |
|||
} |
@ -0,0 +1,179 @@ |
|||
// Copyright 2014 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
) |
|||
|
|||
// Deployment represents a deployment in a repo
|
|||
type Deployment struct { |
|||
URL *string `json:"url,omitempty"` |
|||
ID *int `json:"id,omitempty"` |
|||
SHA *string `json:"sha,omitempty"` |
|||
Ref *string `json:"ref,omitempty"` |
|||
Task *string `json:"task,omitempty"` |
|||
Payload json.RawMessage `json:"payload,omitempty"` |
|||
Environment *string `json:"environment,omitempty"` |
|||
Description *string `json:"description,omitempty"` |
|||
Creator *User `json:"creator,omitempty"` |
|||
CreatedAt *Timestamp `json:"created_at,omitempty"` |
|||
UpdatedAt *Timestamp `json:"pushed_at,omitempty"` |
|||
StatusesURL *string `json:"statuses_url,omitempty"` |
|||
RepositoryURL *string `json:"repository_url,omitempty"` |
|||
} |
|||
|
|||
// DeploymentRequest represents a deployment request
|
|||
type DeploymentRequest struct { |
|||
Ref *string `json:"ref,omitempty"` |
|||
Task *string `json:"task,omitempty"` |
|||
AutoMerge *bool `json:"auto_merge,omitempty"` |
|||
RequiredContexts *[]string `json:"required_contexts,omitempty"` |
|||
Payload *string `json:"payload,omitempty"` |
|||
Environment *string `json:"environment,omitempty"` |
|||
Description *string `json:"description,omitempty"` |
|||
TransientEnvironment *bool `json:"transient_environment,omitempty"` |
|||
ProductionEnvironment *bool `json:"production_environment,omitempty"` |
|||
} |
|||
|
|||
// DeploymentsListOptions specifies the optional parameters to the
|
|||
// RepositoriesService.ListDeployments method.
|
|||
type DeploymentsListOptions struct { |
|||
// SHA of the Deployment.
|
|||
SHA string `url:"sha,omitempty"` |
|||
|
|||
// List deployments for a given ref.
|
|||
Ref string `url:"ref,omitempty"` |
|||
|
|||
// List deployments for a given task.
|
|||
Task string `url:"task,omitempty"` |
|||
|
|||
// List deployments for a given environment.
|
|||
Environment string `url:"environment,omitempty"` |
|||
|
|||
ListOptions |
|||
} |
|||
|
|||
// ListDeployments lists the deployments of a repository.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/repos/deployments/#list-deployments
|
|||
func (s *RepositoriesService) ListDeployments(owner, repo string, opt *DeploymentsListOptions) ([]*Deployment, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/deployments", owner, repo) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
deployments := new([]*Deployment) |
|||
resp, err := s.client.Do(req, deployments) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *deployments, resp, err |
|||
} |
|||
|
|||
// CreateDeployment creates a new deployment for a repository.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/repos/deployments/#create-a-deployment
|
|||
func (s *RepositoriesService) CreateDeployment(owner, repo string, request *DeploymentRequest) (*Deployment, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/deployments", owner, repo) |
|||
|
|||
req, err := s.client.NewRequest("POST", u, request) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when deployment support fully launches
|
|||
req.Header.Set("Accept", mediaTypeDeploymentStatusPreview) |
|||
|
|||
d := new(Deployment) |
|||
resp, err := s.client.Do(req, d) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return d, resp, err |
|||
} |
|||
|
|||
// DeploymentStatus represents the status of a
|
|||
// particular deployment.
|
|||
type DeploymentStatus struct { |
|||
ID *int `json:"id,omitempty"` |
|||
// State is the deployment state.
|
|||
// Possible values are: "pending", "success", "failure", "error", "inactive".
|
|||
State *string `json:"state,omitempty"` |
|||
Creator *User `json:"creator,omitempty"` |
|||
Description *string `json:"description,omitempty"` |
|||
TargetURL *string `json:"target_url,omitempty"` |
|||
CreatedAt *Timestamp `json:"created_at,omitempty"` |
|||
UpdatedAt *Timestamp `json:"pushed_at,omitempty"` |
|||
DeploymentURL *string `json:"deployment_url,omitempty"` |
|||
RepositoryURL *string `json:"repository_url,omitempty"` |
|||
} |
|||
|
|||
// DeploymentStatusRequest represents a deployment request
|
|||
type DeploymentStatusRequest struct { |
|||
State *string `json:"state,omitempty"` |
|||
TargetURL *string `json:"target_url,omitempty"` // Deprecated. Use LogURL instead.
|
|||
LogURL *string `json:"log_url,omitempty"` |
|||
Description *string `json:"description,omitempty"` |
|||
EnvironmentURL *string `json:"environment_url,omitempty"` |
|||
AutoInactive *bool `json:"auto_inactive,omitempty"` |
|||
} |
|||
|
|||
// ListDeploymentStatuses lists the statuses of a given deployment of a repository.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/repos/deployments/#list-deployment-statuses
|
|||
func (s *RepositoriesService) ListDeploymentStatuses(owner, repo string, deployment int, opt *ListOptions) ([]*DeploymentStatus, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/deployments/%v/statuses", owner, repo, deployment) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
statuses := new([]*DeploymentStatus) |
|||
resp, err := s.client.Do(req, statuses) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *statuses, resp, err |
|||
} |
|||
|
|||
// CreateDeploymentStatus creates a new status for a deployment.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/repos/deployments/#create-a-deployment-status
|
|||
func (s *RepositoriesService) CreateDeploymentStatus(owner, repo string, deployment int, request *DeploymentStatusRequest) (*DeploymentStatus, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/deployments/%v/statuses", owner, repo, deployment) |
|||
|
|||
req, err := s.client.NewRequest("POST", u, request) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when deployment support fully launches
|
|||
req.Header.Set("Accept", mediaTypeDeploymentStatusPreview) |
|||
|
|||
d := new(DeploymentStatus) |
|||
resp, err := s.client.Do(req, d) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return d, resp, err |
|||
} |
@ -0,0 +1,118 @@ |
|||
// Copyright 2014 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestRepositoriesService_ListDeployments(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/deployments", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{"environment": "test"}) |
|||
fmt.Fprint(w, `[{"id":1}, {"id":2}]`) |
|||
}) |
|||
|
|||
opt := &DeploymentsListOptions{Environment: "test"} |
|||
deployments, _, err := client.Repositories.ListDeployments("o", "r", opt) |
|||
if err != nil { |
|||
t.Errorf("Repositories.ListDeployments returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Deployment{{ID: Int(1)}, {ID: Int(2)}} |
|||
if !reflect.DeepEqual(deployments, want) { |
|||
t.Errorf("Repositories.ListDeployments returned %+v, want %+v", deployments, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_CreateDeployment(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &DeploymentRequest{Ref: String("1111"), Task: String("deploy"), TransientEnvironment: Bool(true)} |
|||
|
|||
mux.HandleFunc("/repos/o/r/deployments", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(DeploymentRequest) |
|||
json.NewDecoder(r.Body).Decode(v) |
|||
|
|||
testMethod(t, r, "POST") |
|||
testHeader(t, r, "Accept", mediaTypeDeploymentStatusPreview) |
|||
if !reflect.DeepEqual(v, input) { |
|||
t.Errorf("Request body = %+v, want %+v", v, input) |
|||
} |
|||
|
|||
fmt.Fprint(w, `{"ref": "1111", "task": "deploy"}`) |
|||
}) |
|||
|
|||
deployment, _, err := client.Repositories.CreateDeployment("o", "r", input) |
|||
if err != nil { |
|||
t.Errorf("Repositories.CreateDeployment returned error: %v", err) |
|||
} |
|||
|
|||
want := &Deployment{Ref: String("1111"), Task: String("deploy")} |
|||
if !reflect.DeepEqual(deployment, want) { |
|||
t.Errorf("Repositories.CreateDeployment returned %+v, want %+v", deployment, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_ListDeploymentStatuses(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/deployments/1/statuses", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{"page": "2"}) |
|||
fmt.Fprint(w, `[{"id":1}, {"id":2}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 2} |
|||
statutses, _, err := client.Repositories.ListDeploymentStatuses("o", "r", 1, opt) |
|||
if err != nil { |
|||
t.Errorf("Repositories.ListDeploymentStatuses returned error: %v", err) |
|||
} |
|||
|
|||
want := []*DeploymentStatus{{ID: Int(1)}, {ID: Int(2)}} |
|||
if !reflect.DeepEqual(statutses, want) { |
|||
t.Errorf("Repositories.ListDeploymentStatuses returned %+v, want %+v", statutses, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_CreateDeploymentStatus(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &DeploymentStatusRequest{State: String("inactive"), Description: String("deploy"), AutoInactive: Bool(false)} |
|||
|
|||
mux.HandleFunc("/repos/o/r/deployments/1/statuses", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(DeploymentStatusRequest) |
|||
json.NewDecoder(r.Body).Decode(v) |
|||
|
|||
testMethod(t, r, "POST") |
|||
testHeader(t, r, "Accept", mediaTypeDeploymentStatusPreview) |
|||
if !reflect.DeepEqual(v, input) { |
|||
t.Errorf("Request body = %+v, want %+v", v, input) |
|||
} |
|||
|
|||
fmt.Fprint(w, `{"state": "inactive", "description": "deploy"}`) |
|||
}) |
|||
|
|||
deploymentStatus, _, err := client.Repositories.CreateDeploymentStatus("o", "r", 1, input) |
|||
if err != nil { |
|||
t.Errorf("Repositories.CreateDeploymentStatus returned error: %v", err) |
|||
} |
|||
|
|||
want := &DeploymentStatus{State: String("inactive"), Description: String("deploy")} |
|||
if !reflect.DeepEqual(deploymentStatus, want) { |
|||
t.Errorf("Repositories.CreateDeploymentStatus returned %+v, want %+v", deploymentStatus, want) |
|||
} |
|||
} |
@ -0,0 +1,73 @@ |
|||
// Copyright 2013 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 "fmt" |
|||
|
|||
// RepositoryListForksOptions specifies the optional parameters to the
|
|||
// RepositoriesService.ListForks method.
|
|||
type RepositoryListForksOptions struct { |
|||
// How to sort the forks list. Possible values are: newest, oldest,
|
|||
// watchers. Default is "newest".
|
|||
Sort string `url:"sort,omitempty"` |
|||
|
|||
ListOptions |
|||
} |
|||
|
|||
// ListForks lists the forks of the specified repository.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/forks/#list-forks
|
|||
func (s *RepositoriesService) ListForks(owner, repo string, opt *RepositoryListForksOptions) ([]*Repository, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/forks", owner, repo) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
repos := new([]*Repository) |
|||
resp, err := s.client.Do(req, repos) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *repos, resp, err |
|||
} |
|||
|
|||
// RepositoryCreateForkOptions specifies the optional parameters to the
|
|||
// RepositoriesService.CreateFork method.
|
|||
type RepositoryCreateForkOptions struct { |
|||
// The organization to fork the repository into.
|
|||
Organization string `url:"organization,omitempty"` |
|||
} |
|||
|
|||
// CreateFork creates a fork of the specified repository.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/forks/#list-forks
|
|||
func (s *RepositoriesService) CreateFork(owner, repo string, opt *RepositoryCreateForkOptions) (*Repository, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/forks", owner, repo) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("POST", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
fork := new(Repository) |
|||
resp, err := s.client.Do(req, fork) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return fork, resp, err |
|||
} |
@ -0,0 +1,73 @@ |
|||
// Copyright 2013 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 ( |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestRepositoriesService_ListForks(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/forks", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{ |
|||
"sort": "newest", |
|||
"page": "3", |
|||
}) |
|||
fmt.Fprint(w, `[{"id":1},{"id":2}]`) |
|||
}) |
|||
|
|||
opt := &RepositoryListForksOptions{ |
|||
Sort: "newest", |
|||
ListOptions: ListOptions{Page: 3}, |
|||
} |
|||
repos, _, err := client.Repositories.ListForks("o", "r", opt) |
|||
if err != nil { |
|||
t.Errorf("Repositories.ListForks returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Repository{{ID: Int(1)}, {ID: Int(2)}} |
|||
if !reflect.DeepEqual(repos, want) { |
|||
t.Errorf("Repositories.ListForks returned %+v, want %+v", repos, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_ListForks_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Repositories.ListForks("%", "r", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestRepositoriesService_CreateFork(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/forks", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "POST") |
|||
testFormValues(t, r, values{"organization": "o"}) |
|||
fmt.Fprint(w, `{"id":1}`) |
|||
}) |
|||
|
|||
opt := &RepositoryCreateForkOptions{Organization: "o"} |
|||
repo, _, err := client.Repositories.CreateFork("o", "r", opt) |
|||
if err != nil { |
|||
t.Errorf("Repositories.CreateFork returned error: %v", err) |
|||
} |
|||
|
|||
want := &Repository{ID: Int(1)} |
|||
if !reflect.DeepEqual(repo, want) { |
|||
t.Errorf("Repositories.CreateFork returned %+v, want %+v", repo, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_CreateFork_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Repositories.CreateFork("%", "r", nil) |
|||
testURLParseError(t, err) |
|||
} |
@ -0,0 +1,196 @@ |
|||
// Copyright 2013 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 ( |
|||
"fmt" |
|||
"time" |
|||
) |
|||
|
|||
// WebHookPayload represents the data that is received from GitHub when a push
|
|||
// event hook is triggered. The format of these payloads pre-date most of the
|
|||
// GitHub v3 API, so there are lots of minor incompatibilities with the types
|
|||
// defined in the rest of the API. Therefore, several types are duplicated
|
|||
// here to account for these differences.
|
|||
//
|
|||
// GitHub API docs: https://help.github.com/articles/post-receive-hooks
|
|||
type WebHookPayload struct { |
|||
After *string `json:"after,omitempty"` |
|||
Before *string `json:"before,omitempty"` |
|||
Commits []WebHookCommit `json:"commits,omitempty"` |
|||
Compare *string `json:"compare,omitempty"` |
|||
Created *bool `json:"created,omitempty"` |
|||
Deleted *bool `json:"deleted,omitempty"` |
|||
Forced *bool `json:"forced,omitempty"` |
|||
HeadCommit *WebHookCommit `json:"head_commit,omitempty"` |
|||
Pusher *User `json:"pusher,omitempty"` |
|||
Ref *string `json:"ref,omitempty"` |
|||
Repo *Repository `json:"repository,omitempty"` |
|||
Sender *User `json:"sender,omitempty"` |
|||
} |
|||
|
|||
func (w WebHookPayload) String() string { |
|||
return Stringify(w) |
|||
} |
|||
|
|||
// WebHookCommit represents the commit variant we receive from GitHub in a
|
|||
// WebHookPayload.
|
|||
type WebHookCommit struct { |
|||
Added []string `json:"added,omitempty"` |
|||
Author *WebHookAuthor `json:"author,omitempty"` |
|||
Committer *WebHookAuthor `json:"committer,omitempty"` |
|||
Distinct *bool `json:"distinct,omitempty"` |
|||
ID *string `json:"id,omitempty"` |
|||
Message *string `json:"message,omitempty"` |
|||
Modified []string `json:"modified,omitempty"` |
|||
Removed []string `json:"removed,omitempty"` |
|||
Timestamp *time.Time `json:"timestamp,omitempty"` |
|||
} |
|||
|
|||
func (w WebHookCommit) String() string { |
|||
return Stringify(w) |
|||
} |
|||
|
|||
// WebHookAuthor represents the author or committer of a commit, as specified
|
|||
// in a WebHookCommit. The commit author may not correspond to a GitHub User.
|
|||
type WebHookAuthor struct { |
|||
Email *string `json:"email,omitempty"` |
|||
Name *string `json:"name,omitempty"` |
|||
Username *string `json:"username,omitempty"` |
|||
} |
|||
|
|||
func (w WebHookAuthor) String() string { |
|||
return Stringify(w) |
|||
} |
|||
|
|||
// Hook represents a GitHub (web and service) hook for a repository.
|
|||
type Hook struct { |
|||
CreatedAt *time.Time `json:"created_at,omitempty"` |
|||
UpdatedAt *time.Time `json:"updated_at,omitempty"` |
|||
Name *string `json:"name,omitempty"` |
|||
URL *string `json:"url,omitempty"` |
|||
Events []string `json:"events,omitempty"` |
|||
Active *bool `json:"active,omitempty"` |
|||
Config map[string]interface{} `json:"config,omitempty"` |
|||
ID *int `json:"id,omitempty"` |
|||
} |
|||
|
|||
func (h Hook) String() string { |
|||
return Stringify(h) |
|||
} |
|||
|
|||
// CreateHook creates a Hook for the specified repository.
|
|||
// Name and Config are required fields.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/hooks/#create-a-hook
|
|||
func (s *RepositoriesService) CreateHook(owner, repo string, hook *Hook) (*Hook, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/hooks", owner, repo) |
|||
req, err := s.client.NewRequest("POST", u, hook) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
h := new(Hook) |
|||
resp, err := s.client.Do(req, h) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return h, resp, err |
|||
} |
|||
|
|||
// ListHooks lists all Hooks for the specified repository.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/hooks/#list
|
|||
func (s *RepositoriesService) ListHooks(owner, repo string, opt *ListOptions) ([]*Hook, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/hooks", owner, repo) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
hooks := new([]*Hook) |
|||
resp, err := s.client.Do(req, hooks) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *hooks, resp, err |
|||
} |
|||
|
|||
// GetHook returns a single specified Hook.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/hooks/#get-single-hook
|
|||
func (s *RepositoriesService) GetHook(owner, repo string, id int) (*Hook, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/hooks/%d", owner, repo, id) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
hook := new(Hook) |
|||
resp, err := s.client.Do(req, hook) |
|||
return hook, resp, err |
|||
} |
|||
|
|||
// EditHook updates a specified Hook.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/hooks/#edit-a-hook
|
|||
func (s *RepositoriesService) EditHook(owner, repo string, id int, hook *Hook) (*Hook, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/hooks/%d", owner, repo, id) |
|||
req, err := s.client.NewRequest("PATCH", u, hook) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
h := new(Hook) |
|||
resp, err := s.client.Do(req, h) |
|||
return h, resp, err |
|||
} |
|||
|
|||
// DeleteHook deletes a specified Hook.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/hooks/#delete-a-hook
|
|||
func (s *RepositoriesService) DeleteHook(owner, repo string, id int) (*Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/hooks/%d", owner, repo, id) |
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return s.client.Do(req, nil) |
|||
} |
|||
|
|||
// PingHook triggers a 'ping' event to be sent to the Hook.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/repos/hooks/#ping-a-hook
|
|||
func (s *RepositoriesService) PingHook(owner, repo string, id int) (*Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/hooks/%d/pings", owner, repo, id) |
|||
req, err := s.client.NewRequest("POST", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return s.client.Do(req, nil) |
|||
} |
|||
|
|||
// TestHook triggers a test Hook by github.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/hooks/#test-a-push-hook
|
|||
func (s *RepositoriesService) TestHook(owner, repo string, id int) (*Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/hooks/%d/tests", owner, repo, id) |
|||
req, err := s.client.NewRequest("POST", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return s.client.Do(req, nil) |
|||
} |
|||
|
|||
// ListServiceHooks is deprecated. Use Client.ListServiceHooks instead.
|
|||
func (s *RepositoriesService) ListServiceHooks() ([]*ServiceHook, *Response, error) { |
|||
return s.client.ListServiceHooks() |
|||
} |
@ -0,0 +1,187 @@ |
|||
// Copyright 2013 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestRepositoriesService_CreateHook(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &Hook{Name: String("t")} |
|||
|
|||
mux.HandleFunc("/repos/o/r/hooks", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(Hook) |
|||
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}`) |
|||
}) |
|||
|
|||
hook, _, err := client.Repositories.CreateHook("o", "r", input) |
|||
if err != nil { |
|||
t.Errorf("Repositories.CreateHook returned error: %v", err) |
|||
} |
|||
|
|||
want := &Hook{ID: Int(1)} |
|||
if !reflect.DeepEqual(hook, want) { |
|||
t.Errorf("Repositories.CreateHook returned %+v, want %+v", hook, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_CreateHook_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Repositories.CreateHook("%", "%", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestRepositoriesService_ListHooks(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/hooks", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{"page": "2"}) |
|||
fmt.Fprint(w, `[{"id":1}, {"id":2}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 2} |
|||
|
|||
hooks, _, err := client.Repositories.ListHooks("o", "r", opt) |
|||
if err != nil { |
|||
t.Errorf("Repositories.ListHooks returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Hook{{ID: Int(1)}, {ID: Int(2)}} |
|||
if !reflect.DeepEqual(hooks, want) { |
|||
t.Errorf("Repositories.ListHooks returned %+v, want %+v", hooks, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_ListHooks_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Repositories.ListHooks("%", "%", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestRepositoriesService_GetHook(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/hooks/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{"id":1}`) |
|||
}) |
|||
|
|||
hook, _, err := client.Repositories.GetHook("o", "r", 1) |
|||
if err != nil { |
|||
t.Errorf("Repositories.GetHook returned error: %v", err) |
|||
} |
|||
|
|||
want := &Hook{ID: Int(1)} |
|||
if !reflect.DeepEqual(hook, want) { |
|||
t.Errorf("Repositories.GetHook returned %+v, want %+v", hook, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_GetHook_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Repositories.GetHook("%", "%", 1) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestRepositoriesService_EditHook(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &Hook{Name: String("t")} |
|||
|
|||
mux.HandleFunc("/repos/o/r/hooks/1", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(Hook) |
|||
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}`) |
|||
}) |
|||
|
|||
hook, _, err := client.Repositories.EditHook("o", "r", 1, input) |
|||
if err != nil { |
|||
t.Errorf("Repositories.EditHook returned error: %v", err) |
|||
} |
|||
|
|||
want := &Hook{ID: Int(1)} |
|||
if !reflect.DeepEqual(hook, want) { |
|||
t.Errorf("Repositories.EditHook returned %+v, want %+v", hook, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_EditHook_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Repositories.EditHook("%", "%", 1, nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestRepositoriesService_DeleteHook(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/hooks/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
}) |
|||
|
|||
_, err := client.Repositories.DeleteHook("o", "r", 1) |
|||
if err != nil { |
|||
t.Errorf("Repositories.DeleteHook returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_DeleteHook_invalidOwner(t *testing.T) { |
|||
_, err := client.Repositories.DeleteHook("%", "%", 1) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestRepositoriesService_PingHook(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/hooks/1/pings", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "POST") |
|||
}) |
|||
|
|||
_, err := client.Repositories.PingHook("o", "r", 1) |
|||
if err != nil { |
|||
t.Errorf("Repositories.PingHook returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_TestHook(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/hooks/1/tests", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "POST") |
|||
}) |
|||
|
|||
_, err := client.Repositories.TestHook("o", "r", 1) |
|||
if err != nil { |
|||
t.Errorf("Repositories.TestHook returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_TestHook_invalidOwner(t *testing.T) { |
|||
_, err := client.Repositories.TestHook("%", "%", 1) |
|||
testURLParseError(t, err) |
|||
} |
@ -0,0 +1,91 @@ |
|||
// Copyright 2016 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 "fmt" |
|||
|
|||
// RepositoryInvitation represents an invitation to collaborate on a repo.
|
|||
type RepositoryInvitation struct { |
|||
ID *int `json:"id,omitempty"` |
|||
Repo *Repository `json:"repository,omitempty"` |
|||
Invitee *User `json:"invitee,omitempty"` |
|||
Inviter *User `json:"inviter,omitempty"` |
|||
|
|||
// Permissions represents the permissions that the associated user will have
|
|||
// on the repository. Possible values are: "read", "write", "admin".
|
|||
Permissions *string `json:"permissions,omitempty"` |
|||
CreatedAt *Timestamp `json:"created_at,omitempty"` |
|||
URL *string `json:"url,omitempty"` |
|||
HTMLURL *string `json:"html_url,omitempty"` |
|||
} |
|||
|
|||
// ListInvitations lists all currently-open repository invitations.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/repos/invitations/#list-invitations-for-a-repository
|
|||
func (s *RepositoriesService) ListInvitations(repoID int, opt *ListOptions) ([]*RepositoryInvitation, *Response, error) { |
|||
u := fmt.Sprintf("repositories/%v/invitations", repoID) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeRepositoryInvitationsPreview) |
|||
|
|||
invites := []*RepositoryInvitation{} |
|||
resp, err := s.client.Do(req, &invites) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return invites, resp, err |
|||
} |
|||
|
|||
// DeleteInvitation deletes a repository invitation.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/repos/invitations/#delete-a-repository-invitation
|
|||
func (s *RepositoriesService) DeleteInvitation(repoID, invitationID int) (*Response, error) { |
|||
u := fmt.Sprintf("repositories/%v/invitations/%v", repoID, invitationID) |
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeRepositoryInvitationsPreview) |
|||
|
|||
return s.client.Do(req, nil) |
|||
} |
|||
|
|||
// UpdateInvitation updates the permissions associated with a repository
|
|||
// invitation.
|
|||
//
|
|||
// permissions represents the permissions that the associated user will have
|
|||
// on the repository. Possible values are: "read", "write", "admin".
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/repos/invitations/#update-a-repository-invitation
|
|||
func (s *RepositoriesService) UpdateInvitation(repoID, invitationID int, permissions string) (*RepositoryInvitation, *Response, error) { |
|||
opts := &struct { |
|||
Permissions string `json:"permissions"` |
|||
}{Permissions: permissions} |
|||
u := fmt.Sprintf("repositories/%v/invitations/%v", repoID, invitationID) |
|||
req, err := s.client.NewRequest("PATCH", u, opts) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypeRepositoryInvitationsPreview) |
|||
|
|||
invite := &RepositoryInvitation{} |
|||
resp, err := s.client.Do(req, invite) |
|||
return invite, resp, err |
|||
} |
@ -0,0 +1,73 @@ |
|||
// Copyright 2016 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 ( |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestRepositoriesService_ListInvitations(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repositories/1/invitations", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypeRepositoryInvitationsPreview) |
|||
testFormValues(t, r, values{"page": "2"}) |
|||
fmt.Fprintf(w, `[{"id":1}, {"id":2}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 2} |
|||
got, _, err := client.Repositories.ListInvitations(1, opt) |
|||
if err != nil { |
|||
t.Errorf("Repositories.ListInvitations returned error: %v", err) |
|||
} |
|||
|
|||
want := []*RepositoryInvitation{{ID: Int(1)}, {ID: Int(2)}} |
|||
if !reflect.DeepEqual(got, want) { |
|||
t.Errorf("Repositories.ListInvitations = %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_DeleteInvitation(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repositories/1/invitations/2", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
testHeader(t, r, "Accept", mediaTypeRepositoryInvitationsPreview) |
|||
w.WriteHeader(http.StatusNoContent) |
|||
}) |
|||
|
|||
_, err := client.Repositories.DeleteInvitation(1, 2) |
|||
if err != nil { |
|||
t.Errorf("Repositories.DeleteInvitation returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_UpdateInvitation(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repositories/1/invitations/2", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "PATCH") |
|||
testHeader(t, r, "Accept", mediaTypeRepositoryInvitationsPreview) |
|||
fmt.Fprintf(w, `{"id":1}`) |
|||
}) |
|||
|
|||
got, _, err := client.Repositories.UpdateInvitation(1, 2, "write") |
|||
if err != nil { |
|||
t.Errorf("Repositories.UpdateInvitation returned error: %v", err) |
|||
} |
|||
|
|||
want := &RepositoryInvitation{ID: Int(1)} |
|||
if !reflect.DeepEqual(got, want) { |
|||
t.Errorf("Repositories.UpdateInvitation = %+v, want %+v", got, want) |
|||
} |
|||
} |
@ -0,0 +1,108 @@ |
|||
// Copyright 2013 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 "fmt" |
|||
|
|||
// The Key type is defined in users_keys.go
|
|||
|
|||
// ListKeys lists the deploy keys for a repository.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/keys/#list
|
|||
func (s *RepositoriesService) ListKeys(owner string, repo string, opt *ListOptions) ([]*Key, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/keys", owner, repo) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
keys := new([]*Key) |
|||
resp, err := s.client.Do(req, keys) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return *keys, resp, err |
|||
} |
|||
|
|||
// GetKey fetches a single deploy key.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/keys/#get
|
|||
func (s *RepositoriesService) GetKey(owner string, repo string, id int) (*Key, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/keys/%v", owner, repo, id) |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
key := new(Key) |
|||
resp, err := s.client.Do(req, key) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return key, resp, err |
|||
} |
|||
|
|||
// CreateKey adds a deploy key for a repository.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/keys/#create
|
|||
func (s *RepositoriesService) CreateKey(owner string, repo string, key *Key) (*Key, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/keys", owner, repo) |
|||
|
|||
req, err := s.client.NewRequest("POST", u, key) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
k := new(Key) |
|||
resp, err := s.client.Do(req, k) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return k, resp, err |
|||
} |
|||
|
|||
// EditKey edits a deploy key.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/keys/#edit
|
|||
func (s *RepositoriesService) EditKey(owner string, repo string, id int, key *Key) (*Key, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/keys/%v", owner, repo, id) |
|||
|
|||
req, err := s.client.NewRequest("PATCH", u, key) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
k := new(Key) |
|||
resp, err := s.client.Do(req, k) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return k, resp, err |
|||
} |
|||
|
|||
// DeleteKey deletes a deploy key.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/keys/#delete
|
|||
func (s *RepositoriesService) DeleteKey(owner string, repo string, id int) (*Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/keys/%v", owner, repo, id) |
|||
|
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
return s.client.Do(req, nil) |
|||
} |
@ -0,0 +1,153 @@ |
|||
// Copyright 2013 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestRepositoriesService_ListKeys(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/keys", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{"page": "2"}) |
|||
fmt.Fprint(w, `[{"id":1}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 2} |
|||
keys, _, err := client.Repositories.ListKeys("o", "r", opt) |
|||
if err != nil { |
|||
t.Errorf("Repositories.ListKeys returned error: %v", err) |
|||
} |
|||
|
|||
want := []*Key{{ID: Int(1)}} |
|||
if !reflect.DeepEqual(keys, want) { |
|||
t.Errorf("Repositories.ListKeys returned %+v, want %+v", keys, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_ListKeys_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Repositories.ListKeys("%", "%", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestRepositoriesService_GetKey(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/keys/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{"id":1}`) |
|||
}) |
|||
|
|||
key, _, err := client.Repositories.GetKey("o", "r", 1) |
|||
if err != nil { |
|||
t.Errorf("Repositories.GetKey returned error: %v", err) |
|||
} |
|||
|
|||
want := &Key{ID: Int(1)} |
|||
if !reflect.DeepEqual(key, want) { |
|||
t.Errorf("Repositories.GetKey returned %+v, want %+v", key, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_GetKey_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Repositories.GetKey("%", "%", 1) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestRepositoriesService_CreateKey(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &Key{Key: String("k"), Title: String("t")} |
|||
|
|||
mux.HandleFunc("/repos/o/r/keys", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(Key) |
|||
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}`) |
|||
}) |
|||
|
|||
key, _, err := client.Repositories.CreateKey("o", "r", input) |
|||
if err != nil { |
|||
t.Errorf("Repositories.GetKey returned error: %v", err) |
|||
} |
|||
|
|||
want := &Key{ID: Int(1)} |
|||
if !reflect.DeepEqual(key, want) { |
|||
t.Errorf("Repositories.GetKey returned %+v, want %+v", key, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_CreateKey_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Repositories.CreateKey("%", "%", nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestRepositoriesService_EditKey(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &Key{Key: String("k"), Title: String("t")} |
|||
|
|||
mux.HandleFunc("/repos/o/r/keys/1", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(Key) |
|||
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}`) |
|||
}) |
|||
|
|||
key, _, err := client.Repositories.EditKey("o", "r", 1, input) |
|||
if err != nil { |
|||
t.Errorf("Repositories.EditKey returned error: %v", err) |
|||
} |
|||
|
|||
want := &Key{ID: Int(1)} |
|||
if !reflect.DeepEqual(key, want) { |
|||
t.Errorf("Repositories.EditKey returned %+v, want %+v", key, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_EditKey_invalidOwner(t *testing.T) { |
|||
_, _, err := client.Repositories.EditKey("%", "%", 1, nil) |
|||
testURLParseError(t, err) |
|||
} |
|||
|
|||
func TestRepositoriesService_DeleteKey(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/keys/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
}) |
|||
|
|||
_, err := client.Repositories.DeleteKey("o", "r", 1) |
|||
if err != nil { |
|||
t.Errorf("Repositories.DeleteKey returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_DeleteKey_invalidOwner(t *testing.T) { |
|||
_, err := client.Repositories.DeleteKey("%", "%", 1) |
|||
testURLParseError(t, err) |
|||
} |
@ -0,0 +1,37 @@ |
|||
// Copyright 2014 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 ( |
|||
"fmt" |
|||
) |
|||
|
|||
// RepositoryMergeRequest represents a request to merge a branch in a
|
|||
// repository.
|
|||
type RepositoryMergeRequest struct { |
|||
Base *string `json:"base,omitempty"` |
|||
Head *string `json:"head,omitempty"` |
|||
CommitMessage *string `json:"commit_message,omitempty"` |
|||
} |
|||
|
|||
// Merge a branch in the specified repository.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/repos/merging/#perform-a-merge
|
|||
func (s *RepositoriesService) Merge(owner, repo string, request *RepositoryMergeRequest) (*RepositoryCommit, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/merges", owner, repo) |
|||
req, err := s.client.NewRequest("POST", u, request) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
commit := new(RepositoryCommit) |
|||
resp, err := s.client.Do(req, commit) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return commit, resp, err |
|||
} |
@ -0,0 +1,47 @@ |
|||
// Copyright 2014 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 ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestRepositoriesService_Merge(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &RepositoryMergeRequest{ |
|||
Base: String("b"), |
|||
Head: String("h"), |
|||
CommitMessage: String("c"), |
|||
} |
|||
|
|||
mux.HandleFunc("/repos/o/r/merges", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(RepositoryMergeRequest) |
|||
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, `{"sha":"s"}`) |
|||
}) |
|||
|
|||
commit, _, err := client.Repositories.Merge("o", "r", input) |
|||
if err != nil { |
|||
t.Errorf("Repositories.Merge returned error: %v", err) |
|||
} |
|||
|
|||
want := &RepositoryCommit{SHA: String("s")} |
|||
if !reflect.DeepEqual(commit, want) { |
|||
t.Errorf("Repositories.Merge returned %+v, want %+v", commit, want) |
|||
} |
|||
} |
@ -0,0 +1,116 @@ |
|||
// Copyright 2014 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 "fmt" |
|||
|
|||
// Pages represents a GitHub Pages site configuration.
|
|||
type Pages struct { |
|||
URL *string `json:"url,omitempty"` |
|||
Status *string `json:"status,omitempty"` |
|||
CNAME *string `json:"cname,omitempty"` |
|||
Custom404 *bool `json:"custom_404,omitempty"` |
|||
HTMLURL *string `json:"html_url,omitempty"` |
|||
} |
|||
|
|||
// PagesError represents a build error for a GitHub Pages site.
|
|||
type PagesError struct { |
|||
Message *string `json:"message,omitempty"` |
|||
} |
|||
|
|||
// PagesBuild represents the build information for a GitHub Pages site.
|
|||
type PagesBuild struct { |
|||
URL *string `json:"url,omitempty"` |
|||
Status *string `json:"status,omitempty"` |
|||
Error *PagesError `json:"error,omitempty"` |
|||
Pusher *User `json:"pusher,omitempty"` |
|||
Commit *string `json:"commit,omitempty"` |
|||
Duration *int `json:"duration,omitempty"` |
|||
CreatedAt *Timestamp `json:"created_at,omitempty"` |
|||
UpdatedAt *Timestamp `json:"created_at,omitempty"` |
|||
} |
|||
|
|||
// GetPagesInfo fetches information about a GitHub Pages site.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/repos/pages/#get-information-about-a-pages-site
|
|||
func (s *RepositoriesService) GetPagesInfo(owner string, repo string) (*Pages, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/pages", owner, repo) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypePagesPreview) |
|||
|
|||
site := new(Pages) |
|||
resp, err := s.client.Do(req, site) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return site, resp, err |
|||
} |
|||
|
|||
// ListPagesBuilds lists the builds for a GitHub Pages site.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/repos/pages/#list-pages-builds
|
|||
func (s *RepositoriesService) ListPagesBuilds(owner string, repo string) ([]*PagesBuild, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/pages/builds", owner, repo) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
var pages []*PagesBuild |
|||
resp, err := s.client.Do(req, &pages) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return pages, resp, err |
|||
} |
|||
|
|||
// GetLatestPagesBuild fetches the latest build information for a GitHub pages site.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/repos/pages/#list-latest-pages-build
|
|||
func (s *RepositoriesService) GetLatestPagesBuild(owner string, repo string) (*PagesBuild, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/pages/builds/latest", owner, repo) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
build := new(PagesBuild) |
|||
resp, err := s.client.Do(req, build) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return build, resp, err |
|||
} |
|||
|
|||
// RequestPageBuild requests a build of a GitHub Pages site without needing to push new commit.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/repos/pages/#request-a-page-build
|
|||
func (s *RepositoriesService) RequestPageBuild(owner string, repo string) (*PagesBuild, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/pages/builds", owner, repo) |
|||
req, err := s.client.NewRequest("POST", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
// TODO: remove custom Accept header when this API fully launches.
|
|||
req.Header.Set("Accept", mediaTypePagesPreview) |
|||
|
|||
build := new(PagesBuild) |
|||
resp, err := s.client.Do(req, build) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return build, resp, err |
|||
} |
@ -0,0 +1,95 @@ |
|||
// Copyright 2014 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 ( |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
) |
|||
|
|||
func TestRepositoriesService_GetPagesInfo(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/pages", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", mediaTypePagesPreview) |
|||
fmt.Fprint(w, `{"url":"u","status":"s","cname":"c","custom_404":false,"html_url":"h"}`) |
|||
}) |
|||
|
|||
page, _, err := client.Repositories.GetPagesInfo("o", "r") |
|||
if err != nil { |
|||
t.Errorf("Repositories.GetPagesInfo returned error: %v", err) |
|||
} |
|||
|
|||
want := &Pages{URL: String("u"), Status: String("s"), CNAME: String("c"), Custom404: Bool(false), HTMLURL: String("h")} |
|||
if !reflect.DeepEqual(page, want) { |
|||
t.Errorf("Repositories.GetPagesInfo returned %+v, want %+v", page, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_ListPagesBuilds(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/pages/builds", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `[{"url":"u","status":"s","commit":"c"}]`) |
|||
}) |
|||
|
|||
pages, _, err := client.Repositories.ListPagesBuilds("o", "r") |
|||
if err != nil { |
|||
t.Errorf("Repositories.ListPagesBuilds returned error: %v", err) |
|||
} |
|||
|
|||
want := []*PagesBuild{{URL: String("u"), Status: String("s"), Commit: String("c")}} |
|||
if !reflect.DeepEqual(pages, want) { |
|||
t.Errorf("Repositories.ListPagesBuilds returned %+v, want %+v", pages, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_GetLatestPagesBuild(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/pages/builds/latest", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{"url":"u","status":"s","commit":"c"}`) |
|||
}) |
|||
|
|||
build, _, err := client.Repositories.GetLatestPagesBuild("o", "r") |
|||
if err != nil { |
|||
t.Errorf("Repositories.GetLatestPagesBuild returned error: %v", err) |
|||
} |
|||
|
|||
want := &PagesBuild{URL: String("u"), Status: String("s"), Commit: String("c")} |
|||
if !reflect.DeepEqual(build, want) { |
|||
t.Errorf("Repositories.GetLatestPagesBuild returned %+v, want %+v", build, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_RequestPageBuild(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/pages/builds", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "POST") |
|||
testHeader(t, r, "Accept", mediaTypePagesPreview) |
|||
fmt.Fprint(w, `{"url":"u","status":"s"}`) |
|||
}) |
|||
|
|||
build, _, err := client.Repositories.RequestPageBuild("o", "r") |
|||
if err != nil { |
|||
t.Errorf("Repositories.RequestPageBuild returned error: %v", err) |
|||
} |
|||
|
|||
want := &PagesBuild{URL: String("u"), Status: String("s")} |
|||
if !reflect.DeepEqual(build, want) { |
|||
t.Errorf("Repositories.RequestPageBuild returned %+v, want %+v", build, want) |
|||
} |
|||
} |
@ -0,0 +1,325 @@ |
|||
// Copyright 2013 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 ( |
|||
"errors" |
|||
"fmt" |
|||
"io" |
|||
"mime" |
|||
"net/http" |
|||
"os" |
|||
"path/filepath" |
|||
"strings" |
|||
) |
|||
|
|||
// RepositoryRelease represents a GitHub release in a repository.
|
|||
type RepositoryRelease struct { |
|||
ID *int `json:"id,omitempty"` |
|||
TagName *string `json:"tag_name,omitempty"` |
|||
TargetCommitish *string `json:"target_commitish,omitempty"` |
|||
Name *string `json:"name,omitempty"` |
|||
Body *string `json:"body,omitempty"` |
|||
Draft *bool `json:"draft,omitempty"` |
|||
Prerelease *bool `json:"prerelease,omitempty"` |
|||
CreatedAt *Timestamp `json:"created_at,omitempty"` |
|||
PublishedAt *Timestamp `json:"published_at,omitempty"` |
|||
URL *string `json:"url,omitempty"` |
|||
HTMLURL *string `json:"html_url,omitempty"` |
|||
AssetsURL *string `json:"assets_url,omitempty"` |
|||
Assets []ReleaseAsset `json:"assets,omitempty"` |
|||
UploadURL *string `json:"upload_url,omitempty"` |
|||
ZipballURL *string `json:"zipball_url,omitempty"` |
|||
TarballURL *string `json:"tarball_url,omitempty"` |
|||
Author *CommitAuthor `json:"author,omitempty"` |
|||
} |
|||
|
|||
func (r RepositoryRelease) String() string { |
|||
return Stringify(r) |
|||
} |
|||
|
|||
// ReleaseAsset represents a Github release asset in a repository.
|
|||
type ReleaseAsset struct { |
|||
ID *int `json:"id,omitempty"` |
|||
URL *string `json:"url,omitempty"` |
|||
Name *string `json:"name,omitempty"` |
|||
Label *string `json:"label,omitempty"` |
|||
State *string `json:"state,omitempty"` |
|||
ContentType *string `json:"content_type,omitempty"` |
|||
Size *int `json:"size,omitempty"` |
|||
DownloadCount *int `json:"download_count,omitempty"` |
|||
CreatedAt *Timestamp `json:"created_at,omitempty"` |
|||
UpdatedAt *Timestamp `json:"updated_at,omitempty"` |
|||
BrowserDownloadURL *string `json:"browser_download_url,omitempty"` |
|||
Uploader *User `json:"uploader,omitempty"` |
|||
} |
|||
|
|||
func (r ReleaseAsset) String() string { |
|||
return Stringify(r) |
|||
} |
|||
|
|||
// ListReleases lists the releases for a repository.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/releases/#list-releases-for-a-repository
|
|||
func (s *RepositoriesService) ListReleases(owner, repo string, opt *ListOptions) ([]*RepositoryRelease, *Response, error) { |
|||
u := fmt.Sprintf("repos/%s/%s/releases", owner, repo) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
releases := new([]*RepositoryRelease) |
|||
resp, err := s.client.Do(req, releases) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
return *releases, resp, err |
|||
} |
|||
|
|||
// GetRelease fetches a single release.
|
|||
//
|
|||
// GitHub API docs: http://developer.github.com/v3/repos/releases/#get-a-single-release
|
|||
func (s *RepositoriesService) GetRelease(owner, repo string, id int) (*RepositoryRelease, *Response, error) { |
|||
u := fmt.Sprintf("repos/%s/%s/releases/%d", owner, repo, id) |
|||
return s.getSingleRelease(u) |
|||
} |
|||
|
|||
// GetLatestRelease fetches the latest published release for the repository.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/repos/releases/#get-the-latest-release
|
|||
func (s *RepositoriesService) GetLatestRelease(owner, repo string) (*RepositoryRelease, *Response, error) { |
|||
u := fmt.Sprintf("repos/%s/%s/releases/latest", owner, repo) |
|||
return s.getSingleRelease(u) |
|||
} |
|||
|
|||
// GetReleaseByTag fetches a release with the specified tag.
|
|||
//
|
|||
// GitHub API docs: https://developer.github.com/v3/repos/releases/#get-a-release-by-tag-name
|
|||
func (s *RepositoriesService) GetReleaseByTag(owner, repo, tag string) (*RepositoryRelease, *Response, error) { |
|||
u := fmt.Sprintf("repos/%s/%s/releases/tags/%s", owner, repo, tag) |
|||
return s.getSingleRelease(u) |
|||
} |
|||
|
|||
func (s *RepositoriesService) getSingleRelease(url string) (*RepositoryRelease, *Response, error) { |
|||
req, err := s.client.NewRequest("GET", url, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
release := new(RepositoryRelease) |
|||
resp, err := s.client.Do(req, release) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
return release, resp, err |
|||
} |
|||
|
|||
// CreateRelease adds a new release for a repository.
|
|||
//
|
|||
// GitHub API docs : http://developer.github.com/v3/repos/releases/#create-a-release
|
|||
func (s *RepositoriesService) CreateRelease(owner, repo string, release *RepositoryRelease) (*RepositoryRelease, *Response, error) { |
|||
u := fmt.Sprintf("repos/%s/%s/releases", owner, repo) |
|||
|
|||
req, err := s.client.NewRequest("POST", u, release) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
r := new(RepositoryRelease) |
|||
resp, err := s.client.Do(req, r) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
return r, resp, err |
|||
} |
|||
|
|||
// EditRelease edits a repository release.
|
|||
//
|
|||
// GitHub API docs : http://developer.github.com/v3/repos/releases/#edit-a-release
|
|||
func (s *RepositoriesService) EditRelease(owner, repo string, id int, release *RepositoryRelease) (*RepositoryRelease, *Response, error) { |
|||
u := fmt.Sprintf("repos/%s/%s/releases/%d", owner, repo, id) |
|||
|
|||
req, err := s.client.NewRequest("PATCH", u, release) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
r := new(RepositoryRelease) |
|||
resp, err := s.client.Do(req, r) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
return r, resp, err |
|||
} |
|||
|
|||
// DeleteRelease delete a single release from a repository.
|
|||
//
|
|||
// GitHub API docs : http://developer.github.com/v3/repos/releases/#delete-a-release
|
|||
func (s *RepositoriesService) DeleteRelease(owner, repo string, id int) (*Response, error) { |
|||
u := fmt.Sprintf("repos/%s/%s/releases/%d", owner, repo, id) |
|||
|
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return s.client.Do(req, nil) |
|||
} |
|||
|
|||
// ListReleaseAssets lists the release's assets.
|
|||
//
|
|||
// GitHub API docs : http://developer.github.com/v3/repos/releases/#list-assets-for-a-release
|
|||
func (s *RepositoriesService) ListReleaseAssets(owner, repo string, id int, opt *ListOptions) ([]*ReleaseAsset, *Response, error) { |
|||
u := fmt.Sprintf("repos/%s/%s/releases/%d/assets", owner, repo, id) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
assets := new([]*ReleaseAsset) |
|||
resp, err := s.client.Do(req, assets) |
|||
if err != nil { |
|||
return nil, resp, nil |
|||
} |
|||
return *assets, resp, err |
|||
} |
|||
|
|||
// GetReleaseAsset fetches a single release asset.
|
|||
//
|
|||
// GitHub API docs : http://developer.github.com/v3/repos/releases/#get-a-single-release-asset
|
|||
func (s *RepositoriesService) GetReleaseAsset(owner, repo string, id int) (*ReleaseAsset, *Response, error) { |
|||
u := fmt.Sprintf("repos/%s/%s/releases/assets/%d", owner, repo, id) |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
asset := new(ReleaseAsset) |
|||
resp, err := s.client.Do(req, asset) |
|||
if err != nil { |
|||
return nil, resp, nil |
|||
} |
|||
return asset, resp, err |
|||
} |
|||
|
|||
// DownloadReleaseAsset downloads a release asset or returns a redirect URL.
|
|||
//
|
|||
// DownloadReleaseAsset returns an io.ReadCloser that reads the contents of the
|
|||
// specified release asset. It is the caller's responsibility to close the ReadCloser.
|
|||
// If a redirect is returned, the redirect URL will be returned as a string instead
|
|||
// of the io.ReadCloser. Exactly one of rc and redirectURL will be zero.
|
|||
//
|
|||
// GitHub API docs : http://developer.github.com/v3/repos/releases/#get-a-single-release-asset
|
|||
func (s *RepositoriesService) DownloadReleaseAsset(owner, repo string, id int) (rc io.ReadCloser, redirectURL string, err error) { |
|||
u := fmt.Sprintf("repos/%s/%s/releases/assets/%d", owner, repo, id) |
|||
|
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, "", err |
|||
} |
|||
req.Header.Set("Accept", defaultMediaType) |
|||
|
|||
s.client.clientMu.Lock() |
|||
defer s.client.clientMu.Unlock() |
|||
|
|||
var loc string |
|||
saveRedirect := s.client.client.CheckRedirect |
|||
s.client.client.CheckRedirect = func(req *http.Request, via []*http.Request) error { |
|||
loc = req.URL.String() |
|||
return errors.New("disable redirect") |
|||
} |
|||
defer func() { s.client.client.CheckRedirect = saveRedirect }() |
|||
|
|||
resp, err := s.client.client.Do(req) |
|||
if err != nil { |
|||
if !strings.Contains(err.Error(), "disable redirect") { |
|||
return nil, "", err |
|||
} |
|||
return nil, loc, nil |
|||
} |
|||
|
|||
if err := CheckResponse(resp); err != nil { |
|||
resp.Body.Close() |
|||
return nil, "", err |
|||
} |
|||
|
|||
return resp.Body, "", nil |
|||
} |
|||
|
|||
// EditReleaseAsset edits a repository release asset.
|
|||
//
|
|||
// GitHub API docs : http://developer.github.com/v3/repos/releases/#edit-a-release-asset
|
|||
func (s *RepositoriesService) EditReleaseAsset(owner, repo string, id int, release *ReleaseAsset) (*ReleaseAsset, *Response, error) { |
|||
u := fmt.Sprintf("repos/%s/%s/releases/assets/%d", owner, repo, id) |
|||
|
|||
req, err := s.client.NewRequest("PATCH", u, release) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
asset := new(ReleaseAsset) |
|||
resp, err := s.client.Do(req, asset) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
return asset, resp, err |
|||
} |
|||
|
|||
// DeleteReleaseAsset delete a single release asset from a repository.
|
|||
//
|
|||
// GitHub API docs : http://developer.github.com/v3/repos/releases/#delete-a-release-asset
|
|||
func (s *RepositoriesService) DeleteReleaseAsset(owner, repo string, id int) (*Response, error) { |
|||
u := fmt.Sprintf("repos/%s/%s/releases/assets/%d", owner, repo, id) |
|||
|
|||
req, err := s.client.NewRequest("DELETE", u, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return s.client.Do(req, nil) |
|||
} |
|||
|
|||
// UploadReleaseAsset creates an asset by uploading a file into a release repository.
|
|||
// To upload assets that cannot be represented by an os.File, call NewUploadRequest directly.
|
|||
//
|
|||
// GitHub API docs : http://developer.github.com/v3/repos/releases/#upload-a-release-asset
|
|||
func (s *RepositoriesService) UploadReleaseAsset(owner, repo string, id int, opt *UploadOptions, file *os.File) (*ReleaseAsset, *Response, error) { |
|||
u := fmt.Sprintf("repos/%s/%s/releases/%d/assets", owner, repo, id) |
|||
u, err := addOptions(u, opt) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
stat, err := file.Stat() |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
if stat.IsDir() { |
|||
return nil, nil, errors.New("the asset to upload can't be a directory") |
|||
} |
|||
|
|||
mediaType := mime.TypeByExtension(filepath.Ext(file.Name())) |
|||
req, err := s.client.NewUploadRequest(u, file, stat.Size(), mediaType) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
asset := new(ReleaseAsset) |
|||
resp, err := s.client.Do(req, asset) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
return asset, resp, err |
|||
} |
@ -0,0 +1,352 @@ |
|||
// Copyright 2013 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 ( |
|||
"bytes" |
|||
"encoding/json" |
|||
"fmt" |
|||
"io/ioutil" |
|||
"net/http" |
|||
"os" |
|||
"reflect" |
|||
"strings" |
|||
"testing" |
|||
) |
|||
|
|||
func TestRepositoriesService_ListReleases(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/releases", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{"page": "2"}) |
|||
fmt.Fprint(w, `[{"id":1}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 2} |
|||
releases, _, err := client.Repositories.ListReleases("o", "r", opt) |
|||
if err != nil { |
|||
t.Errorf("Repositories.ListReleases returned error: %v", err) |
|||
} |
|||
want := []*RepositoryRelease{{ID: Int(1)}} |
|||
if !reflect.DeepEqual(releases, want) { |
|||
t.Errorf("Repositories.ListReleases returned %+v, want %+v", releases, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_GetRelease(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/releases/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{"id":1}`) |
|||
}) |
|||
|
|||
release, resp, err := client.Repositories.GetRelease("o", "r", 1) |
|||
if err != nil { |
|||
t.Errorf("Repositories.GetRelease returned error: %v\n%v", err, resp.Body) |
|||
} |
|||
|
|||
want := &RepositoryRelease{ID: Int(1)} |
|||
if !reflect.DeepEqual(release, want) { |
|||
t.Errorf("Repositories.GetRelease returned %+v, want %+v", release, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_GetLatestRelease(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/releases/latest", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{"id":3}`) |
|||
}) |
|||
|
|||
release, resp, err := client.Repositories.GetLatestRelease("o", "r") |
|||
if err != nil { |
|||
t.Errorf("Repositories.GetLatestRelease returned error: %v\n%v", err, resp.Body) |
|||
} |
|||
|
|||
want := &RepositoryRelease{ID: Int(3)} |
|||
if !reflect.DeepEqual(release, want) { |
|||
t.Errorf("Repositories.GetLatestRelease returned %+v, want %+v", release, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_GetReleaseByTag(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/releases/tags/foo", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{"id":13}`) |
|||
}) |
|||
|
|||
release, resp, err := client.Repositories.GetReleaseByTag("o", "r", "foo") |
|||
if err != nil { |
|||
t.Errorf("Repositories.GetReleaseByTag returned error: %v\n%v", err, resp.Body) |
|||
} |
|||
|
|||
want := &RepositoryRelease{ID: Int(13)} |
|||
if !reflect.DeepEqual(release, want) { |
|||
t.Errorf("Repositories.GetReleaseByTag returned %+v, want %+v", release, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_CreateRelease(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &RepositoryRelease{Name: String("v1.0")} |
|||
|
|||
mux.HandleFunc("/repos/o/r/releases", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(RepositoryRelease) |
|||
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}`) |
|||
}) |
|||
|
|||
release, _, err := client.Repositories.CreateRelease("o", "r", input) |
|||
if err != nil { |
|||
t.Errorf("Repositories.CreateRelease returned error: %v", err) |
|||
} |
|||
|
|||
want := &RepositoryRelease{ID: Int(1)} |
|||
if !reflect.DeepEqual(release, want) { |
|||
t.Errorf("Repositories.CreateRelease returned %+v, want %+v", release, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_EditRelease(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &RepositoryRelease{Name: String("n")} |
|||
|
|||
mux.HandleFunc("/repos/o/r/releases/1", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(RepositoryRelease) |
|||
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}`) |
|||
}) |
|||
|
|||
release, _, err := client.Repositories.EditRelease("o", "r", 1, input) |
|||
if err != nil { |
|||
t.Errorf("Repositories.EditRelease returned error: %v", err) |
|||
} |
|||
want := &RepositoryRelease{ID: Int(1)} |
|||
if !reflect.DeepEqual(release, want) { |
|||
t.Errorf("Repositories.EditRelease returned = %+v, want %+v", release, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_DeleteRelease(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/releases/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
}) |
|||
|
|||
_, err := client.Repositories.DeleteRelease("o", "r", 1) |
|||
if err != nil { |
|||
t.Errorf("Repositories.DeleteRelease returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_ListReleaseAssets(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/releases/1/assets", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testFormValues(t, r, values{"page": "2"}) |
|||
fmt.Fprint(w, `[{"id":1}]`) |
|||
}) |
|||
|
|||
opt := &ListOptions{Page: 2} |
|||
assets, _, err := client.Repositories.ListReleaseAssets("o", "r", 1, opt) |
|||
if err != nil { |
|||
t.Errorf("Repositories.ListReleaseAssets returned error: %v", err) |
|||
} |
|||
want := []*ReleaseAsset{{ID: Int(1)}} |
|||
if !reflect.DeepEqual(assets, want) { |
|||
t.Errorf("Repositories.ListReleaseAssets returned %+v, want %+v", assets, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_GetReleaseAsset(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
fmt.Fprint(w, `{"id":1}`) |
|||
}) |
|||
|
|||
asset, _, err := client.Repositories.GetReleaseAsset("o", "r", 1) |
|||
if err != nil { |
|||
t.Errorf("Repositories.GetReleaseAsset returned error: %v", err) |
|||
} |
|||
want := &ReleaseAsset{ID: Int(1)} |
|||
if !reflect.DeepEqual(asset, want) { |
|||
t.Errorf("Repositories.GetReleaseAsset returned %+v, want %+v", asset, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_DownloadReleaseAsset_Stream(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", defaultMediaType) |
|||
w.Header().Set("Content-Type", "application/octet-stream") |
|||
w.Header().Set("Content-Disposition", "attachment; filename=hello-world.txt") |
|||
fmt.Fprint(w, "Hello World") |
|||
}) |
|||
|
|||
reader, _, err := client.Repositories.DownloadReleaseAsset("o", "r", 1) |
|||
if err != nil { |
|||
t.Errorf("Repositories.DownloadReleaseAsset returned error: %v", err) |
|||
} |
|||
want := []byte("Hello World") |
|||
content, err := ioutil.ReadAll(reader) |
|||
if err != nil { |
|||
t.Errorf("Repositories.DownloadReleaseAsset returned bad reader: %v", err) |
|||
} |
|||
if !bytes.Equal(want, content) { |
|||
t.Errorf("Repositories.DownloadReleaseAsset returned %+v, want %+v", content, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_DownloadReleaseAsset_Redirect(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", defaultMediaType) |
|||
http.Redirect(w, r, "/yo", http.StatusFound) |
|||
}) |
|||
|
|||
_, got, err := client.Repositories.DownloadReleaseAsset("o", "r", 1) |
|||
if err != nil { |
|||
t.Errorf("Repositories.DownloadReleaseAsset returned error: %v", err) |
|||
} |
|||
want := "/yo" |
|||
if !strings.HasSuffix(got, want) { |
|||
t.Errorf("Repositories.DownloadReleaseAsset returned %+v, want %+v", got, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_DownloadReleaseAsset_APIError(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
testHeader(t, r, "Accept", defaultMediaType) |
|||
w.WriteHeader(http.StatusNotFound) |
|||
fmt.Fprint(w, `{"message":"Not Found","documentation_url":"https://developer.github.com/v3"}`) |
|||
}) |
|||
|
|||
resp, loc, err := client.Repositories.DownloadReleaseAsset("o", "r", 1) |
|||
if err == nil { |
|||
t.Error("Repositories.DownloadReleaseAsset did not return an error") |
|||
} |
|||
|
|||
if resp != nil { |
|||
resp.Close() |
|||
t.Error("Repositories.DownloadReleaseAsset returned stream, want nil") |
|||
} |
|||
|
|||
if loc != "" { |
|||
t.Errorf(`Repositories.DownloadReleaseAsset returned "%s", want empty ""`, loc) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_EditReleaseAsset(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
input := &ReleaseAsset{Name: String("n")} |
|||
|
|||
mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) { |
|||
v := new(ReleaseAsset) |
|||
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}`) |
|||
}) |
|||
|
|||
asset, _, err := client.Repositories.EditReleaseAsset("o", "r", 1, input) |
|||
if err != nil { |
|||
t.Errorf("Repositories.EditReleaseAsset returned error: %v", err) |
|||
} |
|||
want := &ReleaseAsset{ID: Int(1)} |
|||
if !reflect.DeepEqual(asset, want) { |
|||
t.Errorf("Repositories.EditReleaseAsset returned = %+v, want %+v", asset, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_DeleteReleaseAsset(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/releases/assets/1", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "DELETE") |
|||
}) |
|||
|
|||
_, err := client.Repositories.DeleteReleaseAsset("o", "r", 1) |
|||
if err != nil { |
|||
t.Errorf("Repositories.DeleteReleaseAsset returned error: %v", err) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_UploadReleaseAsset(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/releases/1/assets", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "POST") |
|||
testHeader(t, r, "Content-Type", "text/plain; charset=utf-8") |
|||
testHeader(t, r, "Content-Length", "12") |
|||
testFormValues(t, r, values{"name": "n"}) |
|||
testBody(t, r, "Upload me !\n") |
|||
|
|||
fmt.Fprintf(w, `{"id":1}`) |
|||
}) |
|||
|
|||
file, dir, err := openTestFile("upload.txt", "Upload me !\n") |
|||
if err != nil { |
|||
t.Fatalf("Unable to create temp file: %v", err) |
|||
} |
|||
defer os.RemoveAll(dir) |
|||
|
|||
opt := &UploadOptions{Name: "n"} |
|||
asset, _, err := client.Repositories.UploadReleaseAsset("o", "r", 1, opt, file) |
|||
if err != nil { |
|||
t.Errorf("Repositories.UploadReleaseAssert returned error: %v", err) |
|||
} |
|||
want := &ReleaseAsset{ID: Int(1)} |
|||
if !reflect.DeepEqual(asset, want) { |
|||
t.Errorf("Repositories.UploadReleaseAssert returned %+v, want %+v", asset, want) |
|||
} |
|||
} |
@ -0,0 +1,214 @@ |
|||
// Copyright 2014 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 ( |
|||
"fmt" |
|||
"time" |
|||
) |
|||
|
|||
// ContributorStats represents a contributor to a repository and their
|
|||
// weekly contributions to a given repo.
|
|||
type ContributorStats struct { |
|||
Author *Contributor `json:"author,omitempty"` |
|||
Total *int `json:"total,omitempty"` |
|||
Weeks []WeeklyStats `json:"weeks,omitempty"` |
|||
} |
|||
|
|||
func (c ContributorStats) String() string { |
|||
return Stringify(c) |
|||
} |
|||
|
|||
// WeeklyStats represents the number of additions, deletions and commits
|
|||
// a Contributor made in a given week.
|
|||
type WeeklyStats struct { |
|||
Week *Timestamp `json:"w,omitempty"` |
|||
Additions *int `json:"a,omitempty"` |
|||
Deletions *int `json:"d,omitempty"` |
|||
Commits *int `json:"c,omitempty"` |
|||
} |
|||
|
|||
func (w WeeklyStats) String() string { |
|||
return Stringify(w) |
|||
} |
|||
|
|||
// ListContributorsStats gets a repo's contributor list with additions,
|
|||
// deletions and commit counts.
|
|||
//
|
|||
// If this is the first time these statistics are requested for the given
|
|||
// repository, this method will return a non-nil error and a status code of
|
|||
// 202. This is because this is the status that github returns to signify that
|
|||
// it is now computing the requested statistics. A follow up request, after a
|
|||
// delay of a second or so, should result in a successful request.
|
|||
//
|
|||
// GitHub API Docs: https://developer.github.com/v3/repos/statistics/#contributors
|
|||
func (s *RepositoriesService) ListContributorsStats(owner, repo string) ([]*ContributorStats, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/stats/contributors", owner, repo) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
var contributorStats []*ContributorStats |
|||
resp, err := s.client.Do(req, &contributorStats) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return contributorStats, resp, err |
|||
} |
|||
|
|||
// WeeklyCommitActivity represents the weekly commit activity for a repository.
|
|||
// The days array is a group of commits per day, starting on Sunday.
|
|||
type WeeklyCommitActivity struct { |
|||
Days []int `json:"days,omitempty"` |
|||
Total *int `json:"total,omitempty"` |
|||
Week *Timestamp `json:"week,omitempty"` |
|||
} |
|||
|
|||
func (w WeeklyCommitActivity) String() string { |
|||
return Stringify(w) |
|||
} |
|||
|
|||
// ListCommitActivity returns the last year of commit activity
|
|||
// grouped by week. The days array is a group of commits per day,
|
|||
// starting on Sunday.
|
|||
//
|
|||
// If this is the first time these statistics are requested for the given
|
|||
// repository, this method will return a non-nil error and a status code of
|
|||
// 202. This is because this is the status that github returns to signify that
|
|||
// it is now computing the requested statistics. A follow up request, after a
|
|||
// delay of a second or so, should result in a successful request.
|
|||
//
|
|||
// GitHub API Docs: https://developer.github.com/v3/repos/statistics/#commit-activity
|
|||
func (s *RepositoriesService) ListCommitActivity(owner, repo string) ([]*WeeklyCommitActivity, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/stats/commit_activity", owner, repo) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
var weeklyCommitActivity []*WeeklyCommitActivity |
|||
resp, err := s.client.Do(req, &weeklyCommitActivity) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return weeklyCommitActivity, resp, err |
|||
} |
|||
|
|||
// ListCodeFrequency returns a weekly aggregate of the number of additions and
|
|||
// deletions pushed to a repository. Returned WeeklyStats will contain
|
|||
// additions and deletions, but not total commits.
|
|||
//
|
|||
// GitHub API Docs: https://developer.github.com/v3/repos/statistics/#code-frequency
|
|||
func (s *RepositoriesService) ListCodeFrequency(owner, repo string) ([]*WeeklyStats, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/stats/code_frequency", owner, repo) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
var weeks [][]int |
|||
resp, err := s.client.Do(req, &weeks) |
|||
|
|||
// convert int slices into WeeklyStats
|
|||
var stats []*WeeklyStats |
|||
for _, week := range weeks { |
|||
if len(week) != 3 { |
|||
continue |
|||
} |
|||
stat := &WeeklyStats{ |
|||
Week: &Timestamp{time.Unix(int64(week[0]), 0)}, |
|||
Additions: Int(week[1]), |
|||
Deletions: Int(week[2]), |
|||
} |
|||
stats = append(stats, stat) |
|||
} |
|||
|
|||
return stats, resp, err |
|||
} |
|||
|
|||
// RepositoryParticipation is the number of commits by everyone
|
|||
// who has contributed to the repository (including the owner)
|
|||
// as well as the number of commits by the owner themself.
|
|||
type RepositoryParticipation struct { |
|||
All []int `json:"all,omitempty"` |
|||
Owner []int `json:"owner,omitempty"` |
|||
} |
|||
|
|||
func (r RepositoryParticipation) String() string { |
|||
return Stringify(r) |
|||
} |
|||
|
|||
// ListParticipation returns the total commit counts for the 'owner'
|
|||
// and total commit counts in 'all'. 'all' is everyone combined,
|
|||
// including the 'owner' in the last 52 weeks. If you’d like to get
|
|||
// the commit counts for non-owners, you can subtract 'all' from 'owner'.
|
|||
//
|
|||
// The array order is oldest week (index 0) to most recent week.
|
|||
//
|
|||
// If this is the first time these statistics are requested for the given
|
|||
// repository, this method will return a non-nil error and a status code
|
|||
// of 202. This is because this is the status that github returns to
|
|||
// signify that it is now computing the requested statistics. A follow
|
|||
// up request, after a delay of a second or so, should result in a
|
|||
// successful request.
|
|||
//
|
|||
// GitHub API Docs: https://developer.github.com/v3/repos/statistics/#participation
|
|||
func (s *RepositoriesService) ListParticipation(owner, repo string) (*RepositoryParticipation, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/stats/participation", owner, repo) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
participation := new(RepositoryParticipation) |
|||
resp, err := s.client.Do(req, participation) |
|||
if err != nil { |
|||
return nil, resp, err |
|||
} |
|||
|
|||
return participation, resp, err |
|||
} |
|||
|
|||
// PunchCard represents the number of commits made during a given hour of a
|
|||
// day of thew eek.
|
|||
type PunchCard struct { |
|||
Day *int // Day of the week (0-6: =Sunday - Saturday).
|
|||
Hour *int // Hour of day (0-23).
|
|||
Commits *int // Number of commits.
|
|||
} |
|||
|
|||
// ListPunchCard returns the number of commits per hour in each day.
|
|||
//
|
|||
// GitHub API Docs: https://developer.github.com/v3/repos/statistics/#punch-card
|
|||
func (s *RepositoriesService) ListPunchCard(owner, repo string) ([]*PunchCard, *Response, error) { |
|||
u := fmt.Sprintf("repos/%v/%v/stats/punch_card", owner, repo) |
|||
req, err := s.client.NewRequest("GET", u, nil) |
|||
if err != nil { |
|||
return nil, nil, err |
|||
} |
|||
|
|||
var results [][]int |
|||
resp, err := s.client.Do(req, &results) |
|||
|
|||
// convert int slices into Punchcards
|
|||
var cards []*PunchCard |
|||
for _, result := range results { |
|||
if len(result) != 3 { |
|||
continue |
|||
} |
|||
card := &PunchCard{ |
|||
Day: Int(result[0]), |
|||
Hour: Int(result[1]), |
|||
Commits: Int(result[2]), |
|||
} |
|||
cards = append(cards, card) |
|||
} |
|||
|
|||
return cards, resp, err |
|||
} |
@ -0,0 +1,210 @@ |
|||
// Copyright 2014 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 ( |
|||
"fmt" |
|||
"net/http" |
|||
"reflect" |
|||
"testing" |
|||
"time" |
|||
) |
|||
|
|||
func TestRepositoriesService_ListContributorsStats(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/stats/contributors", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
|
|||
fmt.Fprint(w, ` |
|||
[ |
|||
{ |
|||
"author": { |
|||
"id": 1 |
|||
}, |
|||
"total": 135, |
|||
"weeks": [ |
|||
{ |
|||
"w": 1367712000, |
|||
"a": 6898, |
|||
"d": 77, |
|||
"c": 10 |
|||
} |
|||
] |
|||
} |
|||
] |
|||
`) |
|||
}) |
|||
|
|||
stats, _, err := client.Repositories.ListContributorsStats("o", "r") |
|||
if err != nil { |
|||
t.Errorf("RepositoriesService.ListContributorsStats returned error: %v", err) |
|||
} |
|||
|
|||
want := []*ContributorStats{ |
|||
{ |
|||
Author: &Contributor{ |
|||
ID: Int(1), |
|||
}, |
|||
Total: Int(135), |
|||
Weeks: []WeeklyStats{ |
|||
{ |
|||
Week: &Timestamp{time.Date(2013, 05, 05, 00, 00, 00, 0, time.UTC).Local()}, |
|||
Additions: Int(6898), |
|||
Deletions: Int(77), |
|||
Commits: Int(10), |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
if !reflect.DeepEqual(stats, want) { |
|||
t.Errorf("RepositoriesService.ListContributorsStats returned %+v, want %+v", stats, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_ListCommitActivity(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/stats/commit_activity", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
|
|||
fmt.Fprint(w, ` |
|||
[ |
|||
{ |
|||
"days": [0, 3, 26, 20, 39, 1, 0], |
|||
"total": 89, |
|||
"week": 1336280400 |
|||
} |
|||
] |
|||
`) |
|||
}) |
|||
|
|||
activity, _, err := client.Repositories.ListCommitActivity("o", "r") |
|||
if err != nil { |
|||
t.Errorf("RepositoriesService.ListCommitActivity returned error: %v", err) |
|||
} |
|||
|
|||
want := []*WeeklyCommitActivity{ |
|||
{ |
|||
Days: []int{0, 3, 26, 20, 39, 1, 0}, |
|||
Total: Int(89), |
|||
Week: &Timestamp{time.Date(2012, 05, 06, 05, 00, 00, 0, time.UTC).Local()}, |
|||
}, |
|||
} |
|||
|
|||
if !reflect.DeepEqual(activity, want) { |
|||
t.Errorf("RepositoriesService.ListCommitActivity returned %+v, want %+v", activity, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_ListCodeFrequency(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/stats/code_frequency", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
|
|||
fmt.Fprint(w, `[[1302998400, 1124, -435]]`) |
|||
}) |
|||
|
|||
code, _, err := client.Repositories.ListCodeFrequency("o", "r") |
|||
if err != nil { |
|||
t.Errorf("RepositoriesService.ListCodeFrequency returned error: %v", err) |
|||
} |
|||
|
|||
want := []*WeeklyStats{{ |
|||
Week: &Timestamp{time.Date(2011, 04, 17, 00, 00, 00, 0, time.UTC).Local()}, |
|||
Additions: Int(1124), |
|||
Deletions: Int(-435), |
|||
}} |
|||
|
|||
if !reflect.DeepEqual(code, want) { |
|||
t.Errorf("RepositoriesService.ListCodeFrequency returned %+v, want %+v", code, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_Participation(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/stats/participation", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
|
|||
fmt.Fprint(w, ` |
|||
{ |
|||
"all": [ |
|||
11,21,15,2,8,1,8,23,17,21,11,10,33, |
|||
91,38,34,22,23,32,3,43,87,71,18,13,5, |
|||
13,16,66,27,12,45,110,117,13,8,18,9,19, |
|||
26,39,12,20,31,46,91,45,10,24,9,29,7 |
|||
], |
|||
"owner": [ |
|||
3,2,3,0,2,0,5,14,7,9,1,5,0, |
|||
48,19,2,0,1,10,2,23,40,35,8,8,2, |
|||
10,6,30,0,2,9,53,104,3,3,10,4,7, |
|||
11,21,4,4,22,26,63,11,2,14,1,10,3 |
|||
] |
|||
} |
|||
`) |
|||
}) |
|||
|
|||
participation, _, err := client.Repositories.ListParticipation("o", "r") |
|||
if err != nil { |
|||
t.Errorf("RepositoriesService.ListParticipation returned error: %v", err) |
|||
} |
|||
|
|||
want := &RepositoryParticipation{ |
|||
All: []int{ |
|||
11, 21, 15, 2, 8, 1, 8, 23, 17, 21, 11, 10, 33, |
|||
91, 38, 34, 22, 23, 32, 3, 43, 87, 71, 18, 13, 5, |
|||
13, 16, 66, 27, 12, 45, 110, 117, 13, 8, 18, 9, 19, |
|||
26, 39, 12, 20, 31, 46, 91, 45, 10, 24, 9, 29, 7, |
|||
}, |
|||
Owner: []int{ |
|||
3, 2, 3, 0, 2, 0, 5, 14, 7, 9, 1, 5, 0, |
|||
48, 19, 2, 0, 1, 10, 2, 23, 40, 35, 8, 8, 2, |
|||
10, 6, 30, 0, 2, 9, 53, 104, 3, 3, 10, 4, 7, |
|||
11, 21, 4, 4, 22, 26, 63, 11, 2, 14, 1, 10, 3, |
|||
}, |
|||
} |
|||
|
|||
if !reflect.DeepEqual(participation, want) { |
|||
t.Errorf("RepositoriesService.ListParticipation returned %+v, want %+v", participation, want) |
|||
} |
|||
} |
|||
|
|||
func TestRepositoriesService_ListPunchCard(t *testing.T) { |
|||
setup() |
|||
defer teardown() |
|||
|
|||
mux.HandleFunc("/repos/o/r/stats/punch_card", func(w http.ResponseWriter, r *http.Request) { |
|||
testMethod(t, r, "GET") |
|||
|
|||
fmt.Fprint(w, `[ |
|||
[0, 0, 5], |
|||
[0, 1, 43], |
|||
[0, 2, 21] |
|||
]`) |
|||
}) |
|||
|
|||
card, _, err := client.Repositories.ListPunchCard("o", "r") |
|||
if err != nil { |
|||
t.Errorf("RepositoriesService.ListPunchCard returned error: %v", err) |
|||
} |
|||
|
|||
want := []*PunchCard{ |
|||
{Day: Int(0), Hour: Int(0), Commits: Int(5)}, |
|||
{Day: Int(0), Hour: Int(1), Commits: Int(43)}, |
|||
{Day: Int(0), Hour: Int(2), Commits: Int(21)}, |
|||
} |
|||
|
|||
if !reflect.DeepEqual(card, want) { |
|||
t.Errorf("RepositoriesService.ListPunchCard returned %+v, want %+v", card, want) |
|||
} |
|||
} |
Some files were not shown because too many files changed in this diff
Write
Preview
Loading…
Cancel
Save
Reference in new issue