From fa69f1623491c62b873dcf0568dd928613a60c03 Mon Sep 17 00:00:00 2001 From: Marco Crivellaro Date: Wed, 16 Oct 2024 10:34:10 +0200 Subject: [PATCH 1/3] introduce sentry_all_organization_members resource --- .../data_source_all_organization_members.go | 128 ++++++++++++++++++ ...ta_source_all_organization_members_test.go | 55 ++++++++ internal/provider/provider.go | 1 + 3 files changed, 184 insertions(+) create mode 100644 internal/provider/data_source_all_organization_members.go create mode 100644 internal/provider/data_source_all_organization_members_test.go diff --git a/internal/provider/data_source_all_organization_members.go b/internal/provider/data_source_all_organization_members.go new file mode 100644 index 00000000..ca919fd6 --- /dev/null +++ b/internal/provider/data_source_all_organization_members.go @@ -0,0 +1,128 @@ +package provider + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/jianyuan/go-sentry/v2/sentry" +) + +var _ datasource.DataSource = &AllOrganizationMembersDataSource{} +var _ datasource.DataSourceWithConfigure = &OrganizationMemberDataSource{} + +func NewAllOrganizationMembersDataSource() datasource.DataSource { + return &AllOrganizationMembersDataSource{} +} + +type AllOrganizationMembersDataSource struct { + baseDataSource +} + +type AllOrganizationMembersDataSourceMemberModel struct { + Id types.String `tfsdk:"id"` + Email types.String `tfsdk:"email"` + Role types.String `tfsdk:"role"` +} + +func (m *AllOrganizationMembersDataSourceMemberModel) Fill(organization string, member sentry.OrganizationMember) error { + m.Id = types.StringValue(member.ID) + m.Email = types.StringValue(member.Email) + m.Role = types.StringValue(member.OrgRole) + + return nil +} + +type AllOrganizationMembersDataSourceModel struct { + Organization types.String `tfsdk:"organization"` + Members []AllOrganizationMembersDataSourceMemberModel `tfsdk:"members"` +} + +func (m *AllOrganizationMembersDataSourceModel) Fill(organization string, members []sentry.OrganizationMember) error { + m.Organization = types.StringValue(organization) + + for _, member := range members { + mm := AllOrganizationMembersDataSourceMemberModel{} + if err := mm.Fill(organization, member); err != nil { + return err + } + m.Members = append(m.Members, mm) + } + + return nil +} + +func (d *AllOrganizationMembersDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_all_organization_members" +} + +func (d *AllOrganizationMembersDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "Retrieve all organization members.", + + Attributes: map[string]schema.Attribute{ + "organization": schema.StringAttribute{ + MarkdownDescription: "The slug of the organization.", + Required: true, + }, + "members": schema.SetNestedAttribute{ + MarkdownDescription: "The list of members.", + Computed: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + MarkdownDescription: "The ID of of the organization member.", + Computed: true, + }, + "email": schema.StringAttribute{ + MarkdownDescription: "The email of the organization member.", + Required: true, + }, + "role": schema.StringAttribute{ + MarkdownDescription: "This is the role of the organization member.", + Computed: true, + }, + }, + }, + }, + }, + } +} + +func (d *AllOrganizationMembersDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var data AllOrganizationMembersDataSourceModel + + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + var allMembers []sentry.OrganizationMember + params := &sentry.ListCursorParams{} + + for { + members, apiResp, err := d.client.OrganizationMembers.List(ctx, data.Organization.ValueString(), params) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to list organization members, got error: %s", err)) + return + } + + for _, member := range members { + allMembers = append(allMembers, *member) + } + + if apiResp.Cursor == "" { + break + } + params.Cursor = apiResp.Cursor + } + + if err := data.Fill(data.Organization.ValueString(), allMembers); err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Fill error: %s", err)) + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/internal/provider/data_source_all_organization_members_test.go b/internal/provider/data_source_all_organization_members_test.go new file mode 100644 index 00000000..db519882 --- /dev/null +++ b/internal/provider/data_source_all_organization_members_test.go @@ -0,0 +1,55 @@ +package provider + +import ( + "fmt" + "regexp" + "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" + "github.com/jianyuan/terraform-provider-sentry/internal/acctest" +) + +func TestAccAllOrganizationMembersDataSource(t *testing.T) { + rn := "data.sentry_all_organization_members.test" + email := acctest.RandomWithPrefix("tf-member") + "@example.com" + role := "member" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccAllOrganizationMembersConfig(email, role), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(rn, tfjsonpath.New("organization"), knownvalue.StringExact(acctest.TestOrganization)), + statecheck.ExpectKnownValue(rn, tfjsonpath.New("members"), knownvalue.SetPartial([]knownvalue.Check{ + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "id": knownvalue.StringRegexp(regexp.MustCompile(`^\d+$`)), + "email": knownvalue.StringExact(email), + "role": knownvalue.StringExact(role), + }), + })), + }, + }, + }, + }) +} + +func testAccAllOrganizationMembersConfig(email string, role string) string { + return testAccOrganizationDataSourceConfig + fmt.Sprintf(` +resource "sentry_organization_member" "test" { + organization = data.sentry_organization.test.id + email = "%[1]s" + role = "%[2]s" +} + +data "sentry_all_organization_members" "test" { + organization = data.sentry_organization.test.id + + depends_on = [sentry_organization_member.test] +} +`, email, role) +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 1c9df1e6..64322c4a 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -112,6 +112,7 @@ func (p *SentryProvider) DataSources(ctx context.Context) []func() datasource.Da return []func() datasource.DataSource{ NewAllClientKeysDataSource, NewAllProjectsDataSource, + NewAllOrganizationMembersDataSource, NewClientKeyDataSource, NewIssueAlertDataSource, NewOrganizationDataSource, From eb7f37b63b764b9a48615b3a8ea7964e083f2edd Mon Sep 17 00:00:00 2001 From: Marco Crivellaro Date: Thu, 17 Oct 2024 09:52:32 +0200 Subject: [PATCH 2/3] docs update --- docs/data-sources/all_organization_members.md | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 docs/data-sources/all_organization_members.md diff --git a/docs/data-sources/all_organization_members.md b/docs/data-sources/all_organization_members.md new file mode 100644 index 00000000..9d952b7e --- /dev/null +++ b/docs/data-sources/all_organization_members.md @@ -0,0 +1,36 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "sentry_all_organization_members Data Source - terraform-provider-sentry" +subcategory: "" +description: |- + Retrieve all organization members. +--- + +# sentry_all_organization_members (Data Source) + +Retrieve all organization members. + + + + +## Schema + +### Required + +- `organization` (String) The slug of the organization. + +### Read-Only + +- `members` (Attributes Set) The list of members. (see [below for nested schema](#nestedatt--members)) + + +### Nested Schema for `members` + +Required: + +- `email` (String) The email of the organization member. + +Read-Only: + +- `id` (String) The ID of of the organization member. +- `role` (String) This is the role of the organization member. From 5514e6ec66fefe8eb404bf6bacb011324f42d1cb Mon Sep 17 00:00:00 2001 From: Marco Crivellaro Date: Thu, 17 Oct 2024 09:58:36 +0200 Subject: [PATCH 3/3] member email is readonly --- docs/data-sources/all_organization_members.md | 5 +---- internal/provider/data_source_all_organization_members.go | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/docs/data-sources/all_organization_members.md b/docs/data-sources/all_organization_members.md index 9d952b7e..b70a11cd 100644 --- a/docs/data-sources/all_organization_members.md +++ b/docs/data-sources/all_organization_members.md @@ -26,11 +26,8 @@ Retrieve all organization members. ### Nested Schema for `members` -Required: - -- `email` (String) The email of the organization member. - Read-Only: +- `email` (String) The email of the organization member. - `id` (String) The ID of of the organization member. - `role` (String) This is the role of the organization member. diff --git a/internal/provider/data_source_all_organization_members.go b/internal/provider/data_source_all_organization_members.go index ca919fd6..90c7913e 100644 --- a/internal/provider/data_source_all_organization_members.go +++ b/internal/provider/data_source_all_organization_members.go @@ -78,7 +78,7 @@ func (d *AllOrganizationMembersDataSource) Schema(ctx context.Context, req datas }, "email": schema.StringAttribute{ MarkdownDescription: "The email of the organization member.", - Required: true, + Computed: true, }, "role": schema.StringAttribute{ MarkdownDescription: "This is the role of the organization member.",