diff --git a/docs/data-sources/organization.md b/docs/data-sources/organization.md index ede2f381..201301a3 100644 --- a/docs/data-sources/organization.md +++ b/docs/data-sources/organization.md @@ -28,6 +28,6 @@ data "sentry_organization" "org" { ### Read-Only -- `id` (String) The ID of this resource. +- `id` (String) The unique URL slug for this organization. - `internal_id` (String) The internal ID for this organization. - `name` (String) The human readable name for this organization. diff --git a/go.mod b/go.mod index 56efe384..7bf8f105 100644 --- a/go.mod +++ b/go.mod @@ -24,8 +24,8 @@ require ( github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/yuin/goldmark v1.6.0 // indirect github.com/yuin/goldmark-meta v1.1.0 // indirect - golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // indirect + golang.org/x/tools v0.24.0 // indirect gopkg.in/yaml.v2 v2.3.0 // indirect ) @@ -80,8 +80,8 @@ require ( github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/zclconf/go-cty v1.15.0 // indirect golang.org/x/crypto v0.26.0 // indirect - golang.org/x/mod v0.19.0 // indirect - golang.org/x/net v0.25.0 // indirect + golang.org/x/mod v0.20.0 // indirect + golang.org/x/net v0.28.0 // indirect golang.org/x/sys v0.23.0 // indirect golang.org/x/text v0.17.0 // indirect google.golang.org/appengine v1.6.8 // indirect diff --git a/go.sum b/go.sum index 86e342ae..dd234c64 100644 --- a/go.sum +++ b/go.sum @@ -261,11 +261,15 @@ golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 h1:EDuYyU/MkFXllv9QF9819VlI9a4tzGuCbhG0ExK9o1U= golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk= +golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= +golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= @@ -275,6 +279,8 @@ golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= @@ -335,6 +341,7 @@ golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= diff --git a/internal/provider/data_source_organization.go b/internal/provider/data_source_organization.go new file mode 100644 index 00000000..aecdc855 --- /dev/null +++ b/internal/provider/data_source_organization.go @@ -0,0 +1,94 @@ +package provider + +import ( + "context" + "fmt" + "net/http" + + "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 = &OrganizationDataSource{} +var _ datasource.DataSourceWithConfigure = &OrganizationDataSource{} + +type OrganizationDataSourceModel struct { + Id types.String `tfsdk:"id"` + Slug types.String `tfsdk:"slug"` + Name types.String `tfsdk:"name"` + InternalId types.String `tfsdk:"internal_id"` +} + +func (m *OrganizationDataSourceModel) Fill(org sentry.Organization) error { + m.Id = types.StringPointerValue(org.Slug) + m.Slug = types.StringPointerValue(org.Slug) + m.Name = types.StringPointerValue(org.Name) + m.InternalId = types.StringPointerValue(org.ID) + + return nil +} + +func NewOrganizationDataSource() datasource.DataSource { + return &OrganizationDataSource{} +} + +type OrganizationDataSource struct { + baseDataSource +} + +func (d *OrganizationDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_organization" +} + +func (d *OrganizationDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "Sentry Organization data source.", + + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + MarkdownDescription: "The unique URL slug for this organization.", + Computed: true, + }, + "slug": schema.StringAttribute{ + MarkdownDescription: "The unique URL slug for this organization.", + Required: true, + }, + "internal_id": schema.StringAttribute{ + MarkdownDescription: "The internal ID for this organization.", + Computed: true, + }, + "name": schema.StringAttribute{ + MarkdownDescription: "The human readable name for this organization.", + Computed: true, + }, + }, + } +} + +func (d *OrganizationDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var data OrganizationDataSourceModel + + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + org, apiResp, err := d.client.Organizations.Get(ctx, data.Slug.ValueString()) + if apiResp.StatusCode == http.StatusNotFound { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Not found: %s", err.Error())) + return + } + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Read error: %s", err.Error())) + return + } + + if err := data.Fill(*org); 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_organization_test.go b/internal/provider/data_source_organization_test.go index 9509b8ab..33ede73d 100644 --- a/internal/provider/data_source_organization_test.go +++ b/internal/provider/data_source_organization_test.go @@ -2,10 +2,66 @@ package provider 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" "github.com/jianyuan/terraform-provider-sentry/internal/acctest" ) +func TestAccOrganizationDataSource(t *testing.T) { + rn := "data.sentry_organization.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccOrganizationDataSourceConfig, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(rn, tfjsonpath.New("id"), knownvalue.StringExact(acctest.TestOrganization)), + statecheck.ExpectKnownValue(rn, tfjsonpath.New("slug"), knownvalue.StringExact(acctest.TestOrganization)), + statecheck.ExpectKnownValue(rn, tfjsonpath.New("name"), knownvalue.NotNull()), + statecheck.ExpectKnownValue(rn, tfjsonpath.New("internal_id"), knownvalue.NotNull()), + }, + }, + }, + }) +} + +func TestAccOrganizationDataSource_MigrateFromPluginSDK(t *testing.T) { + rn := "data.sentry_organization.test" + + checks := []statecheck.StateCheck{ + statecheck.ExpectKnownValue(rn, tfjsonpath.New("slug"), knownvalue.StringExact(acctest.TestOrganization)), + statecheck.ExpectKnownValue(rn, tfjsonpath.New("name"), knownvalue.NotNull()), + statecheck.ExpectKnownValue(rn, tfjsonpath.New("internal_id"), knownvalue.NotNull()), + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + Steps: []resource.TestStep{ + { + ExternalProviders: map[string]resource.ExternalProvider{ + acctest.ProviderName: { + Source: "jianyuan/sentry", + VersionConstraint: "0.12.3", + }, + }, + Config: testAccOrganizationDataSourceConfig, + ConfigStateChecks: checks, + }, + { + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Config: testAccOrganizationDataSourceConfig, + ConfigStateChecks: checks, + }, + }, + }) +} + var testAccOrganizationDataSourceConfig = fmt.Sprintf(` data "sentry_organization" "test" { slug = "%s" diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 188b526a..de6ba9da 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -113,6 +113,7 @@ func (p *SentryProvider) DataSources(ctx context.Context) []func() datasource.Da NewAllProjectsDataSource, NewClientKeyDataSource, NewIssueAlertDataSource, + NewOrganizationDataSource, NewOrganizationIntegrationDataSource, NewOrganizationMemberDataSource, NewProjectDataSource, diff --git a/internal/provider/resource_issue_alert.go b/internal/provider/resource_issue_alert.go index 4c52feaf..5a7430a6 100644 --- a/internal/provider/resource_issue_alert.go +++ b/internal/provider/resource_issue_alert.go @@ -137,7 +137,9 @@ Please note the following changes since v0.12.0: "conditions": schema.StringAttribute{ MarkdownDescription: "List of conditions. In JSON string format.", Required: true, - CustomType: sentrytypes.LossyJsonType{}, + CustomType: sentrytypes.LossyJsonType{ + IgnoreKeys: []string{"name"}, + }, }, "filters": schema.StringAttribute{ MarkdownDescription: "A list of filters that determine if a rule fires after the necessary conditions have been met. In JSON string format.", diff --git a/internal/provider/resource_issue_alert_test.go b/internal/provider/resource_issue_alert_test.go index 68755c08..d8d4fb0e 100644 --- a/internal/provider/resource_issue_alert_test.go +++ b/internal/provider/resource_issue_alert_test.go @@ -374,7 +374,8 @@ resource "sentry_issue_alert" "test" { conditions = <