fix(coderd): list templates returns non-deprecated templates by default (#17747)

## Description

Modifies the behaviour of the "list templates" API endpoints to return
non-deprecated templates by default. Users can still query for
deprecated templates by specifying the `deprecated=true` query
parameter.

**Note:** The deprecation feature is an enterprise-level feature

## Affected Endpoints
* /api/v2/organizations/{organization}/templates
* /api/v2/templates

Fixes #17565
This commit is contained in:
Susana Ferreira
2025-05-13 12:44:46 +01:00
committed by GitHub
parent 7f056da088
commit 599bb35a04
6 changed files with 329 additions and 1 deletions

View File

@ -441,6 +441,250 @@ func TestPostTemplateByOrganization(t *testing.T) {
})
}
func TestTemplates(t *testing.T) {
t.Parallel()
t.Run("ListEmpty", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
_ = coderdtest.CreateFirstUser(t, client)
ctx := testutil.Context(t, testutil.WaitLong)
templates, err := client.Templates(ctx, codersdk.TemplateFilter{})
require.NoError(t, err)
require.NotNil(t, templates)
require.Len(t, templates, 0)
})
// Should return only non-deprecated templates by default
t.Run("ListMultiple non-deprecated", func(t *testing.T) {
t.Parallel()
owner, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{IncludeProvisionerDaemon: false})
user := coderdtest.CreateFirstUser(t, owner)
client, tplAdmin := coderdtest.CreateAnotherUser(t, owner, user.OrganizationID, rbac.RoleTemplateAdmin())
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
version2 := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
foo := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(request *codersdk.CreateTemplateRequest) {
request.Name = "foo"
})
bar := coderdtest.CreateTemplate(t, client, user.OrganizationID, version2.ID, func(request *codersdk.CreateTemplateRequest) {
request.Name = "bar"
})
ctx := testutil.Context(t, testutil.WaitLong)
// Deprecate bar template
deprecationMessage := "Some deprecated message"
err := db.UpdateTemplateAccessControlByID(dbauthz.As(ctx, coderdtest.AuthzUserSubject(tplAdmin, user.OrganizationID)), database.UpdateTemplateAccessControlByIDParams{
ID: bar.ID,
RequireActiveVersion: false,
Deprecated: deprecationMessage,
})
require.NoError(t, err)
updatedBar, err := client.Template(ctx, bar.ID)
require.NoError(t, err)
require.True(t, updatedBar.Deprecated)
require.Equal(t, deprecationMessage, updatedBar.DeprecationMessage)
// Should return only the non-deprecated template (foo)
templates, err := client.Templates(ctx, codersdk.TemplateFilter{})
require.NoError(t, err)
require.Len(t, templates, 1)
require.Equal(t, foo.ID, templates[0].ID)
require.False(t, templates[0].Deprecated)
require.Empty(t, templates[0].DeprecationMessage)
})
// Should return only deprecated templates when filtering by deprecated:true
t.Run("ListMultiple deprecated:true", func(t *testing.T) {
t.Parallel()
owner, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{IncludeProvisionerDaemon: false})
user := coderdtest.CreateFirstUser(t, owner)
client, tplAdmin := coderdtest.CreateAnotherUser(t, owner, user.OrganizationID, rbac.RoleTemplateAdmin())
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
version2 := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
foo := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(request *codersdk.CreateTemplateRequest) {
request.Name = "foo"
})
bar := coderdtest.CreateTemplate(t, client, user.OrganizationID, version2.ID, func(request *codersdk.CreateTemplateRequest) {
request.Name = "bar"
})
ctx := testutil.Context(t, testutil.WaitLong)
// Deprecate foo and bar templates
deprecationMessage := "Some deprecated message"
err := db.UpdateTemplateAccessControlByID(dbauthz.As(ctx, coderdtest.AuthzUserSubject(tplAdmin, user.OrganizationID)), database.UpdateTemplateAccessControlByIDParams{
ID: foo.ID,
RequireActiveVersion: false,
Deprecated: deprecationMessage,
})
require.NoError(t, err)
err = db.UpdateTemplateAccessControlByID(dbauthz.As(ctx, coderdtest.AuthzUserSubject(tplAdmin, user.OrganizationID)), database.UpdateTemplateAccessControlByIDParams{
ID: bar.ID,
RequireActiveVersion: false,
Deprecated: deprecationMessage,
})
require.NoError(t, err)
// Should have deprecation message set
updatedFoo, err := client.Template(ctx, foo.ID)
require.NoError(t, err)
require.True(t, updatedFoo.Deprecated)
require.Equal(t, deprecationMessage, updatedFoo.DeprecationMessage)
updatedBar, err := client.Template(ctx, bar.ID)
require.NoError(t, err)
require.True(t, updatedBar.Deprecated)
require.Equal(t, deprecationMessage, updatedBar.DeprecationMessage)
// Should return only the deprecated templates (foo and bar)
templates, err := client.Templates(ctx, codersdk.TemplateFilter{
SearchQuery: "deprecated:true",
})
require.NoError(t, err)
require.Len(t, templates, 2)
// Make sure all the deprecated templates are returned
expectedTemplates := map[uuid.UUID]codersdk.Template{
updatedFoo.ID: updatedFoo,
updatedBar.ID: updatedBar,
}
actualTemplates := map[uuid.UUID]codersdk.Template{}
for _, template := range templates {
actualTemplates[template.ID] = template
}
require.Equal(t, len(expectedTemplates), len(actualTemplates))
for id, expectedTemplate := range expectedTemplates {
actualTemplate, ok := actualTemplates[id]
require.True(t, ok)
require.Equal(t, expectedTemplate.ID, actualTemplate.ID)
require.Equal(t, true, actualTemplate.Deprecated)
require.Equal(t, expectedTemplate.DeprecationMessage, actualTemplate.DeprecationMessage)
}
})
// Should return only non-deprecated templates when filtering by deprecated:false
t.Run("ListMultiple deprecated:false", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
user := coderdtest.CreateFirstUser(t, client)
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
version2 := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
foo := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(request *codersdk.CreateTemplateRequest) {
request.Name = "foo"
})
bar := coderdtest.CreateTemplate(t, client, user.OrganizationID, version2.ID, func(request *codersdk.CreateTemplateRequest) {
request.Name = "bar"
})
ctx := testutil.Context(t, testutil.WaitLong)
// Should return only the non-deprecated templates
templates, err := client.Templates(ctx, codersdk.TemplateFilter{
SearchQuery: "deprecated:false",
})
require.NoError(t, err)
require.Len(t, templates, 2)
// Make sure all the non-deprecated templates are returned
expectedTemplates := map[uuid.UUID]codersdk.Template{
foo.ID: foo,
bar.ID: bar,
}
actualTemplates := map[uuid.UUID]codersdk.Template{}
for _, template := range templates {
actualTemplates[template.ID] = template
}
require.Equal(t, len(expectedTemplates), len(actualTemplates))
for id, expectedTemplate := range expectedTemplates {
actualTemplate, ok := actualTemplates[id]
require.True(t, ok)
require.Equal(t, expectedTemplate.ID, actualTemplate.ID)
require.Equal(t, false, actualTemplate.Deprecated)
require.Equal(t, expectedTemplate.DeprecationMessage, actualTemplate.DeprecationMessage)
}
})
// Should return a re-enabled template in the default (non-deprecated) list
t.Run("ListMultiple re-enabled template", func(t *testing.T) {
t.Parallel()
owner, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{IncludeProvisionerDaemon: false})
user := coderdtest.CreateFirstUser(t, owner)
client, tplAdmin := coderdtest.CreateAnotherUser(t, owner, user.OrganizationID, rbac.RoleTemplateAdmin())
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
version2 := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
foo := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(request *codersdk.CreateTemplateRequest) {
request.Name = "foo"
})
bar := coderdtest.CreateTemplate(t, client, user.OrganizationID, version2.ID, func(request *codersdk.CreateTemplateRequest) {
request.Name = "bar"
})
ctx := testutil.Context(t, testutil.WaitLong)
// Deprecate bar template
deprecationMessage := "Some deprecated message"
err := db.UpdateTemplateAccessControlByID(dbauthz.As(ctx, coderdtest.AuthzUserSubject(tplAdmin, user.OrganizationID)), database.UpdateTemplateAccessControlByIDParams{
ID: bar.ID,
RequireActiveVersion: false,
Deprecated: deprecationMessage,
})
require.NoError(t, err)
updatedBar, err := client.Template(ctx, bar.ID)
require.NoError(t, err)
require.True(t, updatedBar.Deprecated)
require.Equal(t, deprecationMessage, updatedBar.DeprecationMessage)
// Re-enable bar template
err = db.UpdateTemplateAccessControlByID(dbauthz.As(ctx, coderdtest.AuthzUserSubject(tplAdmin, user.OrganizationID)), database.UpdateTemplateAccessControlByIDParams{
ID: bar.ID,
RequireActiveVersion: false,
Deprecated: "",
})
require.NoError(t, err)
reEnabledBar, err := client.Template(ctx, bar.ID)
require.NoError(t, err)
require.False(t, reEnabledBar.Deprecated)
require.Empty(t, reEnabledBar.DeprecationMessage)
// Should return only the non-deprecated templates (foo and bar)
templates, err := client.Templates(ctx, codersdk.TemplateFilter{})
require.NoError(t, err)
require.Len(t, templates, 2)
// Make sure all the non-deprecated templates are returned
expectedTemplates := map[uuid.UUID]codersdk.Template{
foo.ID: foo,
bar.ID: bar,
}
actualTemplates := map[uuid.UUID]codersdk.Template{}
for _, template := range templates {
actualTemplates[template.ID] = template
}
require.Equal(t, len(expectedTemplates), len(actualTemplates))
for id, expectedTemplate := range expectedTemplates {
actualTemplate, ok := actualTemplates[id]
require.True(t, ok)
require.Equal(t, expectedTemplate.ID, actualTemplate.ID)
require.Equal(t, false, actualTemplate.Deprecated)
require.Equal(t, expectedTemplate.DeprecationMessage, actualTemplate.DeprecationMessage)
}
})
}
func TestTemplatesByOrganization(t *testing.T) {
t.Parallel()
t.Run("ListEmpty", func(t *testing.T) {
@ -525,6 +769,48 @@ func TestTemplatesByOrganization(t *testing.T) {
require.Len(t, templates, 1)
require.Equal(t, bar.ID, templates[0].ID)
})
// Should return only non-deprecated templates by default
t.Run("ListMultiple non-deprecated", func(t *testing.T) {
t.Parallel()
owner, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{IncludeProvisionerDaemon: false})
user := coderdtest.CreateFirstUser(t, owner)
client, tplAdmin := coderdtest.CreateAnotherUser(t, owner, user.OrganizationID, rbac.RoleTemplateAdmin())
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
version2 := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
foo := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(request *codersdk.CreateTemplateRequest) {
request.Name = "foo"
})
bar := coderdtest.CreateTemplate(t, client, user.OrganizationID, version2.ID, func(request *codersdk.CreateTemplateRequest) {
request.Name = "bar"
})
ctx := testutil.Context(t, testutil.WaitLong)
// Deprecate bar template
deprecationMessage := "Some deprecated message"
err := db.UpdateTemplateAccessControlByID(dbauthz.As(ctx, coderdtest.AuthzUserSubject(tplAdmin, user.OrganizationID)), database.UpdateTemplateAccessControlByIDParams{
ID: bar.ID,
RequireActiveVersion: false,
Deprecated: deprecationMessage,
})
require.NoError(t, err)
updatedBar, err := client.Template(ctx, bar.ID)
require.NoError(t, err)
require.True(t, updatedBar.Deprecated)
require.Equal(t, deprecationMessage, updatedBar.DeprecationMessage)
// Should return only the non-deprecated template (foo)
templates, err := client.TemplatesByOrganization(ctx, user.OrganizationID)
require.NoError(t, err)
require.Len(t, templates, 1)
require.Equal(t, foo.ID, templates[0].ID)
require.False(t, templates[0].Deprecated)
require.Empty(t, templates[0].DeprecationMessage)
})
}
func TestTemplateByOrganizationAndName(t *testing.T) {