Skip to content

Commit

Permalink
SUP-2214 - Create data sources for organization_members and `organi…
Browse files Browse the repository at this point in the history
…zation_member` (#590)

* Create buildkite_organization_members data source

* Update schema

* Generate genqlient code

* Create test for organization_members

* Implement paging for organization_members data source

* Create buildkite_organization_member data source

* Update schema

* Cleanup

* Fix linter error

* Return string literal

* Update organization member(s) tests

* Remove unused test functions

* Rename organization_member test

* Cleanup wording

* Regen docs

* Increase organization members returned to 500
  • Loading branch information
petetomasik authored Dec 11, 2024
1 parent 3fcb09b commit f9076d0
Show file tree
Hide file tree
Showing 13 changed files with 1,034 additions and 33 deletions.
104 changes: 104 additions & 0 deletions buildkite/data_source_organization_member.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package buildkite

import (
"context"
"fmt"

"github.com/MakeNowJust/heredoc"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
)

type organizationMemberDatasourceModel struct {
ID types.String `tfsdk:"id"`
UUID types.String `tfsdk:"uuid"`
Name types.String `tfsdk:"name"`
Email types.String `tfsdk:"email"`
}

type organizationMemberDatasource struct {
client *Client
}

func newOrganizationMemberDatasource() datasource.DataSource {
return &organizationMemberDatasource{}
}

func (o *organizationMemberDatasource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
if req.ProviderData == nil {
return
}

o.client = req.ProviderData.(*Client)
}

func (o *organizationMemberDatasource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_organization_member"
}

func (o *organizationMemberDatasource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
MarkdownDescription: heredoc.Doc(`
Use this data source to retrieve a specific organization member, using their email. You can find out more about organization members in the Buildkite
[documentation](https://buildkite.com/docs/platform/team-management).
`),
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
MarkdownDescription: "The GraphQL ID of the organization member.",
Computed: true,
},
"uuid": schema.StringAttribute{
MarkdownDescription: "The UUID of the organization member.",
Computed: true,
},
"name": schema.StringAttribute{
MarkdownDescription: "The name of the organization member.",
Computed: true,
},
"email": schema.StringAttribute{
MarkdownDescription: "The email address of the organization member.",
Required: true,
},
},
}
}

func (o *organizationMemberDatasource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var state organizationMemberDatasourceModel

resp.Diagnostics.Append(req.Config.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}

res, err := GetOrganizationMemberByEmail(ctx, o.client.genqlient, o.client.organization, state.Email.ValueString())
if err != nil {
resp.Diagnostics.AddError(
"Unable to get organization member",
fmt.Sprintf("Error getting organization member: %s", err.Error()),
)
return
}

if len(res.Organization.Members.Edges) == 0 {
resp.Diagnostics.AddError(
"No organization member found",
fmt.Sprintf("Organization member not found: %s", state.Email.ValueString()),
)
return
}

for _, member := range res.Organization.Members.Edges {
updateOrganizationMemberDatasourceState(&state, member)
}

resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}

func updateOrganizationMemberDatasourceState(state *organizationMemberDatasourceModel, data GetOrganizationMemberByEmailOrganizationMembersOrganizationMemberConnectionEdgesOrganizationMemberEdge) {
state.ID = types.StringValue(data.Node.User.Id)
state.UUID = types.StringValue(data.Node.User.Uuid)
state.Name = types.StringValue(data.Node.User.Name)
state.Email = types.StringValue(data.Node.User.Email)
}
34 changes: 34 additions & 0 deletions buildkite/data_source_organization_member_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package buildkite

import (
"testing"

"github.com/hashicorp/terraform-plugin-testing/helper/resource"
)

func testDatasourceOrganizationMemberConfig() string {
return `
data "buildkite_organization_members" "members" {}
data "buildkite_organization_member" "member" {
email = data.buildkite_organization_members.members.members[0].email
}
`
}

