Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions github/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ const (

// 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"
)

// A Client manages communication with the GitHub API.
Expand Down
7 changes: 5 additions & 2 deletions github/repos_collaborators.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,20 +58,23 @@ type RepositoryAddCollaboratorOptions struct {
// 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 "pull". This option is only valid for organization-owned repositories.
// 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: http://developer.github.com/v3/repos/collaborators/#add-collaborator
// 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)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's interesting that this takes an existing, previously stable endpoint, and changes it slightly to use the (potentially less stable) preview API. (Usually preview APIs are for new endpoints.)

I think it's fine per our discussion in #376 because people who have really really specific needs will be vendoring this library anyway.


return s.client.Do(req, nil)
}

Expand Down
1 change: 1 addition & 0 deletions github/repos_collaborators_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ func TestRepositoriesService_AddCollaborator(t *testing.T) {
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)
}
Expand Down
91 changes: 91 additions & 0 deletions github/repos_invitations.go
Original file line number Diff line number Diff line change
@@ -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
}
73 changes: 73 additions & 0 deletions github/repos_invitations_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
56 changes: 56 additions & 0 deletions github/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,3 +166,59 @@ func (s *UsersService) ListAll(opt *UserListOptions) ([]*User, *Response, error)

return *users, resp, err
}

// ListInvitations lists all currently-open repository invitations for the
// authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/repos/invitations/#list-a-users-repository-invitations
func (s *UsersService) ListInvitations() ([]*RepositoryInvitation, *Response, error) {
req, err := s.client.NewRequest("GET", "user/repository_invitations", 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
}

// AcceptInvitation accepts the currently-open repository invitation for the
// authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/repos/invitations/#accept-a-repository-invitation
func (s *UsersService) AcceptInvitation(invitationID int) (*Response, error) {
u := fmt.Sprintf("user/repository_invitations/%v", invitationID)
req, err := s.client.NewRequest("PATCH", 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)
}

// DeclineInvitation declines the currently-open repository invitation for the
// authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/repos/invitations/#decline-a-repository-invitation
func (s *UsersService) DeclineInvitation(invitationID int) (*Response, error) {
u := fmt.Sprintf("user/repository_invitations/%v", 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)
}
6 changes: 2 additions & 4 deletions github/users_gpg_keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,8 @@ func (s *UsersService) GetGPGKey(id int) (*GPGKey, *Response, error) {
// GitHub API docs: https://developer.github.com/v3/users/gpg_keys/#create-a-gpg-key
func (s *UsersService) CreateGPGKey(armoredPublicKey string) (*GPGKey, *Response, error) {
gpgKey := &struct {
ArmoredPublicKey *string `json:"armored_public_key,omitempty"`
}{
ArmoredPublicKey: String(armoredPublicKey),
}
ArmoredPublicKey string `json:"armored_public_key"`
}{ArmoredPublicKey: armoredPublicKey}
req, err := s.client.NewRequest("POST", "user/gpg_keys", gpgKey)
if err != nil {
return nil, nil, err
Expand Down
51 changes: 51 additions & 0 deletions github/users_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,54 @@ func TestUsersService_ListAll(t *testing.T) {
t.Errorf("Users.ListAll returned %+v, want %+v", users, want)
}
}

func TestUsersService_ListInvitations(t *testing.T) {
setup()
defer teardown()

mux.HandleFunc("/user/repository_invitations", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testHeader(t, r, "Accept", mediaTypeRepositoryInvitationsPreview)
fmt.Fprintf(w, `[{"id":1}, {"id":2}]`)
})

got, _, err := client.Users.ListInvitations()
if err != nil {
t.Errorf("Users.ListInvitations returned error: %v", err)
}

want := []*RepositoryInvitation{{ID: Int(1)}, {ID: Int(2)}}
if !reflect.DeepEqual(got, want) {
t.Errorf("Users.ListInvitations = %+v, want %+v", got, want)
}
}

func TestUsersService_AcceptInvitation(t *testing.T) {
setup()
defer teardown()

mux.HandleFunc("/user/repository_invitations/1", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "PATCH")
testHeader(t, r, "Accept", mediaTypeRepositoryInvitationsPreview)
w.WriteHeader(http.StatusNoContent)
})

if _, err := client.Users.AcceptInvitation(1); err != nil {
t.Errorf("Users.AcceptInvitation returned error: %v", err)
}
}

func TestUsersService_DeclineInvitation(t *testing.T) {
setup()
defer teardown()

mux.HandleFunc("/user/repository_invitations/1", func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "DELETE")
testHeader(t, r, "Accept", mediaTypeRepositoryInvitationsPreview)
w.WriteHeader(http.StatusNoContent)
})

if _, err := client.Users.DeclineInvitation(1); err != nil {
t.Errorf("Users.DeclineInvitation returned error: %v", err)
}
}