Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Updated repo collaborators to support ignoring teams #2481

Merged
merged 1 commit into from
Jan 17, 2025
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
117 changes: 84 additions & 33 deletions github/resource_github_repository_collaborators.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func resourceGithubRepositoryCollaborators() *schema.Resource {
"user": {
Type: schema.TypeSet,
Optional: true,
Description: "List of users",
Description: "List of users.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"permission": {
Expand All @@ -52,7 +52,7 @@ func resourceGithubRepositoryCollaborators() *schema.Resource {
"team": {
Type: schema.TypeSet,
Optional: true,
Description: "List of teams",
Description: "List of teams.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"permission": {
Expand All @@ -76,6 +76,20 @@ func resourceGithubRepositoryCollaborators() *schema.Resource {
},
Computed: true,
},
"ignore_team": {
Type: schema.TypeSet,
Optional: true,
Description: "List of teams to ignore.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"team_id": {
Type: schema.TypeString,
Description: "ID or slug of the team to ignore.",
Required: true,
},
},
},
},
},

CustomizeDiff: customdiff.Sequence(
Expand Down Expand Up @@ -145,16 +159,16 @@ func (c teamCollaborator) Empty() bool {
return c == teamCollaborator{}
}

func flattenTeamCollaborator(obj teamCollaborator, teamIDs []int64) interface{} {
func flattenTeamCollaborator(obj teamCollaborator, teamSlugs []string) interface{} {
if obj.Empty() {
return nil
}

var teamIDString string
if slices.Contains(teamIDs, obj.teamID) {
teamIDString = strconv.FormatInt(obj.teamID, 10)
} else {
if slices.Contains(teamSlugs, obj.teamSlug) {
teamIDString = obj.teamSlug
} else {
teamIDString = strconv.FormatInt(obj.teamID, 10)
}

transformed := map[string]interface{}{
Expand All @@ -165,7 +179,7 @@ func flattenTeamCollaborator(obj teamCollaborator, teamIDs []int64) interface{}
return transformed
}

func flattenTeamCollaborators(objs []teamCollaborator, teamIDs []int64) []interface{} {
func flattenTeamCollaborators(objs []teamCollaborator, teamSlugs []string) []interface{} {
if objs == nil {
return nil
}
Expand All @@ -176,14 +190,14 @@ func flattenTeamCollaborators(objs []teamCollaborator, teamIDs []int64) []interf

items := make([]interface{}, len(objs))
for i, obj := range objs {
items[i] = flattenTeamCollaborator(obj, teamIDs)
items[i] = flattenTeamCollaborator(obj, teamSlugs)
}

return items
}

func listUserCollaborators(client *github.Client, isOrg bool, ctx context.Context, owner, repoName string) ([]userCollaborator, error) {
var userCollaborators []userCollaborator
userCollaborators := make([]userCollaborator, 0)
affiliations := []string{"direct", "outside"}
for _, affiliation := range affiliations {
opt := &github.ListCollaboratorsOptions{ListOptions: github.ListOptions{
Expand Down Expand Up @@ -217,7 +231,7 @@ func listUserCollaborators(client *github.Client, isOrg bool, ctx context.Contex
}

func listInvitations(client *github.Client, ctx context.Context, owner, repoName string) ([]invitedCollaborator, error) {
var invitedCollaborators []invitedCollaborator
invitedCollaborators := make([]invitedCollaborator, 0)

opt := &github.ListOptions{PerPage: maxPerPage}
for {
Expand All @@ -230,7 +244,8 @@ func listInvitations(client *github.Client, ctx context.Context, owner, repoName
permissionName := getPermission(i.GetPermissions())

invitedCollaborators = append(invitedCollaborators, invitedCollaborator{
userCollaborator{permissionName, i.GetInvitee().GetLogin()}, i.GetID()})
userCollaborator{permissionName, i.GetInvitee().GetLogin()}, i.GetID(),
})
}

if resp.NextPage == 0 {
Expand All @@ -241,11 +256,11 @@ func listInvitations(client *github.Client, ctx context.Context, owner, repoName
return invitedCollaborators, nil
}

func listTeams(client *github.Client, isOrg bool, ctx context.Context, owner, repoName string) ([]teamCollaborator, error) {
var teamCollaborators []teamCollaborator
func listTeams(client *github.Client, isOrg bool, ctx context.Context, owner, repoName string, ignoreTeamIds []int64) ([]teamCollaborator, error) {
allTeams := make([]teamCollaborator, 0)

if !isOrg {
return teamCollaborators, nil
return allTeams, nil
}

opt := &github.ListOptions{PerPage: maxPerPage}
Expand All @@ -256,20 +271,23 @@ func listTeams(client *github.Client, isOrg bool, ctx context.Context, owner, re
}

for _, t := range repoTeams {
permissionName := getPermission(t.GetPermission())
if slices.Contains(ignoreTeamIds, t.GetID()) {
continue
}

teamCollaborators = append(teamCollaborators, teamCollaborator{permissionName, t.GetID(), t.GetSlug()})
allTeams = append(allTeams, teamCollaborator{permission: getPermission(t.GetPermission()), teamID: t.GetID(), teamSlug: t.GetSlug()})
}

if resp.NextPage == 0 {
break
}
opt.Page = resp.NextPage
}
return teamCollaborators, nil

return allTeams, nil
}

func listAllCollaborators(client *github.Client, isOrg bool, ctx context.Context, owner, repoName string) ([]userCollaborator, []invitedCollaborator, []teamCollaborator, error) {
func listAllCollaborators(client *github.Client, isOrg bool, ctx context.Context, owner, repoName string, ignoreTeamIds []int64) ([]userCollaborator, []invitedCollaborator, []teamCollaborator, error) {
userCollaborators, err := listUserCollaborators(client, isOrg, ctx, owner, repoName)
if err != nil {
return nil, nil, nil, err
Expand All @@ -278,16 +296,14 @@ func listAllCollaborators(client *github.Client, isOrg bool, ctx context.Context
if err != nil {
return nil, nil, nil, err
}
teamCollaborators, err := listTeams(client, isOrg, ctx, owner, repoName)
teamCollaborators, err := listTeams(client, isOrg, ctx, owner, repoName, ignoreTeamIds)
if err != nil {
return nil, nil, nil, err
}
return userCollaborators, invitations, teamCollaborators, err
}

func matchUserCollaboratorsAndInvites(
repoName string, want []interface{}, hasUsers []userCollaborator, hasInvites []invitedCollaborator,
meta interface{}) error {
func matchUserCollaboratorsAndInvites(repoName string, want []interface{}, hasUsers []userCollaborator, hasInvites []invitedCollaborator, meta interface{}) error {
client := meta.(*Owner).v3client

owner := meta.(*Owner).name
Expand Down Expand Up @@ -383,8 +399,7 @@ func matchUserCollaboratorsAndInvites(
return nil
}

func matchTeamCollaborators(
repoName string, want []interface{}, has []teamCollaborator, meta interface{}) error {
func matchTeamCollaborators(repoName string, want []interface{}, has []teamCollaborator, meta interface{}) error {
client := meta.(*Owner).v3client
orgID := meta.(*Owner).id
owner := meta.(*Owner).name
Expand Down Expand Up @@ -471,15 +486,15 @@ func resourceGithubRepositoryCollaboratorsCreate(d *schema.ResourceData, meta in
repoName := d.Get("repository").(string)
ctx := context.Background()

teamsMap := make(map[string]struct{})
teamsMap := make(map[string]struct{}, len(teams))
for _, team := range teams {
teamIDString := team.(map[string]interface{})["team_id"].(string)
if _, found := teamsMap[teamIDString]; found {
return fmt.Errorf("duplicate set member: %s", teamIDString)
}
teamsMap[teamIDString] = struct{}{}
}
usersMap := make(map[string]struct{})
usersMap := make(map[string]struct{}, len(users))
for _, user := range users {
username := user.(map[string]interface{})["username"].(string)
if _, found := usersMap[username]; found {
Expand All @@ -488,7 +503,12 @@ func resourceGithubRepositoryCollaboratorsCreate(d *schema.ResourceData, meta in
usersMap[username] = struct{}{}
}

userCollaborators, invitations, teamCollaborators, err := listAllCollaborators(client, isOrg, ctx, owner, repoName)
ignoreTeamIds, err := getIgnoreTeamIds(d, meta)
if err != nil {
return err
}

userCollaborators, invitations, teamCollaborators, err := listAllCollaborators(client, isOrg, ctx, owner, repoName, ignoreTeamIds)
if err != nil {
return deleteResourceOn404AndSwallow304OtherwiseReturnError(err, d, "repository collaborators (%s/%s)", owner, repoName)
}
Expand Down Expand Up @@ -516,7 +536,12 @@ func resourceGithubRepositoryCollaboratorsRead(d *schema.ResourceData, meta inte
repoName := d.Id()
ctx := context.WithValue(context.Background(), ctxId, d.Id())

userCollaborators, invitedCollaborators, teamCollaborators, err := listAllCollaborators(client, isOrg, ctx, owner, repoName)
ignoreTeamIds, err := getIgnoreTeamIds(d, meta)
if err != nil {
return err
}

userCollaborators, invitedCollaborators, teamCollaborators, err := listAllCollaborators(client, isOrg, ctx, owner, repoName, ignoreTeamIds)
if err != nil {
return deleteResourceOn404AndSwallow304OtherwiseReturnError(err, d, "repository collaborators (%s/%s)", owner, repoName)
}
Expand All @@ -526,9 +551,14 @@ func resourceGithubRepositoryCollaboratorsRead(d *schema.ResourceData, meta inte
invitationIds[i.username] = strconv.FormatInt(i.invitationID, 10)
}

teamIDs := make([]int64, len(teamCollaborators))
for i, t := range teamCollaborators {
teamIDs[i] = t.teamID
sourceTeams := d.Get("team").(*schema.Set).List()
teamSlugs := make([]string, len(sourceTeams))
for i, t := range sourceTeams {
teamIdString := t.(map[string]interface{})["team_id"].(string)
_, parseIntErr := strconv.ParseInt(teamIdString, 10, 64)
if parseIntErr != nil {
teamSlugs[i] = teamIdString
}
}

err = d.Set("repository", repoName)
Expand All @@ -539,7 +569,7 @@ func resourceGithubRepositoryCollaboratorsRead(d *schema.ResourceData, meta inte
if err != nil {
return err
}
err = d.Set("team", flattenTeamCollaborators(teamCollaborators, teamIDs))
err = d.Set("team", flattenTeamCollaborators(teamCollaborators, teamSlugs))
if err != nil {
return err
}
Expand All @@ -563,7 +593,12 @@ func resourceGithubRepositoryCollaboratorsDelete(d *schema.ResourceData, meta in
repoName := d.Get("repository").(string)
ctx := context.Background()

userCollaborators, invitations, teamCollaborators, err := listAllCollaborators(client, isOrg, ctx, owner, repoName)
ignoreTeamIds, err := getIgnoreTeamIds(d, meta)
if err != nil {
return err
}

userCollaborators, invitations, teamCollaborators, err := listAllCollaborators(client, isOrg, ctx, owner, repoName, ignoreTeamIds)
if err != nil {
return deleteResourceOn404AndSwallow304OtherwiseReturnError(err, d, "repository collaborators (%s/%s)", owner, repoName)
}
Expand All @@ -580,3 +615,19 @@ func resourceGithubRepositoryCollaboratorsDelete(d *schema.ResourceData, meta in
err = matchTeamCollaborators(repoName, nil, teamCollaborators, meta)
return err
}

func getIgnoreTeamIds(d *schema.ResourceData, meta interface{}) ([]int64, error) {
ignoreTeams := d.Get("ignore_team").(*schema.Set).List()
ignoreTeamIds := make([]int64, len(ignoreTeams))

for i, t := range ignoreTeams {
s := t.(map[string]interface{})["team_id"].(string)
id, err := getTeamID(s, meta)
if err != nil {
return nil, err
}
ignoreTeamIds[i] = id
}

return ignoreTeamIds, nil
}
Loading
Loading