func TestAccBuildkiteOrganizationMemberDatasource(t *testing.T) {
t.Run("loads an organization member data source with required attribute", func(t *testing.T) {
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProtoV6ProviderFactories: protoV6ProviderFactories(),
Steps: []resource.TestStep{
{
Config: testDatasourceOrganizationMemberConfig(),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet("data.buildkite_organization_member.member", "id"),
),
},
},
})
})
}
127 changes: 127 additions & 0 deletions buildkite/data_source_organization_members.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package buildkite

import (
"context"
"fmt"

"github.com/MakeNowJust/heredoc"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
)

type organizationMembersDatasourceModel struct {
Members []organizationMembersModel `tfsdk:"members"`
}

type organizationMembersModel struct {
ID types.String `tfsdk:"id"`
UUID types.String `tfsdk:"uuid"`
Name types.String `tfsdk:"name"`
Email types.String `tfsdk:"email"`
}

type organizationMembersDatasource struct {
client *Client
}

func newOrganizationMembersDatasource() datasource.DataSource {
return &organizationMembersDatasource{}
}

func (o *organizationMembersDatasource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
if req.ProviderData == nil {
return
}

o.client = req.ProviderData.(*Client)
}

func (o *organizationMembersDatasource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_organization_members"
}

func (o *organizationMembersDatasource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
MarkdownDescription: heredoc.Doc(`
Use this data source to retrieve a members of an organization. You can find out more about organization members in the Buildkite
[documentation](https://buildkite.com/docs/platform/team-management).
`),
Attributes: map[string]schema.Attribute{
"members": schema.ListNestedAttribute{
Computed: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
MarkdownDescription: "The GraphQL ID of the organization member.",
Computed: true,
},
"uuid": schema.StringAttribute{
MarkdownDescription: "The UUID of the organization member.",
Computed: true,
},
"name": schema.StringAttribute{
MarkdownDescription: "The name of the organization member.",
Computed: true,
},
"email": schema.StringAttribute{
MarkdownDescription: "The email address of the organization member.",
Computed: true,
},
},
},
},
},
}
}

func (o *organizationMembersDatasource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var state organizationMembersDatasourceModel

resp.Diagnostics.Append(req.Config.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}

var cursor *string
for {
res, err := GetOrganizationMembers(ctx, o.client.genqlient, o.client.organization, cursor)
if err != nil {
resp.Diagnostics.AddError(
"Unable to get organization members",
fmt.Sprintf("Error getting organization members: %s", err.Error()),
)
return
}

if len(res.Organization.Members.Edges) == 0 {
resp.Diagnostics.AddError(
"No organization members found",
fmt.Sprintf("Error getting members for organization: %s", o.client.organization),
)
return
}

for _, member := range res.Organization.Members.Edges {
updateOrganizationMembersDatasourceState(&state, member)
}

if !res.Organization.Members.PageInfo.HasNextPage {
break
}

cursor = &res.Organization.Members.PageInfo.EndCursor
}
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}

func updateOrganizationMembersDatasourceState(state *organizationMembersDatasourceModel, data GetOrganizationMembersOrganizationMembersOrganizationMemberConnectionEdgesOrganizationMemberEdge) {
memberState := organizationMembersModel{
ID: types.StringValue(data.Node.User.Id),
UUID: types.StringValue(data.Node.User.Uuid),
Name: types.StringValue(data.Node.User.Name),
Email: types.StringValue(data.Node.User.Email),
}

state.Members = append(state.Members, memberState)
}
24 changes: 24 additions & 0 deletions buildkite/data_source_organization_members_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package buildkite

import (
"testing"

"github.com/hashicorp/terraform-plugin-testing/helper/resource"
)

func TestAccBuildkiteOrganizationMembersDatasource(t *testing.T) {
t.Run("organization members data source can be loaded with defaults", func(t *testing.T) {
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProtoV6ProviderFactories: protoV6ProviderFactories(),
Steps: []resource.TestStep{
{
Config: `data "buildkite_organization_members" "members" {}`,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet("data.buildkite_organization_members.members", "members.0.email"),
),
},
},
})
})
}
Loading

0 comments on commit f9076d0

Please sign in to comment.