1// Copyright 2013 The go-github AUTHORS. All rights reserved.
2//
3// Use of this source code is governed by a BSD-style
4// license that can be found in the LICENSE file.
5
6package github
7
8import (
9	"context"
10	"fmt"
11)
12
13// UsersService handles communication with the user related
14// methods of the GitHub API.
15//
16// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/users/
17type UsersService service
18
19// User represents a GitHub user.
20type User struct {
21	Login                   *string    `json:"login,omitempty"`
22	ID                      *int64     `json:"id,omitempty"`
23	NodeID                  *string    `json:"node_id,omitempty"`
24	AvatarURL               *string    `json:"avatar_url,omitempty"`
25	HTMLURL                 *string    `json:"html_url,omitempty"`
26	GravatarID              *string    `json:"gravatar_id,omitempty"`
27	Name                    *string    `json:"name,omitempty"`
28	Company                 *string    `json:"company,omitempty"`
29	Blog                    *string    `json:"blog,omitempty"`
30	Location                *string    `json:"location,omitempty"`
31	Email                   *string    `json:"email,omitempty"`
32	Hireable                *bool      `json:"hireable,omitempty"`
33	Bio                     *string    `json:"bio,omitempty"`
34	TwitterUsername         *string    `json:"twitter_username,omitempty"`
35	PublicRepos             *int       `json:"public_repos,omitempty"`
36	PublicGists             *int       `json:"public_gists,omitempty"`
37	Followers               *int       `json:"followers,omitempty"`
38	Following               *int       `json:"following,omitempty"`
39	CreatedAt               *Timestamp `json:"created_at,omitempty"`
40	UpdatedAt               *Timestamp `json:"updated_at,omitempty"`
41	SuspendedAt             *Timestamp `json:"suspended_at,omitempty"`
42	Type                    *string    `json:"type,omitempty"`
43	SiteAdmin               *bool      `json:"site_admin,omitempty"`
44	TotalPrivateRepos       *int       `json:"total_private_repos,omitempty"`
45	OwnedPrivateRepos       *int       `json:"owned_private_repos,omitempty"`
46	PrivateGists            *int       `json:"private_gists,omitempty"`
47	DiskUsage               *int       `json:"disk_usage,omitempty"`
48	Collaborators           *int       `json:"collaborators,omitempty"`
49	TwoFactorAuthentication *bool      `json:"two_factor_authentication,omitempty"`
50	Plan                    *Plan      `json:"plan,omitempty"`
51	LdapDn                  *string    `json:"ldap_dn,omitempty"`
52
53	// API URLs
54	URL               *string `json:"url,omitempty"`
55	EventsURL         *string `json:"events_url,omitempty"`
56	FollowingURL      *string `json:"following_url,omitempty"`
57	FollowersURL      *string `json:"followers_url,omitempty"`
58	GistsURL          *string `json:"gists_url,omitempty"`
59	OrganizationsURL  *string `json:"organizations_url,omitempty"`
60	ReceivedEventsURL *string `json:"received_events_url,omitempty"`
61	ReposURL          *string `json:"repos_url,omitempty"`
62	StarredURL        *string `json:"starred_url,omitempty"`
63	SubscriptionsURL  *string `json:"subscriptions_url,omitempty"`
64
65	// TextMatches is only populated from search results that request text matches
66	// See: search.go and https://docs.github.com/en/free-pro-team@latest/rest/reference/search/#text-match-metadata
67	TextMatches []*TextMatch `json:"text_matches,omitempty"`
68
69	// Permissions identifies the permissions that a user has on a given
70	// repository. This is only populated when calling Repositories.ListCollaborators.
71	Permissions map[string]bool `json:"permissions,omitempty"`
72}
73
74func (u User) String() string {
75	return Stringify(u)
76}
77
78// Get fetches a user. Passing the empty string will fetch the authenticated
79// user.
80//
81// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/users/#get-the-authenticated-user
82// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/users/#get-a-user
83func (s *UsersService) Get(ctx context.Context, user string) (*User, *Response, error) {
84	var u string
85	if user != "" {
86		u = fmt.Sprintf("users/%v", user)
87	} else {
88		u = "user"
89	}
90	req, err := s.client.NewRequest("GET", u, nil)
91	if err != nil {
92		return nil, nil, err
93	}
94
95	uResp := new(User)
96	resp, err := s.client.Do(ctx, req, uResp)
97	if err != nil {
98		return nil, resp, err
99	}
100
101	return uResp, resp, nil
102}
103
104// GetByID fetches a user.
105//
106// Note: GetByID uses the undocumented GitHub API endpoint /user/:id.
107func (s *UsersService) GetByID(ctx context.Context, id int64) (*User, *Response, error) {
108	u := fmt.Sprintf("user/%d", id)
109	req, err := s.client.NewRequest("GET", u, nil)
110	if err != nil {
111		return nil, nil, err
112	}
113
114	user := new(User)
115	resp, err := s.client.Do(ctx, req, user)
116	if err != nil {
117		return nil, resp, err
118	}
119
120	return user, resp, nil
121}
122
123// Edit the authenticated user.
124//
125// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/users/#update-the-authenticated-user
126func (s *UsersService) Edit(ctx context.Context, user *User) (*User, *Response, error) {
127	u := "user"
128	req, err := s.client.NewRequest("PATCH", u, user)
129	if err != nil {
130		return nil, nil, err
131	}
132
133	uResp := new(User)
134	resp, err := s.client.Do(ctx, req, uResp)
135	if err != nil {
136		return nil, resp, err
137	}
138
139	return uResp, resp, nil
140}
141
142// HovercardOptions specifies optional parameters to the UsersService.GetHovercard
143// method.
144type HovercardOptions struct {
145	// SubjectType specifies the additional information to be received about the hovercard.
146	// Possible values are: organization, repository, issue, pull_request. (Required when using subject_id.)
147	SubjectType string `url:"subject_type"`
148
149	// SubjectID specifies the ID for the SubjectType. (Required when using subject_type.)
150	SubjectID string `url:"subject_id"`
151}
152
153// Hovercard represents hovercard information about a user.
154type Hovercard struct {
155	Contexts []*UserContext `json:"contexts,omitempty"`
156}
157
158// UserContext represents the contextual information about user.
159type UserContext struct {
160	Message *string `json:"message,omitempty"`
161	Octicon *string `json:"octicon,omitempty"`
162}
163
164// GetHovercard fetches contextual information about user. It requires authentication
165// via Basic Auth or via OAuth with the repo scope.
166//
167// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/users/#get-contextual-information-for-a-user
168func (s *UsersService) GetHovercard(ctx context.Context, user string, opts *HovercardOptions) (*Hovercard, *Response, error) {
169	u := fmt.Sprintf("users/%v/hovercard", user)
170	u, err := addOptions(u, opts)
171	if err != nil {
172		return nil, nil, err
173	}
174
175	req, err := s.client.NewRequest("GET", u, nil)
176	if err != nil {
177		return nil, nil, err
178	}
179
180	hc := new(Hovercard)
181	resp, err := s.client.Do(ctx, req, hc)
182	if err != nil {
183		return nil, resp, err
184	}
185
186	return hc, resp, nil
187}
188
189// UserListOptions specifies optional parameters to the UsersService.ListAll
190// method.
191type UserListOptions struct {
192	// ID of the last user seen
193	Since int64 `url:"since,omitempty"`
194
195	// Note: Pagination is powered exclusively by the Since parameter,
196	// ListOptions.Page has no effect.
197	// ListOptions.PerPage controls an undocumented GitHub API parameter.
198	ListOptions
199}
200
201// ListAll lists all GitHub users.
202//
203// To paginate through all users, populate 'Since' with the ID of the last user.
204//
205// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/users/#list-users
206func (s *UsersService) ListAll(ctx context.Context, opts *UserListOptions) ([]*User, *Response, error) {
207	u, err := addOptions("users", opts)
208	if err != nil {
209		return nil, nil, err
210	}
211
212	req, err := s.client.NewRequest("GET", u, nil)
213	if err != nil {
214		return nil, nil, err
215	}
216
217	var users []*User
218	resp, err := s.client.Do(ctx, req, &users)
219	if err != nil {
220		return nil, resp, err
221	}
222
223	return users, resp, nil
224}
225
226// ListInvitations lists all currently-open repository invitations for the
227// authenticated user.
228//
229// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/repos/#list-repository-invitations-for-the-authenticated-user
230func (s *UsersService) ListInvitations(ctx context.Context, opts *ListOptions) ([]*RepositoryInvitation, *Response, error) {
231	u, err := addOptions("user/repository_invitations", opts)
232	if err != nil {
233		return nil, nil, err
234	}
235
236	req, err := s.client.NewRequest("GET", u, nil)
237	if err != nil {
238		return nil, nil, err
239	}
240
241	invites := []*RepositoryInvitation{}
242	resp, err := s.client.Do(ctx, req, &invites)
243	if err != nil {
244		return nil, resp, err
245	}
246
247	return invites, resp, nil
248}
249
250// AcceptInvitation accepts the currently-open repository invitation for the
251// authenticated user.
252//
253// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/repos/#accept-a-repository-invitation
254func (s *UsersService) AcceptInvitation(ctx context.Context, invitationID int64) (*Response, error) {
255	u := fmt.Sprintf("user/repository_invitations/%v", invitationID)
256	req, err := s.client.NewRequest("PATCH", u, nil)
257	if err != nil {
258		return nil, err
259	}
260
261	return s.client.Do(ctx, req, nil)
262}
263
264// DeclineInvitation declines the currently-open repository invitation for the
265// authenticated user.
266//
267// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/repos/#decline-a-repository-invitation
268func (s *UsersService) DeclineInvitation(ctx context.Context, invitationID int64) (*Response, error) {
269	u := fmt.Sprintf("user/repository_invitations/%v", invitationID)
270	req, err := s.client.NewRequest("DELETE", u, nil)
271	if err != nil {
272		return nil, err
273	}
274
275	return s.client.Do(ctx, req, nil)
276}
277