diff --git a/RESOURCES.md b/RESOURCES.md index 31da8864d9..f509264d9d 100644 --- a/RESOURCES.md +++ b/RESOURCES.md @@ -69,7 +69,7 @@ The overall status of each resource or data source is captured in this document | `github_dependabot_public_key` | ⚠️ | ✅ | ❓ | ❓ | ❓ | ❓ | | `github_dependabot_secrets` | ⚠️ | ✅ | ❓ | ❓ | ❓ | ❓ | | `github_enterprise` | ⚠️ | ✅ | ❓ | ❓ | ❓ | ❓ | -| `github_external_groups` | ⚠️ | ✅ | ❓ | ❓ | ❓ | ❓ | +| `github_external_groups` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | `github_ip_ranges` | ⚠️ | ✅ | ❓ | ❓ | ❓ | ❓ | | `github_issue_labels` | ⚠️ | ✅ | ❓ | ❓ | ❓ | ❓ | | `github_membership` | ⚠️ | ✅ | ❓ | ❓ | ❓ | ❓ | diff --git a/docs/data-sources/external_groups.md b/docs/data-sources/external_groups.md index 7823355c6f..60a74c5d16 100644 --- a/docs/data-sources/external_groups.md +++ b/docs/data-sources/external_groups.md @@ -1,37 +1,58 @@ --- page_title: "github_external_groups (Data Source) - GitHub" +subcategory: "" description: |- - Retrieve external groups belonging to an organization. + Data source to list all external groups in an organization. --- # github_external_groups (Data Source) -Use this data source to retrieve external groups belonging to an organization. +Data source to list all external groups in an organization. ## Example Usage ```terraform -data "github_external_groups" "example_external_groups" {} +data "github_external_groups" "example" {} +``` -locals { - local_groups = data.github_external_groups.example_external_groups -} + + +## Schema + +### Optional + +- `display_name_filter` (String) Filter external groups by display name. + +### Read-Only + +- `external_groups` (List of Object) List of external groups in the organization. (see [below for nested schema](#nestedatt--external_groups)) +- `id` (String) The ID of this resource. + + +### Nested Schema for `external_groups` + +Read-Only: -- `group_id` - the ID of the group. -- `group_name` - the name of the group. -- `updated_at` - the date the group was last updated. +- `group_id` (Number) ID of the external group. +- `group_name` (String) Name of the external group. +- `updated_at` (String) Timestamp of the last update to the external group. diff --git a/examples/data-sources/external_groups/example_1.tf b/examples/data-sources/external_groups/example_1.tf deleted file mode 100644 index 6c0f3976c1..0000000000 --- a/examples/data-sources/external_groups/example_1.tf +++ /dev/null @@ -1,9 +0,0 @@ -data "github_external_groups" "example_external_groups" {} - -locals { - local_groups = data.github_external_groups.example_external_groups -} - -output "groups" { - value = local.local_groups -} diff --git a/examples/data-sources/github_external_groups/data-source_1.tf b/examples/data-sources/github_external_groups/data-source_1.tf new file mode 100644 index 0000000000..a4be2f083e --- /dev/null +++ b/examples/data-sources/github_external_groups/data-source_1.tf @@ -0,0 +1 @@ +data "github_external_groups" "example" {} diff --git a/github/acc_test.go b/github/acc_test.go index 191d5b9e97..d623abeec1 100644 --- a/github/acc_test.go +++ b/github/acc_test.go @@ -73,7 +73,10 @@ type testAccConfig struct { testExternalUser2 string // Enterprise test configuration - testEnterpriseEMUGroupId int + testEnterpriseEMUGroupId int + testExternalGroup1ID int + testExternalGroup1DisplayName string + testExternalGroup2ID int // Test options testAdvancedSecurity bool diff --git a/github/data_source_github_external_groups.go b/github/data_source_github_external_groups.go index da3be6b2eb..b57cf336f4 100644 --- a/github/data_source_github_external_groups.go +++ b/github/data_source_github_external_groups.go @@ -2,8 +2,6 @@ package github import ( "context" - "encoding/json" - "fmt" "github.com/google/go-github/v89/github" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" @@ -13,23 +11,33 @@ import ( func dataSourceGithubExternalGroups() *schema.Resource { return &schema.Resource{ ReadContext: dataSourceGithubExternalGroupsRead, + Description: "Data source to list all external groups in an organization.", Schema: map[string]*schema.Schema{ + "display_name_filter": { + Description: "Filter external groups by display name.", + Type: schema.TypeString, + Optional: true, + }, "external_groups": { - Type: schema.TypeList, - Computed: true, + Description: "List of external groups in the organization.", + Type: schema.TypeList, + Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "group_id": { - Type: schema.TypeInt, - Computed: true, + Description: "ID of the external group.", + Type: schema.TypeInt, + Computed: true, }, "group_name": { - Type: schema.TypeString, - Computed: true, + Description: "Name of the external group.", + Type: schema.TypeString, + Computed: true, }, "updated_at": { - Type: schema.TypeString, - Computed: true, + Description: "Timestamp of the last update to the external group.", + Type: schema.TypeString, + Computed: true, }, }, }, @@ -38,48 +46,50 @@ func dataSourceGithubExternalGroups() *schema.Resource { } } -func dataSourceGithubExternalGroupsRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { - err := checkOrganization(meta) - if err != nil { +func dataSourceGithubExternalGroupsRead(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics { + meta, _ := m.(*Owner) + + if err := checkOrganization(meta); err != nil { return diag.FromErr(err) } - client := meta.(*Owner).v3client - orgName := meta.(*Owner).name - opts := &github.ListExternalGroupsOptions{} - - externalGroups := new(github.ExternalGroupList) + var filter *string + if v, ok := d.GetOk("display_name_filter"); ok { + s, _ := v.(string) + filter = &s + } - for { - groups, resp, err := client.Teams.ListExternalGroups(ctx, orgName, opts) + groups := make([]map[string]any, 0) + for group, err := range meta.v3client.Teams.ListExternalGroupsIter(ctx, meta.name, &github.ListExternalGroupsOptions{DisplayName: filter, ListOptions: github.ListOptions{PerPage: maxPerPage}}) { if err != nil { return diag.FromErr(err) } - externalGroups.Groups = append(externalGroups.Groups, groups.Groups...) - - if resp.NextPage == 0 { - break + g := map[string]any{ + "group_id": group.GetGroupID(), + "group_name": group.GetGroupName(), + "updated_at": group.GetUpdatedAt().String(), } - opts.Page = resp.NextPage - } - // convert to JSON in order to martial to format we can return - jsonGroups, err := json.Marshal(externalGroups.Groups) - if err != nil { - return diag.FromErr(err) + groups = append(groups, g) } - groupsState := make([]map[string]any, 0) - err = json.Unmarshal(jsonGroups, &groupsState) - if err != nil { - return diag.FromErr(err) + var id string + if filter != nil { + s, err := buildID(meta.name, *filter) + if err != nil { + return diag.FromErr(err) + } + id = s + } else { + id = meta.name } - if err := d.Set("external_groups", groupsState); err != nil { - return diag.FromErr(err) + d.SetId(id) + + if err := d.Set("external_groups", groups); err != nil { + return diag.Errorf("error setting external_groups: %v", err) } - d.SetId(fmt.Sprintf("/orgs/%v/external-groups", orgName)) return nil } diff --git a/github/data_source_github_external_groups_test.go b/github/data_source_github_external_groups_test.go new file mode 100644 index 0000000000..009e729fed --- /dev/null +++ b/github/data_source_github_external_groups_test.go @@ -0,0 +1,82 @@ +package github + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/statecheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" +) + +func TestAccDataSourceGithubExternalGroups(t *testing.T) { + t.Parallel() + + if testAccConf.authMode != enterprise && !testAccConf.enterpriseIsEMU { + t.Skip("Skipping as test mode is not enterprise using EMU") + } + + t.Run("queries_all_external_groups", func(t *testing.T) { + t.Parallel() + + if testAccConf.testExternalGroup1ID == 0 { + t.Skip("Skipping as no external groups are configured for the test organization") + } + + groupID := int32(testAccConf.testExternalGroup1ID) + + config := ` +data "github_external_groups" "example" {} +` + + resource.Test(t, resource.TestCase{ + ProviderFactories: providerFactories, + Steps: []resource.TestStep{ + { + Config: config, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue("data.github_external_groups.example", tfjsonpath.New("groups"), knownvalue.SetPartial([]knownvalue.Check{ + knownvalue.MapPartial(map[string]knownvalue.Check{ + "group_id": knownvalue.Int32Exact(groupID), + }), + })), + }, + }, + }, + }) + }) + + t.Run("queries_external_groups_with_filter", func(t *testing.T) { + t.Parallel() + + if testAccConf.testExternalGroup1ID == 0 || testAccConf.testExternalGroup1DisplayName == "" || testAccConf.testExternalGroup2ID == 0 { + t.Skip("Skipping as no external groups are configured for the test organization") + } + + groupID := int32(testAccConf.testExternalGroup1ID) + + config := fmt.Sprintf(` +data "github_external_groups" "example" { + display_name_filter = "%s" +} +`, testAccConf.testExternalGroup1DisplayName) + + // TODO: Use new not in set matcher when available from #3537 + resource.Test(t, resource.TestCase{ + ProviderFactories: providerFactories, + Steps: []resource.TestStep{ + { + Config: config, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue("data.github_external_groups.example", tfjsonpath.New("groups"), knownvalue.ListExact([]knownvalue.Check{ + knownvalue.MapPartial(map[string]knownvalue.Check{ + "group_id": knownvalue.Int32Exact(groupID), + }), + })), + }, + }, + }, + }) + }) +} diff --git a/templates/data-sources/external_groups.md.tmpl b/templates/data-sources/external_groups.md.tmpl index 206555129b..3260662c94 100644 --- a/templates/data-sources/external_groups.md.tmpl +++ b/templates/data-sources/external_groups.md.tmpl @@ -1,27 +1,48 @@ --- page_title: "{{.Name}} ({{.Type}}) - {{.RenderedProviderName}}" +subcategory: "" description: |- - Retrieve external groups belonging to an organization. +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} --- # {{.Name}} ({{.Type}}) -Use this data source to retrieve external groups belonging to an organization. +{{ .Description | trimspace }} +{{ if .HasExamples -}} ## Example Usage -{{ tffile "examples/data-sources/external_groups/example_1.tf" }} +{{- range .ExampleFiles }} -## Argument Reference +{{ tffile . }} +{{- end }} +{{- end }} -N/A. This resource will retrieve all the external groups belonging to an organization. +" }} +{{ $line }} +{{- end }} +{{- end }} +--> -## Attributes Reference +## Schema -- `external_groups` - an array of external groups belonging to the organization. Each group consists of the fields documented below. +### Optional ---- +- `display_name_filter` (String) Filter external groups by display name. + +### Read-Only + +- `external_groups` (List of Object) List of external groups in the organization. (see [below for nested schema](#nestedatt--external_groups)) +- `id` (String) The ID of this resource. + + +### Nested Schema for `external_groups` + +Read-Only: -- `group_id` - the ID of the group. -- `group_name` - the name of the group. -- `updated_at` - the date the group was last updated. +- `group_id` (Number) ID of the external group. +- `group_name` (String) Name of the external group. +- `updated_at` (String) Timestamp of the last update to the external group.