@@ -50,6 +50,7 @@ func (t *TemplateService) GetTemplates(ctx context.Context, orgID int64) ([]defi
5050 templates := make ([]definitions.NotificationTemplate , 0 , len (revision .Config .TemplateFiles ))
5151 for name , tmpl := range revision .Config .TemplateFiles {
5252 tmpl := definitions.NotificationTemplate {
53+ UID : legacy_storage .NameToUid (name ),
5354 Name : name ,
5455 Template : tmpl ,
5556 ResourceVersion : calculateTemplateFingerprint (tmpl ),
@@ -65,30 +66,34 @@ func (t *TemplateService) GetTemplates(ctx context.Context, orgID int64) ([]defi
6566 return templates , nil
6667}
6768
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 ) {
6970 revision , err := t .configStore .Get (ctx , orgID )
7071 if err != nil {
7172 return definitions.NotificationTemplate {}, err
7273 }
7374
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+ }
8383
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 ),
9089 }
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
9297}
9398
9499func (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
107112 if ! errors .Is (err , ErrTemplateNotFound ) {
108113 return d , err
109114 }
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 != "" {
111120 return definitions.NotificationTemplate {}, ErrTemplateNotFound .Errorf ("" )
112121 }
113122 return t .createTemplate (ctx , revision , orgID , tmpl )
@@ -150,6 +159,7 @@ func (t *TemplateService) createTemplate(ctx context.Context, revision *legacy_s
150159 }
151160
152161 return definitions.NotificationTemplate {
162+ UID : legacy_storage .NameToUid (tmpl .Name ),
153163 Name : tmpl .Name ,
154164 Template : tmpl .Template ,
155165 Provenance : tmpl .Provenance ,
@@ -175,12 +185,28 @@ func (t *TemplateService) updateTemplate(ctx context.Context, revision *legacy_s
175185 revision .Config .TemplateFiles = map [string ]string {}
176186 }
177187
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+ }
180198 if ! found {
181199 return definitions.NotificationTemplate {}, ErrTemplateNotFound .Errorf ("" )
182200 }
183201
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+
184210 // check that provenance is not changed in an invalid way
185211 storedProvenance , err := t .provenanceStore .GetProvenance (ctx , & tmpl , orgID )
186212 if err != nil {
@@ -190,14 +216,22 @@ func (t *TemplateService) updateTemplate(ctx context.Context, revision *legacy_s
190216 return definitions.NotificationTemplate {}, err
191217 }
192218
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" )
194220 if err != nil {
195221 return definitions.NotificationTemplate {}, err
196222 }
197223
198224 revision .Config .TemplateFiles [tmpl .Name ] = tmpl .Template
199225
200226 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+
201235 if err := t .configStore .Save (ctx , revision , orgID ); err != nil {
202236 return err
203237 }
@@ -208,14 +242,15 @@ func (t *TemplateService) updateTemplate(ctx context.Context, revision *legacy_s
208242 }
209243
210244 return definitions.NotificationTemplate {
245+ UID : legacy_storage .NameToUid (tmpl .Name ), // if name was changed, this UID will not match the incoming one
211246 Name : tmpl .Name ,
212247 Template : tmpl .Template ,
213248 Provenance : tmpl .Provenance ,
214249 ResourceVersion : calculateTemplateFingerprint (tmpl .Template ),
215250 }, nil
216251}
217252
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 {
219254 revision , err := t .configStore .Get (ctx , orgID )
220255 if err != nil {
221256 return err
@@ -225,33 +260,37 @@ func (t *TemplateService) DeleteTemplate(ctx context.Context, orgID int64, name
225260 return nil
226261 }
227262
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+ }
229268 if ! ok {
230269 return nil
231270 }
232271
233- err = t .checkOptimisticConcurrency (name , existing , models .Provenance (provenance ), version , "delete" )
272+ err = t .checkOptimisticConcurrency (existingName , existing , models .Provenance (provenance ), version , "delete" )
234273 if err != nil {
235274 return err
236275 }
237276
238277 // 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 )
240279 if err != nil {
241280 return err
242281 }
243282 if err = t .validator (storedProvenance , models .Provenance (provenance )); err != nil {
244283 return err
245284 }
246285
247- delete (revision .Config .TemplateFiles , name )
286+ delete (revision .Config .TemplateFiles , existingName )
248287
249288 return t .xact .InTransaction (ctx , func (ctx context.Context ) error {
250289 if err := t .configStore .Save (ctx , revision , orgID ); err != nil {
251290 return err
252291 }
253292 tgt := definitions.NotificationTemplate {
254- Name : name ,
293+ Name : existingName ,
255294 }
256295 return t .provenanceStore .DeleteProvenance (ctx , & tgt , orgID )
257296 })
@@ -277,3 +316,12 @@ func calculateTemplateFingerprint(t string) string {
277316 _ , _ = sum .Write (unsafe .Slice (unsafe .StringData (t ), len (t ))) //nolint:gosec
278317 return fmt .Sprintf ("%016x" , sum .Sum64 ())
279318}
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