@@ -15,10 +15,13 @@ import (
1515 "github.com/hashicorp/terraform-plugin-framework/resource/schema"
1616 "github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier"
1717 "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier"
18+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier"
1819 "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
20+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/setplanmodifier"
1921 "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
2022 "github.com/hashicorp/terraform-plugin-framework/schema/validator"
2123 "github.com/hashicorp/terraform-plugin-framework/types"
24+ "github.com/hashicorp/terraform-plugin-framework/types/basetypes"
2225 "github.com/jianyuan/go-utils/sliceutils"
2326 "github.com/jianyuan/terraform-provider-sentry/internal/apiclient"
2427 "github.com/jianyuan/terraform-provider-sentry/internal/diagutils"
@@ -34,6 +37,14 @@ type ProjectFilterResourceModel struct {
3437 ErrorMessages types.Set `tfsdk:"error_messages"`
3538}
3639
40+ func (m ProjectFilterResourceModel ) AttributeTypes () map [string ]attr.Type {
41+ return map [string ]attr.Type {
42+ "blacklisted_ips" : types.SetType {ElemType : types .StringType },
43+ "releases" : types.SetType {ElemType : types .StringType },
44+ "error_messages" : types.SetType {ElemType : types .StringType },
45+ }
46+ }
47+
3748func (m * ProjectFilterResourceModel ) Fill (ctx context.Context , project apiclient.Project ) (diags diag.Diagnostics ) {
3849 if project .Options == nil {
3950 m .BlacklistedIps = types .SetNull (types .StringType )
@@ -45,7 +56,7 @@ func (m *ProjectFilterResourceModel) Fill(ctx context.Context, project apiclient
4556
4657 if values , ok := project .Options ["filters:blacklisted_ips" ].(string ); ok {
4758 if values == "" {
48- m .BlacklistedIps = types .SetNull (types .StringType )
59+ m .BlacklistedIps = types .SetValueMust (types .StringType , []attr. Value {} )
4960 } else {
5061 m .BlacklistedIps = types .SetValueMust (types .StringType , sliceutils .Map (func (v string ) attr.Value {
5162 return types .StringValue (v )
@@ -57,7 +68,7 @@ func (m *ProjectFilterResourceModel) Fill(ctx context.Context, project apiclient
5768
5869 if values , ok := project .Options ["filters:releases" ].(string ); ok {
5970 if values == "" {
60- m .Releases = types .SetNull (types .StringType )
71+ m .Releases = types .SetValueMust (types .StringType , []attr. Value {} )
6172 } else {
6273 m .Releases = types .SetValueMust (types .StringType , sliceutils .Map (func (v string ) attr.Value {
6374 return types .StringValue (v )
@@ -69,7 +80,7 @@ func (m *ProjectFilterResourceModel) Fill(ctx context.Context, project apiclient
6980
7081 if values , ok := project .Options ["filters:error_messages" ].(string ); ok {
7182 if values == "" {
72- m .ErrorMessages = types .SetNull (types .StringType )
83+ m .ErrorMessages = types .SetValueMust (types .StringType , []attr. Value {} )
7384 } else {
7485 m .ErrorMessages = types .SetValueMust (types .StringType , sliceutils .Map (func (v string ) attr.Value {
7586 return types .StringValue (v )
@@ -83,22 +94,22 @@ func (m *ProjectFilterResourceModel) Fill(ctx context.Context, project apiclient
8394}
8495
8596type ProjectResourceModel struct {
86- Id types.String `tfsdk:"id"`
87- Organization types.String `tfsdk:"organization"`
88- Teams types.Set `tfsdk:"teams"`
89- Name types.String `tfsdk:"name"`
90- Slug types.String `tfsdk:"slug"`
91- Platform types.String `tfsdk:"platform"`
92- DefaultRules types.Bool `tfsdk:"default_rules"`
93- DefaultKey types.Bool `tfsdk:"default_key"`
94- InternalId types.String `tfsdk:"internal_id"`
95- Features types.Set `tfsdk:"features"`
96- DigestsMinDelay types.Int64 `tfsdk:"digests_min_delay"`
97- DigestsMaxDelay types.Int64 `tfsdk:"digests_max_delay"`
98- ResolveAge types.Int64 `tfsdk:"resolve_age"`
99- Filters * ProjectFilterResourceModel `tfsdk:"filters"`
100- FingerprintingRules sentrytypes.TrimmedString `tfsdk:"fingerprinting_rules"`
101- GroupingEnhancements sentrytypes.TrimmedString `tfsdk:"grouping_enhancements"`
97+ Id types.String `tfsdk:"id"`
98+ Organization types.String `tfsdk:"organization"`
99+ Teams types.Set `tfsdk:"teams"`
100+ Name types.String `tfsdk:"name"`
101+ Slug types.String `tfsdk:"slug"`
102+ Platform types.String `tfsdk:"platform"`
103+ DefaultRules types.Bool `tfsdk:"default_rules"`
104+ DefaultKey types.Bool `tfsdk:"default_key"`
105+ InternalId types.String `tfsdk:"internal_id"`
106+ Features types.Set `tfsdk:"features"`
107+ DigestsMinDelay types.Int64 `tfsdk:"digests_min_delay"`
108+ DigestsMaxDelay types.Int64 `tfsdk:"digests_max_delay"`
109+ ResolveAge types.Int64 `tfsdk:"resolve_age"`
110+ Filters types. Object `tfsdk:"filters"`
111+ FingerprintingRules sentrytypes.TrimmedString `tfsdk:"fingerprinting_rules"`
112+ GroupingEnhancements sentrytypes.TrimmedString `tfsdk:"grouping_enhancements"`
102113}
103114
104115func (m * ProjectResourceModel ) Fill (ctx context.Context , project apiclient.Project ) (diags diag.Diagnostics ) {
@@ -125,9 +136,12 @@ func (m *ProjectResourceModel) Fill(ctx context.Context, project apiclient.Proje
125136 m .DigestsMaxDelay = types .Int64Value (project .DigestsMaxDelay )
126137 m .ResolveAge = types .Int64Value (project .ResolveAge )
127138
128- if m .Filters != nil {
129- diags .Append (m .Filters .Fill (ctx , project )... )
130- }
139+ var filters ProjectFilterResourceModel
140+ diags .Append (filters .Fill (ctx , project )... )
141+
142+ var filtersDiags diag.Diagnostics
143+ m .Filters , filtersDiags = types .ObjectValueFrom (ctx , filters .AttributeTypes (), filters )
144+ diags .Append (filtersDiags ... )
131145
132146 m .FingerprintingRules = sentrytypes .TrimmedStringValue (project .FingerprintingRules )
133147 m .GroupingEnhancements = sentrytypes .TrimmedStringValue (project .GroupingEnhancements )
@@ -209,6 +223,9 @@ func (r *ProjectResource) Schema(ctx context.Context, req resource.SchemaRequest
209223 "features" : schema.SetAttribute {
210224 ElementType : types .StringType ,
211225 Computed : true ,
226+ PlanModifiers : []planmodifier.Set {
227+ setplanmodifier .UseStateForUnknown (),
228+ },
212229 },
213230 "digests_min_delay" : schema.Int64Attribute {
214231 Description : "The minimum amount of time (in seconds) to wait between scheduling digests for delivery after the initial scheduling." ,
@@ -237,32 +254,48 @@ func (r *ProjectResource) Schema(ctx context.Context, req resource.SchemaRequest
237254 "filters" : schema.SingleNestedAttribute {
238255 Description : "Custom filters for this project." ,
239256 Optional : true ,
257+ Computed : true ,
240258 Attributes : map [string ]schema.Attribute {
241259 "blacklisted_ips" : schema.SetAttribute {
242260 Description : "Filter events from these IP addresses. (e.g. 127.0.0.1 or 10.0.0.0/8)" ,
243261 ElementType : types .StringType ,
244262 Optional : true ,
263+ Computed : true ,
245264 Validators : []validator.Set {
246265 setvalidator .SizeAtLeast (1 ),
247266 },
267+ PlanModifiers : []planmodifier.Set {
268+ setplanmodifier .UseStateForUnknown (),
269+ },
248270 },
249271 "releases" : schema.SetAttribute {
250272 MarkdownDescription : "Filter events from these releases. Allows [glob pattern matching](https://en.wikipedia.org/wiki/Glob_(programming)). (e.g. 1.* or [!3].[0-9].*)" ,
251273 ElementType : types .StringType ,
252274 Optional : true ,
275+ Computed : true ,
253276 Validators : []validator.Set {
254277 setvalidator .SizeAtLeast (1 ),
255278 },
279+ PlanModifiers : []planmodifier.Set {
280+ setplanmodifier .UseStateForUnknown (),
281+ },
256282 },
257283 "error_messages" : schema.SetAttribute {
258284 MarkdownDescription : "Filter events by error messages. Allows [glob pattern matching](https://en.wikipedia.org/wiki/Glob_(programming)). (e.g. TypeError* or *: integer division or modulo by zero)" ,
259285 ElementType : types .StringType ,
260286 Optional : true ,
287+ Computed : true ,
261288 Validators : []validator.Set {
262289 setvalidator .SizeAtLeast (1 ),
263290 },
291+ PlanModifiers : []planmodifier.Set {
292+ setplanmodifier .UseStateForUnknown (),
293+ },
264294 },
265295 },
296+ PlanModifiers : []planmodifier.Object {
297+ objectplanmodifier .UseStateForUnknown (),
298+ },
266299 },
267300 "fingerprinting_rules" : schema.StringAttribute {
268301 MarkdownDescription : "This can be used to modify the fingerprint rules on the server with custom rules. Rules follow the pattern `matcher:glob -> fingerprint, values`. To learn more about fingerprint rules, [read the docs](https://docs.sentry.io/concepts/data-management/event-grouping/fingerprint-rules/)." ,
@@ -355,35 +388,35 @@ func (r *ProjectResource) Create(ctx context.Context, req resource.CreateRequest
355388 updateBody .ResolveAge = data .ResolveAge .ValueInt64Pointer ()
356389 }
357390
358- if data .Filters != nil {
391+ if ! data .Filters .IsUnknown () {
392+ var filters ProjectFilterResourceModel
393+ resp .Diagnostics .Append (data .Filters .As (ctx , & filters , basetypes.ObjectAsOptions {})... )
394+ if resp .Diagnostics .HasError () {
395+ return
396+ }
397+
359398 options := make (map [string ]interface {})
360- if data .Filters .BlacklistedIps .IsNull () {
361- options ["filters:blacklisted_ips" ] = ""
362- } else {
363- values := []string {}
364- resp .Diagnostics .Append (data .Filters .BlacklistedIps .ElementsAs (ctx , & values , false )... )
399+ if ! filters .BlacklistedIps .IsUnknown () {
400+ var values []string
401+ resp .Diagnostics .Append (filters .BlacklistedIps .ElementsAs (ctx , & values , false )... )
365402 if resp .Diagnostics .HasError () {
366403 return
367404 }
368405 options ["filters:blacklisted_ips" ] = strings .Join (values , "\n " )
369406 }
370407
371- if data .Filters .Releases .IsNull () {
372- options ["filters:releases" ] = ""
373- } else {
374- values := []string {}
375- resp .Diagnostics .Append (data .Filters .Releases .ElementsAs (ctx , & values , false )... )
408+ if ! filters .Releases .IsUnknown () {
409+ var values []string
410+ resp .Diagnostics .Append (filters .Releases .ElementsAs (ctx , & values , false )... )
376411 if resp .Diagnostics .HasError () {
377412 return
378413 }
379414 options ["filters:releases" ] = strings .Join (values , "\n " )
380415 }
381416
382- if data .Filters .ErrorMessages .IsNull () {
383- options ["filters:error_messages" ] = ""
384- } else {
385- values := []string {}
386- resp .Diagnostics .Append (data .Filters .ErrorMessages .ElementsAs (ctx , & values , false )... )
417+ if ! filters .ErrorMessages .IsUnknown () {
418+ var values []string
419+ resp .Diagnostics .Append (filters .ErrorMessages .ElementsAs (ctx , & values , false )... )
387420 if resp .Diagnostics .HasError () {
388421 return
389422 }
@@ -538,35 +571,36 @@ func (r *ProjectResource) Update(ctx context.Context, req resource.UpdateRequest
538571 updateBody .ResolveAge = plan .ResolveAge .ValueInt64Pointer ()
539572 }
540573
541- if plan .Filters != nil {
574+ if ! plan .Filters .Equal (state .Filters ) {
575+ var filtersPlan , filtersState ProjectFilterResourceModel
576+ resp .Diagnostics .Append (plan .Filters .As (ctx , & filtersPlan , basetypes.ObjectAsOptions {})... )
577+ resp .Diagnostics .Append (state .Filters .As (ctx , & filtersState , basetypes.ObjectAsOptions {})... )
578+ if resp .Diagnostics .HasError () {
579+ return
580+ }
581+
542582 options := make (map [string ]interface {})
543- if plan .Filters .BlacklistedIps .IsNull () {
544- options ["filters:blacklisted_ips" ] = ""
545- } else {
546- values := []string {}
547- resp .Diagnostics .Append (plan .Filters .BlacklistedIps .ElementsAs (ctx , & values , false )... )
583+ if ! filtersPlan .BlacklistedIps .Equal (filtersState .BlacklistedIps ) {
584+ var values []string
585+ resp .Diagnostics .Append (filtersPlan .BlacklistedIps .ElementsAs (ctx , & values , false )... )
548586 if resp .Diagnostics .HasError () {
549587 return
550588 }
551589 options ["filters:blacklisted_ips" ] = strings .Join (values , "\n " )
552590 }
553591
554- if plan .Filters .Releases .IsNull () {
555- options ["filters:releases" ] = ""
556- } else {
557- values := []string {}
558- resp .Diagnostics .Append (plan .Filters .Releases .ElementsAs (ctx , & values , false )... )
592+ if ! filtersPlan .Releases .Equal (filtersState .Releases ) {
593+ var values []string
594+ resp .Diagnostics .Append (filtersPlan .Releases .ElementsAs (ctx , & values , false )... )
559595 if resp .Diagnostics .HasError () {
560596 return
561597 }
562598 options ["filters:releases" ] = strings .Join (values , "\n " )
563599 }
564600
565- if plan .Filters .ErrorMessages .IsNull () {
566- options ["filters:error_messages" ] = ""
567- } else {
568- values := []string {}
569- resp .Diagnostics .Append (plan .Filters .ErrorMessages .ElementsAs (ctx , & values , false )... )
601+ if ! filtersPlan .ErrorMessages .Equal (filtersState .ErrorMessages ) {
602+ var values []string
603+ resp .Diagnostics .Append (filtersPlan .ErrorMessages .ElementsAs (ctx , & values , false )... )
570604 if resp .Diagnostics .HasError () {
571605 return
572606 }
0 commit comments