Skip to content

Commit e16f435

Browse files
fix: preserve existing template fields in PATCH requests
Fixes issue where updating only specific fields (like default_ttl_ms) would unintentionally clear other fields (like icon, display_name, description) when they weren't provided in the PATCH request. The fix preserves existing values for fields that are not explicitly provided in the request, following the expected PATCH semantics. Fixes #19036 Co-authored-by: matifali <10648092+matifali@users.noreply.github.com>
1 parent 8ba8b4f commit e16f435

File tree

2 files changed

+35
-16
lines changed

2 files changed

+35
-16
lines changed

coderd/templates.go

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -771,12 +771,34 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
771771
classicTemplateFlow = *req.UseClassicParameterFlow
772772
}
773773

774+
// Users should not be able to clear the template name in the UI
775+
name := req.Name
776+
if name == "" {
777+
name = template.Name
778+
}
779+
780+
// Preserve existing values for fields not provided in PATCH request
781+
displayName := req.DisplayName
782+
if displayName == "" {
783+
displayName = template.DisplayName
784+
}
785+
786+
description := req.Description
787+
if description == "" {
788+
description = template.Description
789+
}
790+
791+
icon := req.Icon
792+
if icon == "" {
793+
icon = template.Icon
794+
}
795+
774796
var updated database.Template
775797
err = api.Database.InTx(func(tx database.Store) error {
776-
if req.Name == template.Name &&
777-
req.Description == template.Description &&
778-
req.DisplayName == template.DisplayName &&
779-
req.Icon == template.Icon &&
798+
if name == template.Name &&
799+
description == template.Description &&
800+
displayName == template.DisplayName &&
801+
icon == template.Icon &&
780802
req.AllowUserAutostart == template.AllowUserAutostart &&
781803
req.AllowUserAutostop == template.AllowUserAutostop &&
782804
req.AllowUserCancelWorkspaceJobs == template.AllowUserCancelWorkspaceJobs &&
@@ -796,12 +818,6 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
796818
return nil
797819
}
798820

799-
// Users should not be able to clear the template name in the UI
800-
name := req.Name
801-
if name == "" {
802-
name = template.Name
803-
}
804-
805821
groupACL := template.GroupACL
806822
if req.DisableEveryoneGroupAccess {
807823
delete(groupACL, template.OrganizationID.String())
@@ -827,9 +843,9 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
827843
ID: template.ID,
828844
UpdatedAt: dbtime.Now(),
829845
Name: name,
830-
DisplayName: req.DisplayName,
831-
Description: req.Description,
832-
Icon: req.Icon,
846+
DisplayName: displayName,
847+
Description: description,
848+
Icon: icon,
833849
AllowUserCancelWorkspaceJobs: req.AllowUserCancelWorkspaceJobs,
834850
GroupACL: groupACL,
835851
MaxPortSharingLevel: maxPortShareLevel,

coderd/templates_test.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1377,7 +1377,7 @@ func TestPatchTemplateMeta(t *testing.T) {
13771377
assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis)
13781378
})
13791379

1380-
t.Run("RemoveIcon", func(t *testing.T) {
1380+
t.Run("PreserveIconWhenEmpty", func(t *testing.T) {
13811381
t.Parallel()
13821382

13831383
client := coderdtest.New(t, nil)
@@ -1386,15 +1386,18 @@ func TestPatchTemplateMeta(t *testing.T) {
13861386
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
13871387
ctr.Icon = "/icon/code.png"
13881388
})
1389+
// Test that when only updating other fields, the icon is preserved
13891390
req := codersdk.UpdateTemplateMeta{
1390-
Icon: "",
1391+
DefaultTTLMillis: 12 * time.Hour.Milliseconds(),
13911392
}
13921393

13931394
ctx := testutil.Context(t, testutil.WaitLong)
13941395

13951396
updated, err := client.UpdateTemplateMeta(ctx, template.ID, req)
13961397
require.NoError(t, err)
1397-
assert.Equal(t, updated.Icon, "")
1398+
// Icon should be preserved when not explicitly provided
1399+
assert.Equal(t, "/icon/code.png", updated.Icon)
1400+
assert.Equal(t, req.DefaultTTLMillis, updated.DefaultTTLMillis)
13981401
})
13991402

14001403
t.Run("AutostopRequirement", func(t *testing.T) {

0 commit comments

Comments
 (0)