From 56f6b12f8ce99c254a30f48d87d8bb28024a9e4e Mon Sep 17 00:00:00 2001 From: Will Norris Date: Fri, 29 Aug 2014 08:13:25 -0700 Subject: [PATCH] update Membership to keep up with latest API changes Latest blog post: https://developer.github.com/changes/2014-08-28-accepting-organization-invitations-from-the-api/ - TeamMembership has been renamed to Membership, since it now covers organization membership as well - Status field renamed to State - added new methods: ListOrgMemberships, GetOrgMembership, EditOrgMembership --- github/github.go | 2 +- github/orgs_members.go | 99 +++++++++++++++++++++++++++++++++++++ github/orgs_members_test.go | 81 ++++++++++++++++++++++++++++++ github/orgs_teams.go | 25 +++------- github/orgs_teams_test.go | 14 +++--- 5 files changed, 195 insertions(+), 26 deletions(-) diff --git a/github/github.go b/github/github.go index f5cd1b9..f4e0dc9 100644 --- a/github/github.go +++ b/github/github.go @@ -38,7 +38,7 @@ const ( // Media Type values to access preview APIs // https://developer.github.com/changes/2014-08-05-team-memberships-api/ - mediaTypeTeamMembershipPreview = "application/vnd.github.the-wasp-preview+json" + mediaTypeMembershipPreview = "application/vnd.github.the-wasp-preview+json" ) // A Client manages communication with the GitHub API. diff --git a/github/orgs_members.go b/github/orgs_members.go index 5a721c8..02eb13a 100644 --- a/github/orgs_members.go +++ b/github/orgs_members.go @@ -7,6 +7,22 @@ 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"` + + // For organization membership, the API URL of the organization. + OrganizationURL *string `json:"organization_url,omitempty"` +} + +func (m Membership) String() string { + return Stringify(m) +} + // ListMembersOptions specifies optional parameters to the // OrganizationsService.ListMembers method. type ListMembersOptions struct { @@ -120,3 +136,86 @@ func (s *OrganizationsService) ConcealMembership(org, user string) (*Response, e return s.client.Do(req, nil) } + +// ListOrgMembershipsOptions specifies optional parameters to the +// OrganizationsService.ListOrgMemberships method. +type ListOrgMembershipsOptions struct { + // Filter memberships to include only those withe 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 + } + + // TODO: remove custom Accept header when this API fully launches + req.Header.Set("Accept", mediaTypeMembershipPreview) + + 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 the authenticated user for the +// specified organization. +// +// GitHub API docs: https://developer.github.com/v3/orgs/members/#get-your-organization-membership +func (s *OrganizationsService) GetOrgMembership(org string) (*Membership, *Response, error) { + u := fmt.Sprintf("user/memberships/orgs/%v", 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", mediaTypeMembershipPreview) + + 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 the authenticated user for the +// specified organization. +// +// GitHub API docs: https://developer.github.com/v3/orgs/members/#edit-your-organization-membership +func (s *OrganizationsService) EditOrgMembership(org string, membership *Membership) (*Membership, *Response, error) { + u := fmt.Sprintf("user/memberships/orgs/%v", org) + req, err := s.client.NewRequest("PATCH", u, membership) + if err != nil { + return nil, nil, err + } + + // TODO: remove custom Accept header when this API fully launches + req.Header.Set("Accept", mediaTypeMembershipPreview) + + m := new(Membership) + resp, err := s.client.Do(req, m) + if err != nil { + return nil, resp, err + } + + return m, resp, err +} diff --git a/github/orgs_members_test.go b/github/orgs_members_test.go index c7e6e1b..85cb987 100644 --- a/github/orgs_members_test.go +++ b/github/orgs_members_test.go @@ -6,6 +6,7 @@ package github import ( + "encoding/json" "fmt" "net/http" "reflect" @@ -209,3 +210,83 @@ 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") + testHeader(t, r, "Accept", mediaTypeMembershipPreview) + 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(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/user/memberships/orgs/o", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testHeader(t, r, "Accept", mediaTypeMembershipPreview) + 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_EditOrgMembership(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") + testHeader(t, r, "Accept", mediaTypeMembershipPreview) + 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) + } +} diff --git a/github/orgs_teams.go b/github/orgs_teams.go index ed5d43a..9e32113 100644 --- a/github/orgs_teams.go +++ b/github/orgs_teams.go @@ -23,17 +23,6 @@ func (t Team) String() string { return Stringify(t) } -// TeamMembership represents the status an a user's membership in a team. -type TeamMembership struct { - URL *string `json:"url,omitempty"` - // Status is the user's status within the team. Possible values are: "active", "pending" - Status *string `json:"status,omitempty"` -} - -func (t TeamMembership) 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 @@ -266,7 +255,7 @@ func (s *OrganizationsService) RemoveTeamRepo(team int, owner string, repo strin // 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) (*TeamMembership, *Response, error) { +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 { @@ -274,9 +263,9 @@ func (s *OrganizationsService) GetTeamMembership(team int, user string) (*TeamMe } // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeTeamMembershipPreview) + req.Header.Set("Accept", mediaTypeMembershipPreview) - t := new(TeamMembership) + t := new(Membership) resp, err := s.client.Do(req, t) if err != nil { return nil, resp, err @@ -303,7 +292,7 @@ func (s *OrganizationsService) GetTeamMembership(team int, user string) (*TeamMe // 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) (*TeamMembership, *Response, error) { +func (s *OrganizationsService) AddTeamMembership(team int, user string) (*Membership, *Response, error) { u := fmt.Sprintf("teams/%v/memberships/%v", team, user) req, err := s.client.NewRequest("PUT", u, nil) if err != nil { @@ -311,9 +300,9 @@ func (s *OrganizationsService) AddTeamMembership(team int, user string) (*TeamMe } // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeTeamMembershipPreview) + req.Header.Set("Accept", mediaTypeMembershipPreview) - t := new(TeamMembership) + t := new(Membership) resp, err := s.client.Do(req, t) if err != nil { return nil, resp, err @@ -333,7 +322,7 @@ func (s *OrganizationsService) RemoveTeamMembership(team int, user string) (*Res } // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeTeamMembershipPreview) + req.Header.Set("Accept", mediaTypeMembershipPreview) return s.client.Do(req, nil) } diff --git a/github/orgs_teams_test.go b/github/orgs_teams_test.go index d413697..e2ddae4 100644 --- a/github/orgs_teams_test.go +++ b/github/orgs_teams_test.go @@ -442,8 +442,8 @@ func TestOrganizationsService_GetTeamMembership(t *testing.T) { mux.HandleFunc("/teams/1/memberships/u", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testHeader(t, r, "Accept", mediaTypeTeamMembershipPreview) - fmt.Fprint(w, `{"url":"u", "status":"active"}`) + testHeader(t, r, "Accept", mediaTypeMembershipPreview) + fmt.Fprint(w, `{"url":"u", "state":"active"}`) }) membership, _, err := client.Organizations.GetTeamMembership(1, "u") @@ -451,7 +451,7 @@ func TestOrganizationsService_GetTeamMembership(t *testing.T) { t.Errorf("Organizations.GetTeamMembership returned error: %v", err) } - want := &TeamMembership{URL: String("u"), Status: String("active")} + want := &Membership{URL: String("u"), State: String("active")} if !reflect.DeepEqual(membership, want) { t.Errorf("Organizations.GetTeamMembership returned %+v, want %+v", membership, want) } @@ -463,8 +463,8 @@ func TestOrganizationsService_AddTeamMembership(t *testing.T) { mux.HandleFunc("/teams/1/memberships/u", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "PUT") - testHeader(t, r, "Accept", mediaTypeTeamMembershipPreview) - fmt.Fprint(w, `{"url":"u", "status":"pending"}`) + testHeader(t, r, "Accept", mediaTypeMembershipPreview) + fmt.Fprint(w, `{"url":"u", "state":"pending"}`) }) membership, _, err := client.Organizations.AddTeamMembership(1, "u") @@ -472,7 +472,7 @@ func TestOrganizationsService_AddTeamMembership(t *testing.T) { t.Errorf("Organizations.AddTeamMembership returned error: %v", err) } - want := &TeamMembership{URL: String("u"), Status: String("pending")} + want := &Membership{URL: String("u"), State: String("pending")} if !reflect.DeepEqual(membership, want) { t.Errorf("Organizations.AddTeamMembership returned %+v, want %+v", membership, want) } @@ -484,7 +484,7 @@ func TestOrganizationsService_RemoveTeamMembership(t *testing.T) { mux.HandleFunc("/teams/1/memberships/u", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "DELETE") - testHeader(t, r, "Accept", mediaTypeTeamMembershipPreview) + testHeader(t, r, "Accept", mediaTypeMembershipPreview) w.WriteHeader(http.StatusNoContent) })