diff --git a/github/examples_test.go b/github/examples_test.go index 525b0f03158..ad9430930de 100644 --- a/github/examples_test.go +++ b/github/examples_test.go @@ -173,3 +173,19 @@ func ExampleTeamsService_ListTeams() { fmt.Printf("Team %q was not found\n", teamName) } + +func ExampleUsersService_ListUserSocialAccounts() { + client := github.NewClient(nil) + ctx := context.Background() + opts := &github.ListOptions{} + for { + accounts, resp, err := client.Users.ListUserSocialAccounts(ctx, "shreyjain13", opts) + if err != nil { + log.Fatalf("Failed to list user social accounts: %v", err) + } + if resp.NextPage == 0 || len(accounts) == 0 { + break + } + opts.Page = resp.NextPage + } +} diff --git a/github/github-accessors.go b/github/github-accessors.go index b9d8bcf7e65..127359f1226 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -25734,6 +25734,22 @@ func (s *SignatureVerification) GetVerified() bool { return *s.Verified } +// GetProvider returns the Provider field if it's non-nil, zero value otherwise. +func (s *SocialAccount) GetProvider() string { + if s == nil || s.Provider == nil { + return "" + } + return *s.Provider +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (s *SocialAccount) GetURL() string { + if s == nil || s.URL == nil { + return "" + } + return *s.URL +} + // GetActor returns the Actor field. func (s *Source) GetActor() *User { if s == nil { diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index f6a191ee0f8..50188684d5d 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -33097,6 +33097,28 @@ func TestSignatureVerification_GetVerified(tt *testing.T) { s.GetVerified() } +func TestSocialAccount_GetProvider(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SocialAccount{Provider: &zeroValue} + s.GetProvider() + s = &SocialAccount{} + s.GetProvider() + s = nil + s.GetProvider() +} + +func TestSocialAccount_GetURL(tt *testing.T) { + tt.Parallel() + var zeroValue string + s := &SocialAccount{URL: &zeroValue} + s.GetURL() + s = &SocialAccount{} + s.GetURL() + s = nil + s.GetURL() +} + func TestSource_GetActor(tt *testing.T) { tt.Parallel() s := &Source{} diff --git a/github/users_social_accounts.go b/github/users_social_accounts.go new file mode 100644 index 00000000000..30927cd8db1 --- /dev/null +++ b/github/users_social_accounts.go @@ -0,0 +1,105 @@ +// Copyright 2025 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 ( + "context" + "fmt" +) + +// SocialAccount represents a social account linked to a user. +type SocialAccount struct { + Provider *string `json:"provider,omitempty"` + URL *string `json:"url,omitempty"` +} + +// ListSocialAccounts lists all social accounts for the authenticated user. +// +// GitHub API docs: https://docs.github.com/rest/users/social-accounts#list-social-accounts-for-the-authenticated-user +// +//meta:operation GET /user/social_accounts +func (s *UsersService) ListSocialAccounts(ctx context.Context, opts *ListOptions) ([]*SocialAccount, *Response, error) { + u := "user/social_accounts" + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var socialAccounts []*SocialAccount + resp, err := s.client.Do(ctx, req, &socialAccounts) + if err != nil { + return nil, resp, err + } + + return socialAccounts, resp, nil +} + +// AddSocialAccounts adds social accounts for the authenticated user. +// +// GitHub API docs: https://docs.github.com/rest/users/social-accounts#add-social-accounts-for-the-authenticated-user +// +//meta:operation POST /user/social_accounts +func (s *UsersService) AddSocialAccounts(ctx context.Context, accountURLs []string) ([]*SocialAccount, *Response, error) { + u := "user/social_accounts" + req, err := s.client.NewRequest("POST", u, accountURLs) + if err != nil { + return nil, nil, err + } + + var accounts []*SocialAccount + resp, err := s.client.Do(ctx, req, &accounts) + if err != nil { + return nil, resp, err + } + + return accounts, resp, nil +} + +// DeleteSocialAccounts deletes social accounts for the authenticated user. +// +// GitHub API docs: https://docs.github.com/rest/users/social-accounts#delete-social-accounts-for-the-authenticated-user +// +//meta:operation DELETE /user/social_accounts +func (s *UsersService) DeleteSocialAccounts(ctx context.Context, accountURLs []string) (*Response, error) { + u := "user/social_accounts" + req, err := s.client.NewRequest("DELETE", u, accountURLs) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// ListUserSocialAccounts lists all social accounts for a user. +// +// GitHub API docs: https://docs.github.com/rest/users/social-accounts#list-social-accounts-for-a-user +// +//meta:operation GET /users/{username}/social_accounts +func (s *UsersService) ListUserSocialAccounts(ctx context.Context, username string, opts *ListOptions) ([]*SocialAccount, *Response, error) { + u := fmt.Sprintf("users/%v/social_accounts", username) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var addedAccounts []*SocialAccount + resp, err := s.client.Do(ctx, req, &addedAccounts) + if err != nil { + return nil, resp, err + } + + return addedAccounts, resp, nil +} diff --git a/github/users_social_accounts_test.go b/github/users_social_accounts_test.go new file mode 100644 index 00000000000..11424b1284b --- /dev/null +++ b/github/users_social_accounts_test.go @@ -0,0 +1,160 @@ +// Copyright 2025 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 ( + "context" + "encoding/json" + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestUsersService_ListSocialAccounts(t *testing.T) { + t.Parallel() + + client, mux, _ := setup(t) + + mux.HandleFunc("/user/social_accounts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{ + "provider": "twitter", + "url": "https://twitter.com/github" + }]`) + }) + + opt := &ListOptions{Page: 2} + ctx := context.Background() + accounts, _, err := client.Users.ListSocialAccounts(ctx, opt) + if err != nil { + t.Errorf("Users.ListSocialAccounts returned error: %v", err) + } + + want := []*SocialAccount{{Provider: Ptr("twitter"), URL: Ptr("https://twitter.com/github")}} + if !cmp.Equal(accounts, want) { + t.Errorf("Users.ListSocialAccounts returned %#v, want %#v", accounts, want) + } + + const methodName = "ListSocialAccounts" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.ListSocialAccounts(ctx, opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestUsersService_AddSocialAccounts(t *testing.T) { + t.Parallel() + + client, mux, _ := setup(t) + + input := []string{"https://twitter.com/github"} + + mux.HandleFunc("/user/social_accounts", func(w http.ResponseWriter, r *http.Request) { + var v []string + assertNilError(t, json.NewDecoder(r.Body).Decode(&v)) + + testMethod(t, r, "POST") + if !cmp.Equal(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + + fmt.Fprint(w, `[{"provider":"twitter","url":"https://twitter.com/github"},{"provider":"facebook","url":"https://facebook.com/github"}]`) + }) + + ctx := context.Background() + accounts, _, err := client.Users.AddSocialAccounts(ctx, input) + if err != nil { + t.Errorf("Users.AddSocialAccounts returned error: %v", err) + } + + want := []*SocialAccount{ + {Provider: Ptr("twitter"), URL: Ptr("https://twitter.com/github")}, + {Provider: Ptr("facebook"), URL: Ptr("https://facebook.com/github")}, + } + if !cmp.Equal(accounts, want) { + t.Errorf("Users.AddSocialAccounts returned %#v, want %#v", accounts, want) + } + + const methodName = "AddSocialAccounts" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.AddSocialAccounts(ctx, input) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestUsersService_DeleteSocialAccounts(t *testing.T) { + t.Parallel() + + client, mux, _ := setup(t) + + input := []string{"https://twitter.com/github"} + + mux.HandleFunc("/user/social_accounts", func(_ http.ResponseWriter, r *http.Request) { + var v []string + assertNilError(t, json.NewDecoder(r.Body).Decode(&v)) + + testMethod(t, r, "DELETE") + if !cmp.Equal(v, input) { + t.Errorf("Request body = %+v, want %+v", v, input) + } + }) + + ctx := context.Background() + _, err := client.Users.DeleteSocialAccounts(ctx, input) + if err != nil { + t.Errorf("Users.DeleteSocialAccounts returned error: %v", err) + } + + const methodName = "DeleteSocialAccounts" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Users.DeleteSocialAccounts(ctx, input) + }) +} + +func TestUsersService_ListUserSocialAccounts(t *testing.T) { + t.Parallel() + + client, mux, _ := setup(t) + + mux.HandleFunc("/users/u/social_accounts", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{"page": "2"}) + fmt.Fprint(w, `[{ + "provider": "twitter", + "url": "https://twitter.com/github" + }]`) + }) + + opt := &ListOptions{Page: 2} + ctx := context.Background() + accounts, _, err := client.Users.ListUserSocialAccounts(ctx, "u", opt) + if err != nil { + t.Errorf("Users.ListUserSocialAccounts returned error: %v", err) + } + + want := []*SocialAccount{{Provider: Ptr("twitter"), URL: Ptr("https://twitter.com/github")}} + if !cmp.Equal(accounts, want) { + t.Errorf("Users.ListUserSocialAccounts returned %#v, want %#v", accounts, want) + } + + const methodName = "ListUserSocialAccounts" + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Users.ListUserSocialAccounts(ctx, "u", opt) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +}