@@ -3,6 +3,7 @@ package grafana
33import (
44 "context"
55 "fmt"
6+ "reflect"
67 "strconv"
78 "strings"
89 "time"
@@ -100,7 +101,7 @@ func readContactPoint(ctx context.Context, data *schema.ResourceData, meta inter
100101 return diag .FromErr (err )
101102 }
102103 points := resp .Payload
103- if len (points ) == 0 {
104+ if len (points ) == 0 && ! strings . Contains ( name , ":" ) {
104105 // If the contact point was not found by name, try to fetch it by UID.
105106 // This is a deprecated ID format (uid;uid2;uid3)
106107 // TODO: Remove on the next major version
@@ -149,22 +150,17 @@ func updateContactPoint(ctx context.Context, data *schema.ResourceData, meta int
149150
150151 ps := unpackContactPoints (data )
151152
152- // If the contact point already exists, we need to fetch its current state so that we can compare it to the proposed state.
153- var currentPoints models.ContactPoints
154- if ! data .IsNewResource () {
155- name := data .Get ("name" ).(string )
156- resp , err := client .Provisioning .GetContactpoints (provisioning .NewGetContactpointsParams ().WithName (& name ))
157- if err != nil && ! common .IsNotFoundError (err ) {
158- return diag .FromErr (err )
159- }
160- if resp != nil {
161- currentPoints = resp .Payload
162- }
163- }
164-
165- processedUIDs := map [string ]bool {}
166153 for i := range ps {
167154 p := ps [i ]
155+ if p .deleted {
156+ uid := p .tfState ["uid" ].(string )
157+ // If the contact point is not in the proposed state, delete it.
158+ if _ , err := client .Provisioning .DeleteContactpoints (uid ); err != nil {
159+ return diag .Errorf ("failed to remove contact point notifier with UID %s from contact point %s: %v" , uid , data .Id (), err )
160+ }
161+ continue
162+ }
163+
168164 var uid string
169165 if uid = p .tfState ["uid" ].(string ); uid != "" {
170166 // If the contact point already has a UID, update it.
@@ -194,16 +190,6 @@ func updateContactPoint(ctx context.Context, data *schema.ResourceData, meta int
194190 // Since this is a new resource, the proposed state won't have a UID.
195191 // We need the UID so that we can later associate it with the config returned in the api response.
196192 ps [i ].tfState ["uid" ] = uid
197- processedUIDs [uid ] = true
198- }
199-
200- for _ , p := range currentPoints {
201- if _ , ok := processedUIDs [p .UID ]; ! ok {
202- // If the contact point is not in the proposed state, delete it.
203- if _ , err := client .Provisioning .DeleteContactpoints (p .UID ); err != nil {
204- return diag .Errorf ("failed to remove contact point notifier with UID %s from contact point %s: %v" , p .UID , p .Name , err )
205- }
206- }
207193 }
208194
209195 data .SetId (MakeOrgResourceID (orgID , data .Get ("name" ).(string )))
@@ -227,15 +213,55 @@ func deleteContactPoint(ctx context.Context, data *schema.ResourceData, meta int
227213 return nil
228214}
229215
216+ // unpackContactPoints unpacks the contact points from the Terraform state.
217+ // It returns a slice of statePairs, which contain the Terraform state and the Grafana state for each contact point.
218+ // It also tracks receivers that should be deleted. There are two cases where a receiver should be deleted:
219+ // - The receiver is present in the "new" part of the diff, but all fields are zeroed out (except UID).
220+ // - The receiver is present in the "old" part of the diff, but not in the "new" part.
230221func unpackContactPoints (data * schema.ResourceData ) []statePair {
231222 result := make ([]statePair , 0 )
232223 name := data .Get ("name" ).(string )
233224 for _ , n := range notifiers {
234- if points , ok := data .GetOk (n .meta ().field ); ok {
235- for _ , p := range points .(* schema.Set ).List () {
225+ oldPoints , newPoints := data .GetChange (n .meta ().field )
226+ oldPointsList := oldPoints .(* schema.Set ).List ()
227+ newPointsList := newPoints .(* schema.Set ).List ()
228+ if len (oldPointsList ) == 0 && len (newPointsList ) == 0 {
229+ continue
230+ }
231+ processedUIDs := map [string ]bool {}
232+ for _ , p := range newPointsList {
233+ // Checking if the point/receiver should be deleted
234+ // If all fields are zeroed out, except UID, then the receiver should be deleted
235+ deleted := false
236+ pointMap := p .(map [string ]interface {})
237+ if uid , ok := pointMap ["uid" ]; ok && uid != "" {
238+ deleted = true
239+ processedUIDs [uid .(string )] = true
240+ }
241+ for fieldName , fieldSchema := range n .schema ().Schema {
242+ if ! fieldSchema .Computed && fieldSchema .Required && ! reflect .ValueOf (pointMap [fieldName ]).IsZero () {
243+ deleted = false
244+ break
245+ }
246+ }
247+
248+ // Add the point/receiver to the result
249+ // If it's not deleted, it will either be created or updated
250+ result = append (result , statePair {
251+ tfState : pointMap ,
252+ gfState : unpackPointConfig (n , p , name ),
253+ deleted : deleted ,
254+ })
255+ }
256+ // Checking if the point/receiver should be deleted
257+ // If the point is not present in the "new" part of the diff, but is present in the "old" part, then the receiver should be deleted
258+ for _ , p := range oldPointsList {
259+ pointMap := p .(map [string ]interface {})
260+ if uid , ok := pointMap ["uid" ]; ok && uid != "" && ! processedUIDs [uid .(string )] {
236261 result = append (result , statePair {
237262 tfState : p .(map [string ]interface {}),
238- gfState : unpackPointConfig (n , p , name ),
263+ gfState : nil ,
264+ deleted : true ,
239265 })
240266 }
241267 }
@@ -294,7 +320,7 @@ func packCommonNotifierFields(p *models.EmbeddedContactPoint) map[string]interfa
294320func packSettings (p * models.EmbeddedContactPoint ) map [string ]interface {} {
295321 settings := map [string ]interface {}{}
296322 for k , v := range p .Settings .(map [string ]interface {}) {
297- settings [k ] = fmt .Sprintf ("%#v " , v )
323+ settings [k ] = fmt .Sprintf ("%s " , v )
298324 }
299325 return settings
300326}
@@ -344,6 +370,7 @@ type notifierMeta struct {
344370type statePair struct {
345371 tfState map [string ]interface {}
346372 gfState * models.EmbeddedContactPoint
373+ deleted bool
347374}
348375
349376func packNotifierStringField (gfSettings , tfSettings * map [string ]interface {}, gfKey , tfKey string ) {
0 commit comments