@@ -50,6 +50,7 @@ func (t *TemplateService) GetTemplates(ctx context.Context, orgID int64) ([]defi
50
50
templates := make ([]definitions.NotificationTemplate , 0 , len (revision .Config .TemplateFiles ))
51
51
for name , tmpl := range revision .Config .TemplateFiles {
52
52
tmpl := definitions.NotificationTemplate {
53
+ UID : legacy_storage .NameToUid (name ),
53
54
Name : name ,
54
55
Template : tmpl ,
55
56
ResourceVersion : calculateTemplateFingerprint (tmpl ),
@@ -65,30 +66,34 @@ func (t *TemplateService) GetTemplates(ctx context.Context, orgID int64) ([]defi
65
66
return templates , nil
66
67
}
67
68
68
- func (t * TemplateService ) GetTemplate (ctx context.Context , orgID int64 , name string ) (definitions.NotificationTemplate , error ) {
69
+ func (t * TemplateService ) GetTemplate (ctx context.Context , orgID int64 , nameOrUid string ) (definitions.NotificationTemplate , error ) {
69
70
revision , err := t .configStore .Get (ctx , orgID )
70
71
if err != nil {
71
72
return definitions.NotificationTemplate {}, err
72
73
}
73
74
74
- for tmplName , tmpl := range revision .Config .TemplateFiles {
75
- if tmplName != name {
76
- continue
77
- }
78
- tmpl := definitions.NotificationTemplate {
79
- Name : name ,
80
- Template : tmpl ,
81
- ResourceVersion : calculateTemplateFingerprint (tmpl ),
82
- }
75
+ existingName := nameOrUid
76
+ existingContent , ok := revision .Config .TemplateFiles [nameOrUid ]
77
+ if ! ok {
78
+ existingName , existingContent , ok = getTemplateByUid (revision .Config .TemplateFiles , nameOrUid )
79
+ }
80
+ if ! ok {
81
+ return definitions.NotificationTemplate {}, ErrTemplateNotFound .Errorf ("" )
82
+ }
83
83
84
- provenance , err := t .provenanceStore .GetProvenance (ctx , & tmpl , orgID )
85
- if err != nil {
86
- return definitions.NotificationTemplate {}, err
87
- }
88
- tmpl .Provenance = definitions .Provenance (provenance )
89
- return tmpl , nil
84
+ tmpl := definitions.NotificationTemplate {
85
+ UID : legacy_storage .NameToUid (existingName ),
86
+ Name : existingName ,
87
+ Template : existingContent ,
88
+ ResourceVersion : calculateTemplateFingerprint (existingContent ),
90
89
}
91
- return definitions.NotificationTemplate {}, ErrTemplateNotFound .Errorf ("" )
90
+
91
+ provenance , err := t .provenanceStore .GetProvenance (ctx , & tmpl , orgID )
92
+ if err != nil {
93
+ return definitions.NotificationTemplate {}, err
94
+ }
95
+ tmpl .Provenance = definitions .Provenance (provenance )
96
+ return tmpl , nil
92
97
}
93
98
94
99
func (t * TemplateService ) UpsertTemplate (ctx context.Context , orgID int64 , tmpl definitions.NotificationTemplate ) (definitions.NotificationTemplate , error ) {
@@ -107,7 +112,11 @@ func (t *TemplateService) UpsertTemplate(ctx context.Context, orgID int64, tmpl
107
112
if ! errors .Is (err , ErrTemplateNotFound ) {
108
113
return d , err
109
114
}
110
- if tmpl .ResourceVersion != "" { // if version is set then it's an update operation. Fail because resource does not exist anymore
115
+ // If template was not found, this is assumed to be a create operation except for two cases:
116
+ // - If a ResourceVersion is provided: we should assume that this was meant to be a conditional update operation.
117
+ // - If UID is provided: custom UID for templates is not currently supported, so this was meant to be an update
118
+ // operation without a ResourceVersion.
119
+ if tmpl .ResourceVersion != "" || tmpl .UID != "" {
111
120
return definitions.NotificationTemplate {}, ErrTemplateNotFound .Errorf ("" )
112
121
}
113
122
return t .createTemplate (ctx , revision , orgID , tmpl )
@@ -150,6 +159,7 @@ func (t *TemplateService) createTemplate(ctx context.Context, revision *legacy_s
150
159
}
151
160
152
161
return definitions.NotificationTemplate {
162
+ UID : legacy_storage .NameToUid (tmpl .Name ),
153
163
Name : tmpl .Name ,
154
164
Template : tmpl .Template ,
155
165
Provenance : tmpl .Provenance ,
@@ -175,12 +185,28 @@ func (t *TemplateService) updateTemplate(ctx context.Context, revision *legacy_s
175
185
revision .Config .TemplateFiles = map [string ]string {}
176
186
}
177
187
178
- existingName := tmpl .Name
179
- exisitingContent , found := revision .Config .TemplateFiles [existingName ]
188
+ var found bool
189
+ var existingName , existingContent string
190
+ // if UID is specified, look by UID.
191
+ if tmpl .UID != "" {
192
+ existingName , existingContent , found = getTemplateByUid (revision .Config .TemplateFiles , tmpl .UID )
193
+ // do not fall back to name because we address by UID, and resource can be deleted\renamed
194
+ } else {
195
+ existingName = tmpl .Name
196
+ existingContent , found = revision .Config .TemplateFiles [existingName ]
197
+ }
180
198
if ! found {
181
199
return definitions.NotificationTemplate {}, ErrTemplateNotFound .Errorf ("" )
182
200
}
183
201
202
+ if existingName != tmpl .Name { // if template is renamed, check if this name is already taken
203
+ _ , ok := revision .Config .TemplateFiles [tmpl .Name ]
204
+ if ok {
205
+ // return error if template is being renamed to one that already exists
206
+ return definitions.NotificationTemplate {}, ErrTemplateExists .Errorf ("" )
207
+ }
208
+ }
209
+
184
210
// check that provenance is not changed in an invalid way
185
211
storedProvenance , err := t .provenanceStore .GetProvenance (ctx , & tmpl , orgID )
186
212
if err != nil {
@@ -190,14 +216,22 @@ func (t *TemplateService) updateTemplate(ctx context.Context, revision *legacy_s
190
216
return definitions.NotificationTemplate {}, err
191
217
}
192
218
193
- err = t .checkOptimisticConcurrency (tmpl .Name , exisitingContent , models .Provenance (tmpl .Provenance ), tmpl .ResourceVersion , "update" )
219
+ err = t .checkOptimisticConcurrency (tmpl .Name , existingContent , models .Provenance (tmpl .Provenance ), tmpl .ResourceVersion , "update" )
194
220
if err != nil {
195
221
return definitions.NotificationTemplate {}, err
196
222
}
197
223
198
224
revision .Config .TemplateFiles [tmpl .Name ] = tmpl .Template
199
225
200
226
err = t .xact .InTransaction (ctx , func (ctx context.Context ) error {
227
+ if existingName != tmpl .Name { // if template by was found by UID and it's name is different, then this is the rename operation. Delete old resources.
228
+ delete (revision .Config .TemplateFiles , existingName )
229
+ err := t .provenanceStore .DeleteProvenance (ctx , & definitions.NotificationTemplate {Name : existingName }, orgID )
230
+ if err != nil {
231
+ return err
232
+ }
233
+ }
234
+
201
235
if err := t .configStore .Save (ctx , revision , orgID ); err != nil {
202
236
return err
203
237
}
@@ -208,14 +242,15 @@ func (t *TemplateService) updateTemplate(ctx context.Context, revision *legacy_s
208
242
}
209
243
210
244
return definitions.NotificationTemplate {
245
+ UID : legacy_storage .NameToUid (tmpl .Name ), // if name was changed, this UID will not match the incoming one
211
246
Name : tmpl .Name ,
212
247
Template : tmpl .Template ,
213
248
Provenance : tmpl .Provenance ,
214
249
ResourceVersion : calculateTemplateFingerprint (tmpl .Template ),
215
250
}, nil
216
251
}
217
252
218
- func (t * TemplateService ) DeleteTemplate (ctx context.Context , orgID int64 , name string , provenance definitions.Provenance , version string ) error {
253
+ func (t * TemplateService ) DeleteTemplate (ctx context.Context , orgID int64 , nameOrUid string , provenance definitions.Provenance , version string ) error {
219
254
revision , err := t .configStore .Get (ctx , orgID )
220
255
if err != nil {
221
256
return err
@@ -225,33 +260,37 @@ func (t *TemplateService) DeleteTemplate(ctx context.Context, orgID int64, name
225
260
return nil
226
261
}
227
262
228
- existing , ok := revision .Config .TemplateFiles [name ]
263
+ existingName := nameOrUid
264
+ existing , ok := revision .Config .TemplateFiles [nameOrUid ]
265
+ if ! ok {
266
+ existingName , existing , ok = getTemplateByUid (revision .Config .TemplateFiles , nameOrUid )
267
+ }
229
268
if ! ok {
230
269
return nil
231
270
}
232
271
233
- err = t .checkOptimisticConcurrency (name , existing , models .Provenance (provenance ), version , "delete" )
272
+ err = t .checkOptimisticConcurrency (existingName , existing , models .Provenance (provenance ), version , "delete" )
234
273
if err != nil {
235
274
return err
236
275
}
237
276
238
277
// check that provenance is not changed in an invalid way
239
- storedProvenance , err := t .provenanceStore .GetProvenance (ctx , & definitions.NotificationTemplate {Name : name }, orgID )
278
+ storedProvenance , err := t .provenanceStore .GetProvenance (ctx , & definitions.NotificationTemplate {Name : existingName }, orgID )
240
279
if err != nil {
241
280
return err
242
281
}
243
282
if err = t .validator (storedProvenance , models .Provenance (provenance )); err != nil {
244
283
return err
245
284
}
246
285
247
- delete (revision .Config .TemplateFiles , name )
286
+ delete (revision .Config .TemplateFiles , existingName )
248
287
249
288
return t .xact .InTransaction (ctx , func (ctx context.Context ) error {
250
289
if err := t .configStore .Save (ctx , revision , orgID ); err != nil {
251
290
return err
252
291
}
253
292
tgt := definitions.NotificationTemplate {
254
- Name : name ,
293
+ Name : existingName ,
255
294
}
256
295
return t .provenanceStore .DeleteProvenance (ctx , & tgt , orgID )
257
296
})
@@ -277,3 +316,12 @@ func calculateTemplateFingerprint(t string) string {
277
316
_ , _ = sum .Write (unsafe .Slice (unsafe .StringData (t ), len (t ))) //nolint:gosec
278
317
return fmt .Sprintf ("%016x" , sum .Sum64 ())
279
318
}
319
+
320
+ func getTemplateByUid (templates map [string ]string , uid string ) (string , string , bool ) {
321
+ for n , tmpl := range templates {
322
+ if legacy_storage .NameToUid (n ) == uid {
323
+ return n , tmpl , true
324
+ }
325
+ }
326
+ return "" , "" , false
327
+ }
0 commit comments