Skip to content

Commit

Permalink
fix: Ignore name attribute in issue alert conditions (#483)
Browse files Browse the repository at this point in the history
  • Loading branch information
jianyuan committed Sep 8, 2024
1 parent 1004388 commit 4a30ef1
Show file tree
Hide file tree
Showing 13 changed files with 187 additions and 102 deletions.
2 changes: 1 addition & 1 deletion docs/data-sources/organization.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
)

Expand Down Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand All @@ -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=
Expand Down Expand Up @@ -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=
Expand Down
94 changes: 94 additions & 0 deletions internal/provider/data_source_organization.go
Original file line number Diff line number Diff line change
@@ -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)...)
}
56 changes: 56 additions & 0 deletions internal/provider/data_source_organization_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ func (p *SentryProvider) DataSources(ctx context.Context) []func() datasource.Da
NewAllProjectsDataSource,
NewClientKeyDataSource,
NewIssueAlertDataSource,
NewOrganizationDataSource,
NewOrganizationIntegrationDataSource,
NewOrganizationMemberDataSource,
NewProjectDataSource,
Expand Down
4 changes: 3 additions & 1 deletion internal/provider/resource_issue_alert.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
Expand Down
3 changes: 2 additions & 1 deletion internal/provider/resource_issue_alert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,8 @@ resource "sentry_issue_alert" "test" {
conditions = <<EOT
[
{
"id": "sentry.rules.conditions.first_seen_event.FirstSeenEventCondition"
"id": "sentry.rules.conditions.first_seen_event.FirstSeenEventCondition",
"name": "ignored"
},
{
"id": "sentry.rules.conditions.regression_event.RegressionEventCondition"
Expand Down
7 changes: 6 additions & 1 deletion internal/sentrytypes/lossy_json_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,18 @@ var _ basetypes.StringTypable = (*LossyJsonType)(nil)

type LossyJsonType struct {
basetypes.StringType

IgnoreKeys []string
}

func (t LossyJsonType) String() string {
return "sentrytypes.LossyJsonType"
}

func (t LossyJsonType) ValueType(_ context.Context) attr.Value {
return LossyJson{}
return LossyJson{
IgnoreKeys: t.IgnoreKeys,
}
}

func (t LossyJsonType) Equal(o attr.Type) bool {
Expand Down Expand Up @@ -88,6 +92,7 @@ func (t LossyJsonType) Validate(ctx context.Context, in tftypes.Value, path path
func (t LossyJsonType) ValueFromString(ctx context.Context, in basetypes.StringValue) (basetypes.StringValuable, diag.Diagnostics) {
return LossyJson{
StringValue: in,
IgnoreKeys: t.IgnoreKeys,
}, nil
}

Expand Down
19 changes: 13 additions & 6 deletions internal/sentrytypes/lossy_json_value.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"slices"
"strings"

"github.com/hashicorp/terraform-plugin-framework/attr"
Expand All @@ -16,6 +17,8 @@ var _ basetypes.StringValuableWithSemanticEquals = (*LossyJson)(nil)

type LossyJson struct {
basetypes.StringValue

IgnoreKeys []string
}

func (v LossyJson) Type(_ context.Context) attr.Type {
Expand Down Expand Up @@ -48,7 +51,7 @@ func (v LossyJson) StringSemanticEquals(_ context.Context, newValuable basetypes
return false, diags
}

result, err := lossyJsonEqual(newValue.ValueString(), v.ValueString())
result, err := lossyJsonEqual(newValue.ValueString(), v.ValueString(), v.IgnoreKeys)

if err != nil {
diags.AddError(
Expand All @@ -64,7 +67,7 @@ func (v LossyJson) StringSemanticEquals(_ context.Context, newValuable basetypes
return result, diags
}

func lossyJsonEqual(s1, s2 string) (bool, error) {
func lossyJsonEqual(s1, s2 string, ignoreKeys []string) (bool, error) {
v1, err := decodeJson(s1)
if err != nil {
return false, err
Expand All @@ -75,7 +78,7 @@ func lossyJsonEqual(s1, s2 string) (bool, error) {
return false, err
}

return deepLossyEqual(v1, v2), nil
return deepLossyEqual(v1, v2, ignoreKeys), nil
}

func decodeJson(s string) (interface{}, error) {
Expand All @@ -90,7 +93,7 @@ func decodeJson(s string) (interface{}, error) {
return v, nil
}

func deepLossyEqual(v1, v2 interface{}) bool {
func deepLossyEqual(v1, v2 interface{}, ignoreKeys []string) bool {
switch v1 := v1.(type) {
case bool:
v2, ok := v2.(bool)
Expand Down Expand Up @@ -127,7 +130,7 @@ func deepLossyEqual(v1, v2 interface{}) bool {
}

for i := 0; i < len(v1); i++ {
if !deepLossyEqual(v1[i], v2[i]) {
if !deepLossyEqual(v1[i], v2[i], ignoreKeys) {
return false
}
}
Expand All @@ -145,12 +148,16 @@ func deepLossyEqual(v1, v2 interface{}) bool {

// Check that all keys in v1 are in v2 but not the other way around (lossy)
for k := range v1 {
if slices.Contains(ignoreKeys, k) {
continue
}

v2Val, ok := v2[k]
if !ok {
return false
}

if !deepLossyEqual(v1[k], v2Val) {
if !deepLossyEqual(v1[k], v2Val, ignoreKeys) {
return false
}
}
Expand Down
Loading

0 comments on commit 4a30ef1

Please sign in to comment.