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 "strings" 12 "time" 13) 14 15// Team represents a team within a GitHub organization. Teams are used to 16// manage access to an organization's repositories. 17type Team struct { 18 ID *int64 `json:"id,omitempty"` 19 Name *string `json:"name,omitempty"` 20 Description *string `json:"description,omitempty"` 21 URL *string `json:"url,omitempty"` 22 Slug *string `json:"slug,omitempty"` 23 24 // Permission specifies the default permission for repositories owned by the team. 25 Permission *string `json:"permission,omitempty"` 26 27 // Privacy identifies the level of privacy this team should have. 28 // Possible values are: 29 // secret - only visible to organization owners and members of this team 30 // closed - visible to all members of this organization 31 // Default is "secret". 32 Privacy *string `json:"privacy,omitempty"` 33 34 MembersCount *int `json:"members_count,omitempty"` 35 ReposCount *int `json:"repos_count,omitempty"` 36 Organization *Organization `json:"organization,omitempty"` 37 MembersURL *string `json:"members_url,omitempty"` 38 RepositoriesURL *string `json:"repositories_url,omitempty"` 39 Parent *Team `json:"parent,omitempty"` 40 41 // LDAPDN is only available in GitHub Enterprise and when the team 42 // membership is synchronized with LDAP. 43 LDAPDN *string `json:"ldap_dn,omitempty"` 44} 45 46func (t Team) String() string { 47 return Stringify(t) 48} 49 50// Invitation represents a team member's invitation status. 51type Invitation struct { 52 ID *int64 `json:"id,omitempty"` 53 Login *string `json:"login,omitempty"` 54 Email *string `json:"email,omitempty"` 55 // Role can be one of the values - 'direct_member', 'admin', 'billing_manager', 'hiring_manager', or 'reinstate'. 56 Role *string `json:"role,omitempty"` 57 CreatedAt *time.Time `json:"created_at,omitempty"` 58 Inviter *User `json:"inviter,omitempty"` 59} 60 61func (i Invitation) String() string { 62 return Stringify(i) 63} 64 65// ListTeams lists all of the teams for an organization. 66// 67// GitHub API docs: https://developer.github.com/v3/orgs/teams/#list-teams 68func (s *OrganizationsService) ListTeams(ctx context.Context, org string, opt *ListOptions) ([]*Team, *Response, error) { 69 u := fmt.Sprintf("orgs/%v/teams", org) 70 u, err := addOptions(u, opt) 71 if err != nil { 72 return nil, nil, err 73 } 74 75 req, err := s.client.NewRequest("GET", u, nil) 76 if err != nil { 77 return nil, nil, err 78 } 79 80 // TODO: remove custom Accept header when this API fully launches. 81 req.Header.Set("Accept", mediaTypeNestedTeamsPreview) 82 83 var teams []*Team 84 resp, err := s.client.Do(ctx, req, &teams) 85 if err != nil { 86 return nil, resp, err 87 } 88 89 return teams, resp, nil 90} 91 92// GetTeam fetches a team by ID. 93// 94// GitHub API docs: https://developer.github.com/v3/orgs/teams/#get-team 95func (s *OrganizationsService) GetTeam(ctx context.Context, team int64) (*Team, *Response, error) { 96 u := fmt.Sprintf("teams/%v", team) 97 req, err := s.client.NewRequest("GET", u, nil) 98 if err != nil { 99 return nil, nil, err 100 } 101 102 // TODO: remove custom Accept header when this API fully launches. 103 req.Header.Set("Accept", mediaTypeNestedTeamsPreview) 104 105 t := new(Team) 106 resp, err := s.client.Do(ctx, req, t) 107 if err != nil { 108 return nil, resp, err 109 } 110 111 return t, resp, nil 112} 113 114// NewTeam represents a team to be created or modified. 115type NewTeam struct { 116 Name string `json:"name"` // Name of the team. (Required.) 117 Description *string `json:"description,omitempty"` 118 Maintainers []string `json:"maintainers,omitempty"` 119 RepoNames []string `json:"repo_names,omitempty"` 120 ParentTeamID *int64 `json:"parent_team_id,omitempty"` 121 122 // Deprecated: Permission is deprecated when creating or editing a team in an org 123 // using the new GitHub permission model. It no longer identifies the 124 // permission a team has on its repos, but only specifies the default 125 // permission a repo is initially added with. Avoid confusion by 126 // specifying a permission value when calling AddTeamRepo. 127 Permission *string `json:"permission,omitempty"` 128 129 // Privacy identifies the level of privacy this team should have. 130 // Possible values are: 131 // secret - only visible to organization owners and members of this team 132 // closed - visible to all members of this organization 133 // Default is "secret". 134 Privacy *string `json:"privacy,omitempty"` 135 136 // LDAPDN may be used in GitHub Enterprise when the team membership 137 // is synchronized with LDAP. 138 LDAPDN *string `json:"ldap_dn,omitempty"` 139} 140 141func (s NewTeam) String() string { 142 return Stringify(s) 143} 144 145// CreateTeam creates a new team within an organization. 146// 147// GitHub API docs: https://developer.github.com/v3/orgs/teams/#create-team 148func (s *OrganizationsService) CreateTeam(ctx context.Context, org string, team *NewTeam) (*Team, *Response, error) { 149 u := fmt.Sprintf("orgs/%v/teams", org) 150 req, err := s.client.NewRequest("POST", u, team) 151 if err != nil { 152 return nil, nil, err 153 } 154 155 // TODO: remove custom Accept header when this API fully launches. 156 req.Header.Set("Accept", mediaTypeNestedTeamsPreview) 157 158 t := new(Team) 159 resp, err := s.client.Do(ctx, req, t) 160 if err != nil { 161 return nil, resp, err 162 } 163 164 return t, resp, nil 165} 166 167// EditTeam edits a team. 168// 169// GitHub API docs: https://developer.github.com/v3/orgs/teams/#edit-team 170func (s *OrganizationsService) EditTeam(ctx context.Context, id int64, team *NewTeam) (*Team, *Response, error) { 171 u := fmt.Sprintf("teams/%v", id) 172 req, err := s.client.NewRequest("PATCH", u, team) 173 if err != nil { 174 return nil, nil, err 175 } 176 177 // TODO: remove custom Accept header when this API fully launches. 178 req.Header.Set("Accept", mediaTypeNestedTeamsPreview) 179 180 t := new(Team) 181 resp, err := s.client.Do(ctx, req, t) 182 if err != nil { 183 return nil, resp, err 184 } 185 186 return t, resp, nil 187} 188 189// DeleteTeam deletes a team. 190// 191// GitHub API docs: https://developer.github.com/v3/orgs/teams/#delete-team 192func (s *OrganizationsService) DeleteTeam(ctx context.Context, team int64) (*Response, error) { 193 u := fmt.Sprintf("teams/%v", team) 194 req, err := s.client.NewRequest("DELETE", u, nil) 195 if err != nil { 196 return nil, err 197 } 198 199 req.Header.Set("Accept", mediaTypeNestedTeamsPreview) 200 201 return s.client.Do(ctx, req, nil) 202} 203 204// OrganizationListTeamMembersOptions specifies the optional parameters to the 205// OrganizationsService.ListTeamMembers method. 206type OrganizationListTeamMembersOptions struct { 207 // Role filters members returned by their role in the team. Possible 208 // values are "all", "member", "maintainer". Default is "all". 209 Role string `url:"role,omitempty"` 210 211 ListOptions 212} 213 214// ListChildTeams lists child teams for a team. 215// 216// GitHub API docs: https://developer.github.com/v3/orgs/teams/#list-child-teams 217func (s *OrganizationsService) ListChildTeams(ctx context.Context, teamID int64, opt *ListOptions) ([]*Team, *Response, error) { 218 u := fmt.Sprintf("teams/%v/teams", teamID) 219 u, err := addOptions(u, opt) 220 if err != nil { 221 return nil, nil, err 222 } 223 224 req, err := s.client.NewRequest("GET", u, nil) 225 if err != nil { 226 return nil, nil, err 227 } 228 229 req.Header.Set("Accept", mediaTypeNestedTeamsPreview) 230 231 var teams []*Team 232 resp, err := s.client.Do(ctx, req, &teams) 233 if err != nil { 234 return nil, resp, err 235 } 236 237 return teams, resp, nil 238} 239 240// ListTeamMembers lists all of the users who are members of the specified 241// team. 242// 243// GitHub API docs: https://developer.github.com/v3/orgs/teams/#list-team-members 244func (s *OrganizationsService) ListTeamMembers(ctx context.Context, team int64, opt *OrganizationListTeamMembersOptions) ([]*User, *Response, error) { 245 u := fmt.Sprintf("teams/%v/members", team) 246 u, err := addOptions(u, opt) 247 if err != nil { 248 return nil, nil, err 249 } 250 251 req, err := s.client.NewRequest("GET", u, nil) 252 if err != nil { 253 return nil, nil, err 254 } 255 256 req.Header.Set("Accept", mediaTypeNestedTeamsPreview) 257 258 var members []*User 259 resp, err := s.client.Do(ctx, req, &members) 260 if err != nil { 261 return nil, resp, err 262 } 263 264 return members, resp, nil 265} 266 267// IsTeamMember checks if a user is a member of the specified team. 268// 269// GitHub API docs: https://developer.github.com/v3/orgs/teams/#get-team-member 270// 271// Deprecated: This API has been marked as deprecated in the Github API docs, 272// OrganizationsService.GetTeamMembership method should be used instead. 273func (s *OrganizationsService) IsTeamMember(ctx context.Context, team int64, user string) (bool, *Response, error) { 274 u := fmt.Sprintf("teams/%v/members/%v", team, user) 275 req, err := s.client.NewRequest("GET", u, nil) 276 if err != nil { 277 return false, nil, err 278 } 279 280 resp, err := s.client.Do(ctx, req, nil) 281 member, err := parseBoolResponse(err) 282 return member, resp, err 283} 284 285// ListTeamRepos lists the repositories that the specified team has access to. 286// 287// GitHub API docs: https://developer.github.com/v3/orgs/teams/#list-team-repos 288func (s *OrganizationsService) ListTeamRepos(ctx context.Context, team int64, opt *ListOptions) ([]*Repository, *Response, error) { 289 u := fmt.Sprintf("teams/%v/repos", team) 290 u, err := addOptions(u, opt) 291 if err != nil { 292 return nil, nil, err 293 } 294 295 req, err := s.client.NewRequest("GET", u, nil) 296 if err != nil { 297 return nil, nil, err 298 } 299 300 // TODO: remove custom Accept header when topics API fully launches. 301 headers := []string{mediaTypeTopicsPreview, mediaTypeNestedTeamsPreview} 302 req.Header.Set("Accept", strings.Join(headers, ", ")) 303 304 var repos []*Repository 305 resp, err := s.client.Do(ctx, req, &repos) 306 if err != nil { 307 return nil, resp, err 308 } 309 310 return repos, resp, nil 311} 312 313// IsTeamRepo checks if a team manages the specified repository. If the 314// repository is managed by team, a Repository is returned which includes the 315// permissions team has for that repo. 316// 317// GitHub API docs: https://developer.github.com/v3/orgs/teams/#check-if-a-team-manages-a-repository 318func (s *OrganizationsService) IsTeamRepo(ctx context.Context, team int64, owner string, repo string) (*Repository, *Response, error) { 319 u := fmt.Sprintf("teams/%v/repos/%v/%v", team, owner, repo) 320 req, err := s.client.NewRequest("GET", u, nil) 321 if err != nil { 322 return nil, nil, err 323 } 324 325 headers := []string{mediaTypeOrgPermissionRepo, mediaTypeNestedTeamsPreview} 326 req.Header.Set("Accept", strings.Join(headers, ", ")) 327 328 repository := new(Repository) 329 resp, err := s.client.Do(ctx, req, repository) 330 if err != nil { 331 return nil, resp, err 332 } 333 334 return repository, resp, nil 335} 336 337// OrganizationAddTeamRepoOptions specifies the optional parameters to the 338// OrganizationsService.AddTeamRepo method. 339type OrganizationAddTeamRepoOptions struct { 340 // Permission specifies the permission to grant the team on this repository. 341 // Possible values are: 342 // pull - team members can pull, but not push to or administer this repository 343 // push - team members can pull and push, but not administer this repository 344 // admin - team members can pull, push and administer this repository 345 // 346 // If not specified, the team's permission attribute will be used. 347 Permission string `json:"permission,omitempty"` 348} 349 350// AddTeamRepo adds a repository to be managed by the specified team. The 351// specified repository must be owned by the organization to which the team 352// belongs, or a direct fork of a repository owned by the organization. 353// 354// GitHub API docs: https://developer.github.com/v3/orgs/teams/#add-team-repo 355func (s *OrganizationsService) AddTeamRepo(ctx context.Context, team int64, owner string, repo string, opt *OrganizationAddTeamRepoOptions) (*Response, error) { 356 u := fmt.Sprintf("teams/%v/repos/%v/%v", team, owner, repo) 357 req, err := s.client.NewRequest("PUT", u, opt) 358 if err != nil { 359 return nil, err 360 } 361 362 return s.client.Do(ctx, req, nil) 363} 364 365// RemoveTeamRepo removes a repository from being managed by the specified 366// team. Note that this does not delete the repository, it just removes it 367// from the team. 368// 369// GitHub API docs: https://developer.github.com/v3/orgs/teams/#remove-team-repo 370func (s *OrganizationsService) RemoveTeamRepo(ctx context.Context, team int64, owner string, repo string) (*Response, error) { 371 u := fmt.Sprintf("teams/%v/repos/%v/%v", team, owner, repo) 372 req, err := s.client.NewRequest("DELETE", u, nil) 373 if err != nil { 374 return nil, err 375 } 376 377 return s.client.Do(ctx, req, nil) 378} 379 380// ListUserTeams lists a user's teams 381// GitHub API docs: https://developer.github.com/v3/orgs/teams/#list-user-teams 382func (s *OrganizationsService) ListUserTeams(ctx context.Context, opt *ListOptions) ([]*Team, *Response, error) { 383 u := "user/teams" 384 u, err := addOptions(u, opt) 385 if err != nil { 386 return nil, nil, err 387 } 388 389 req, err := s.client.NewRequest("GET", u, nil) 390 if err != nil { 391 return nil, nil, err 392 } 393 394 // TODO: remove custom Accept header when this API fully launches. 395 req.Header.Set("Accept", mediaTypeNestedTeamsPreview) 396 397 var teams []*Team 398 resp, err := s.client.Do(ctx, req, &teams) 399 if err != nil { 400 return nil, resp, err 401 } 402 403 return teams, resp, nil 404} 405 406// GetTeamMembership returns the membership status for a user in a team. 407// 408// GitHub API docs: https://developer.github.com/v3/orgs/teams/#get-team-membership 409func (s *OrganizationsService) GetTeamMembership(ctx context.Context, team int64, user string) (*Membership, *Response, error) { 410 u := fmt.Sprintf("teams/%v/memberships/%v", team, user) 411 req, err := s.client.NewRequest("GET", u, nil) 412 if err != nil { 413 return nil, nil, err 414 } 415 416 req.Header.Set("Accept", mediaTypeNestedTeamsPreview) 417 418 t := new(Membership) 419 resp, err := s.client.Do(ctx, req, t) 420 if err != nil { 421 return nil, resp, err 422 } 423 424 return t, resp, nil 425} 426 427// OrganizationAddTeamMembershipOptions does stuff specifies the optional 428// parameters to the OrganizationsService.AddTeamMembership method. 429type OrganizationAddTeamMembershipOptions struct { 430 // Role specifies the role the user should have in the team. Possible 431 // values are: 432 // member - a normal member of the team 433 // maintainer - a team maintainer. Able to add/remove other team 434 // members, promote other team members to team 435 // maintainer, and edit the team’s name and description 436 // 437 // Default value is "member". 438 Role string `json:"role,omitempty"` 439} 440 441// AddTeamMembership adds or invites a user to a team. 442// 443// In order to add a membership between a user and a team, the authenticated 444// user must have 'admin' permissions to the team or be an owner of the 445// organization that the team is associated with. 446// 447// If the user is already a part of the team's organization (meaning they're on 448// at least one other team in the organization), this endpoint will add the 449// user to the team. 450// 451// If the user is completely unaffiliated with the team's organization (meaning 452// they're on none of the organization's teams), this endpoint will send an 453// invitation to the user via email. This newly-created membership will be in 454// the "pending" state until the user accepts the invitation, at which point 455// the membership will transition to the "active" state and the user will be 456// added as a member of the team. 457// 458// GitHub API docs: https://developer.github.com/v3/orgs/teams/#add-team-membership 459func (s *OrganizationsService) AddTeamMembership(ctx context.Context, team int64, user string, opt *OrganizationAddTeamMembershipOptions) (*Membership, *Response, error) { 460 u := fmt.Sprintf("teams/%v/memberships/%v", team, user) 461 req, err := s.client.NewRequest("PUT", u, opt) 462 if err != nil { 463 return nil, nil, err 464 } 465 466 t := new(Membership) 467 resp, err := s.client.Do(ctx, req, t) 468 if err != nil { 469 return nil, resp, err 470 } 471 472 return t, resp, nil 473} 474 475// RemoveTeamMembership removes a user from a team. 476// 477// GitHub API docs: https://developer.github.com/v3/orgs/teams/#remove-team-membership 478func (s *OrganizationsService) RemoveTeamMembership(ctx context.Context, team int64, user string) (*Response, error) { 479 u := fmt.Sprintf("teams/%v/memberships/%v", team, user) 480 req, err := s.client.NewRequest("DELETE", u, nil) 481 if err != nil { 482 return nil, err 483 } 484 485 return s.client.Do(ctx, req, nil) 486} 487 488// ListPendingTeamInvitations get pending invitaion list in team. 489// Warning: The API may change without advance notice during the preview period. 490// Preview features are not supported for production use. 491// 492// GitHub API docs: https://developer.github.com/v3/orgs/teams/#list-pending-team-invitations 493func (s *OrganizationsService) ListPendingTeamInvitations(ctx context.Context, team int64, opt *ListOptions) ([]*Invitation, *Response, error) { 494 u := fmt.Sprintf("teams/%v/invitations", team) 495 u, err := addOptions(u, opt) 496 if err != nil { 497 return nil, nil, err 498 } 499 500 req, err := s.client.NewRequest("GET", u, nil) 501 if err != nil { 502 return nil, nil, err 503 } 504 505 var pendingInvitations []*Invitation 506 resp, err := s.client.Do(ctx, req, &pendingInvitations) 507 if err != nil { 508 return nil, resp, err 509 } 510 511 return pendingInvitations, resp, nil 512} 513