feat: Add icon to templates (#3561)

This commit is contained in:
Bruno Quaresma 2022-08-19 10:17:35 -03:00 committed by GitHub
parent 57f3410009
commit 80f042f01b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 66 additions and 12 deletions

View File

@ -15,6 +15,7 @@ func templateEdit() *cobra.Command {
var (
name string
description string
icon string
maxTTL time.Duration
minAutostartInterval time.Duration
)
@ -41,6 +42,7 @@ func templateEdit() *cobra.Command {
req := codersdk.UpdateTemplateMeta{
Name: name,
Description: description,
Icon: icon,
MaxTTLMillis: maxTTL.Milliseconds(),
MinAutostartIntervalMillis: minAutostartInterval.Milliseconds(),
}
@ -56,6 +58,7 @@ func templateEdit() *cobra.Command {
cmd.Flags().StringVarP(&name, "name", "", "", "Edit the template name")
cmd.Flags().StringVarP(&description, "description", "", "", "Edit the template description")
cmd.Flags().StringVarP(&icon, "icon", "", "", "Edit the template icon path")
cmd.Flags().DurationVarP(&maxTTL, "max-ttl", "", 0, "Edit the template maximum time before shutdown")
cmd.Flags().DurationVarP(&minAutostartInterval, "min-autostart-interval", "", 0, "Edit the template minimum autostart interval")
cliui.AllowSkipPrompt(cmd)

View File

@ -25,6 +25,7 @@ func TestTemplateEdit(t *testing.T) {
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
ctr.Description = "original description"
ctr.Icon = "/icons/default-icon.png"
ctr.MaxTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds())
ctr.MinAutostartIntervalMillis = ptr.Ref(time.Hour.Milliseconds())
})
@ -32,6 +33,7 @@ func TestTemplateEdit(t *testing.T) {
// Test the cli command.
name := "new-template-name"
desc := "lorem ipsum dolor sit amet et cetera"
icon := "/icons/new-icon.png"
maxTTL := 12 * time.Hour
minAutostartInterval := time.Minute
cmdArgs := []string{
@ -40,6 +42,7 @@ func TestTemplateEdit(t *testing.T) {
template.Name,
"--name", name,
"--description", desc,
"--icon", icon,
"--max-ttl", maxTTL.String(),
"--min-autostart-interval", minAutostartInterval.String(),
}
@ -55,6 +58,7 @@ func TestTemplateEdit(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, name, updated.Name)
assert.Equal(t, desc, updated.Description)
assert.Equal(t, icon, updated.Icon)
assert.Equal(t, maxTTL.Milliseconds(), updated.MaxTTLMillis)
assert.Equal(t, minAutostartInterval.Milliseconds(), updated.MinAutostartIntervalMillis)
})
@ -67,6 +71,7 @@ func TestTemplateEdit(t *testing.T) {
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
ctr.Description = "original description"
ctr.Icon = "/icons/default-icon.png"
ctr.MaxTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds())
ctr.MinAutostartIntervalMillis = ptr.Ref(time.Hour.Milliseconds())
})
@ -78,6 +83,7 @@ func TestTemplateEdit(t *testing.T) {
template.Name,
"--name", template.Name,
"--description", template.Description,
"--icon", template.Icon,
"--max-ttl", (time.Duration(template.MaxTTLMillis) * time.Millisecond).String(),
"--min-autostart-interval", (time.Duration(template.MinAutostartIntervalMillis) * time.Millisecond).String(),
}
@ -93,6 +99,7 @@ func TestTemplateEdit(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, template.Name, updated.Name)
assert.Equal(t, template.Description, updated.Description)
assert.Equal(t, template.Icon, updated.Icon)
assert.Equal(t, template.MaxTTLMillis, updated.MaxTTLMillis)
assert.Equal(t, template.MinAutostartIntervalMillis, updated.MinAutostartIntervalMillis)
})

View File

@ -70,6 +70,7 @@ var AuditableResources = auditMap(map[any]map[string]Action{
"provisioner": ActionTrack,
"active_version_id": ActionTrack,
"description": ActionTrack,
"icon": ActionTrack,
"max_ttl": ActionTrack,
"min_autostart_interval": ActionTrack,
"created_by": ActionTrack,

View File

@ -883,6 +883,7 @@ func (q *fakeQuerier) UpdateTemplateMetaByID(_ context.Context, arg database.Upd
tpl.UpdatedAt = database.Now()
tpl.Name = arg.Name
tpl.Description = arg.Description
tpl.Icon = arg.Icon
tpl.MaxTtl = arg.MaxTtl
tpl.MinAutostartInterval = arg.MinAutostartInterval
q.templates[idx] = tpl

View File

@ -260,7 +260,8 @@ CREATE TABLE templates (
description character varying(128) DEFAULT ''::character varying NOT NULL,
max_ttl bigint DEFAULT '604800000000000'::bigint NOT NULL,
min_autostart_interval bigint DEFAULT '3600000000000'::bigint NOT NULL,
created_by uuid NOT NULL
created_by uuid NOT NULL,
icon character varying(256) DEFAULT ''::character varying NOT NULL
);
CREATE TABLE user_links (

View File

@ -0,0 +1 @@
ALTER TABLE templates DROP COLUMN icon;

View File

@ -0,0 +1 @@
ALTER TABLE templates ADD COLUMN icon VARCHAR(256) NOT NULL DEFAULT '';

View File

@ -464,6 +464,7 @@ type Template struct {
MaxTtl int64 `db:"max_ttl" json:"max_ttl"`
MinAutostartInterval int64 `db:"min_autostart_interval" json:"min_autostart_interval"`
CreatedBy uuid.UUID `db:"created_by" json:"created_by"`
Icon string `db:"icon" json:"icon"`
}
type TemplateVersion struct {

View File

@ -1786,7 +1786,7 @@ func (q *sqlQuerier) InsertDeploymentID(ctx context.Context, value string) error
const getTemplateByID = `-- name: GetTemplateByID :one
SELECT
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, min_autostart_interval, created_by
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, min_autostart_interval, created_by, icon
FROM
templates
WHERE
@ -1811,13 +1811,14 @@ func (q *sqlQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (Templat
&i.MaxTtl,
&i.MinAutostartInterval,
&i.CreatedBy,
&i.Icon,
)
return i, err
}
const getTemplateByOrganizationAndName = `-- name: GetTemplateByOrganizationAndName :one
SELECT
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, min_autostart_interval, created_by
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, min_autostart_interval, created_by, icon
FROM
templates
WHERE
@ -1850,12 +1851,13 @@ func (q *sqlQuerier) GetTemplateByOrganizationAndName(ctx context.Context, arg G
&i.MaxTtl,
&i.MinAutostartInterval,
&i.CreatedBy,
&i.Icon,
)
return i, err
}
const getTemplates = `-- name: GetTemplates :many
SELECT id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, min_autostart_interval, created_by FROM templates
SELECT id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, min_autostart_interval, created_by, icon FROM templates
ORDER BY (name, id) ASC
`
@ -1881,6 +1883,7 @@ func (q *sqlQuerier) GetTemplates(ctx context.Context) ([]Template, error) {
&i.MaxTtl,
&i.MinAutostartInterval,
&i.CreatedBy,
&i.Icon,
); err != nil {
return nil, err
}
@ -1897,7 +1900,7 @@ func (q *sqlQuerier) GetTemplates(ctx context.Context) ([]Template, error) {
const getTemplatesWithFilter = `-- name: GetTemplatesWithFilter :many
SELECT
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, min_autostart_interval, created_by
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, min_autostart_interval, created_by, icon
FROM
templates
WHERE
@ -1958,6 +1961,7 @@ func (q *sqlQuerier) GetTemplatesWithFilter(ctx context.Context, arg GetTemplate
&i.MaxTtl,
&i.MinAutostartInterval,
&i.CreatedBy,
&i.Icon,
); err != nil {
return nil, err
}
@ -1985,10 +1989,11 @@ INSERT INTO
description,
max_ttl,
min_autostart_interval,
created_by
created_by,
icon
)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, min_autostart_interval, created_by
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12) RETURNING id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, min_autostart_interval, created_by, icon
`
type InsertTemplateParams struct {
@ -2003,6 +2008,7 @@ type InsertTemplateParams struct {
MaxTtl int64 `db:"max_ttl" json:"max_ttl"`
MinAutostartInterval int64 `db:"min_autostart_interval" json:"min_autostart_interval"`
CreatedBy uuid.UUID `db:"created_by" json:"created_by"`
Icon string `db:"icon" json:"icon"`
}
func (q *sqlQuerier) InsertTemplate(ctx context.Context, arg InsertTemplateParams) (Template, error) {
@ -2018,6 +2024,7 @@ func (q *sqlQuerier) InsertTemplate(ctx context.Context, arg InsertTemplateParam
arg.MaxTtl,
arg.MinAutostartInterval,
arg.CreatedBy,
arg.Icon,
)
var i Template
err := row.Scan(
@ -2033,6 +2040,7 @@ func (q *sqlQuerier) InsertTemplate(ctx context.Context, arg InsertTemplateParam
&i.MaxTtl,
&i.MinAutostartInterval,
&i.CreatedBy,
&i.Icon,
)
return i, err
}
@ -2087,11 +2095,12 @@ SET
description = $3,
max_ttl = $4,
min_autostart_interval = $5,
name = $6
name = $6,
icon = $7
WHERE
id = $1
RETURNING
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, min_autostart_interval, created_by
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, min_autostart_interval, created_by, icon
`
type UpdateTemplateMetaByIDParams struct {
@ -2101,6 +2110,7 @@ type UpdateTemplateMetaByIDParams struct {
MaxTtl int64 `db:"max_ttl" json:"max_ttl"`
MinAutostartInterval int64 `db:"min_autostart_interval" json:"min_autostart_interval"`
Name string `db:"name" json:"name"`
Icon string `db:"icon" json:"icon"`
}
func (q *sqlQuerier) UpdateTemplateMetaByID(ctx context.Context, arg UpdateTemplateMetaByIDParams) error {
@ -2111,6 +2121,7 @@ func (q *sqlQuerier) UpdateTemplateMetaByID(ctx context.Context, arg UpdateTempl
arg.MaxTtl,
arg.MinAutostartInterval,
arg.Name,
arg.Icon,
)
return err
}

View File

@ -67,10 +67,11 @@ INSERT INTO
description,
max_ttl,
min_autostart_interval,
created_by
created_by,
icon
)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING *;
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12) RETURNING *;
-- name: UpdateTemplateActiveVersionByID :exec
UPDATE
@ -98,7 +99,8 @@ SET
description = $3,
max_ttl = $4,
min_autostart_interval = $5,
name = $6
name = $6,
icon = $7
WHERE
id = $1
RETURNING

View File

@ -411,6 +411,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
if req.Name == template.Name &&
req.Description == template.Description &&
req.Icon == template.Icon &&
req.MaxTTLMillis == time.Duration(template.MaxTtl).Milliseconds() &&
req.MinAutostartIntervalMillis == time.Duration(template.MinAutostartInterval).Milliseconds() {
return nil
@ -419,6 +420,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
// Update template metadata -- empty fields are not overwritten.
name := req.Name
desc := req.Description
icon := req.Icon
maxTTL := time.Duration(req.MaxTTLMillis) * time.Millisecond
minAutostartInterval := time.Duration(req.MinAutostartIntervalMillis) * time.Millisecond
@ -428,6 +430,9 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
if desc == "" {
desc = template.Description
}
if icon == "" {
name = template.Icon
}
if maxTTL == 0 {
maxTTL = time.Duration(template.MaxTtl)
}
@ -440,6 +445,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
UpdatedAt: database.Now(),
Name: name,
Description: desc,
Icon: icon,
MaxTtl: int64(maxTTL),
MinAutostartInterval: int64(minAutostartInterval),
}); err != nil {
@ -519,6 +525,7 @@ func convertTemplate(template database.Template, workspaceOwnerCount uint32, cre
ActiveVersionID: template.ActiveVersionID,
WorkspaceOwnerCount: workspaceOwnerCount,
Description: template.Description,
Icon: template.Icon,
MaxTTLMillis: time.Duration(template.MaxTtl).Milliseconds(),
MinAutostartIntervalMillis: time.Duration(template.MinAutostartInterval).Milliseconds(),
CreatedByID: template.CreatedBy,

View File

@ -233,12 +233,14 @@ func TestPatchTemplateMeta(t *testing.T) {
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
ctr.Description = "original description"
ctr.Icon = "/icons/original-icon.png"
ctr.MaxTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds())
ctr.MinAutostartIntervalMillis = ptr.Ref(time.Hour.Milliseconds())
})
req := codersdk.UpdateTemplateMeta{
Name: "new-template-name",
Description: "lorem ipsum dolor sit amet et cetera",
Icon: "/icons/new-icon.png",
MaxTTLMillis: 12 * time.Hour.Milliseconds(),
MinAutostartIntervalMillis: time.Minute.Milliseconds(),
}
@ -254,6 +256,7 @@ func TestPatchTemplateMeta(t *testing.T) {
assert.Greater(t, updated.UpdatedAt, template.UpdatedAt)
assert.Equal(t, req.Name, updated.Name)
assert.Equal(t, req.Description, updated.Description)
assert.Equal(t, req.Icon, updated.Icon)
assert.Equal(t, req.MaxTTLMillis, updated.MaxTTLMillis)
assert.Equal(t, req.MinAutostartIntervalMillis, updated.MinAutostartIntervalMillis)
@ -263,6 +266,7 @@ func TestPatchTemplateMeta(t *testing.T) {
assert.Greater(t, updated.UpdatedAt, template.UpdatedAt)
assert.Equal(t, req.Name, updated.Name)
assert.Equal(t, req.Description, updated.Description)
assert.Equal(t, req.Icon, updated.Icon)
assert.Equal(t, req.MaxTTLMillis, updated.MaxTTLMillis)
assert.Equal(t, req.MinAutostartIntervalMillis, updated.MinAutostartIntervalMillis)
})
@ -275,6 +279,7 @@ func TestPatchTemplateMeta(t *testing.T) {
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
ctr.Description = "original description"
ctr.Icon = "/icons/original-icon.png"
ctr.MaxTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds())
ctr.MinAutostartIntervalMillis = ptr.Ref(time.Hour.Milliseconds())
})
@ -285,6 +290,7 @@ func TestPatchTemplateMeta(t *testing.T) {
req := codersdk.UpdateTemplateMeta{
Name: template.Name,
Description: template.Description,
Icon: template.Icon,
MaxTTLMillis: template.MaxTTLMillis,
MinAutostartIntervalMillis: template.MinAutostartIntervalMillis,
}
@ -295,6 +301,7 @@ func TestPatchTemplateMeta(t *testing.T) {
assert.Equal(t, updated.UpdatedAt, template.UpdatedAt)
assert.Equal(t, template.Name, updated.Name)
assert.Equal(t, template.Description, updated.Description)
assert.Equal(t, template.Icon, updated.Icon)
assert.Equal(t, template.MaxTTLMillis, updated.MaxTTLMillis)
assert.Equal(t, template.MinAutostartIntervalMillis, updated.MinAutostartIntervalMillis)
})
@ -331,6 +338,7 @@ func TestPatchTemplateMeta(t *testing.T) {
assert.WithinDuration(t, template.UpdatedAt, updated.UpdatedAt, time.Minute)
assert.Equal(t, template.Name, updated.Name)
assert.Equal(t, template.Description, updated.Description)
assert.Equal(t, template.Icon, updated.Icon)
assert.Equal(t, template.MaxTTLMillis, updated.MaxTTLMillis)
assert.Equal(t, template.MinAutostartIntervalMillis, updated.MinAutostartIntervalMillis)
})

View File

@ -52,6 +52,9 @@ type CreateTemplateRequest struct {
// Description is a description of what the template contains. It must be
// less than 128 bytes.
Description string `json:"description,omitempty" validate:"lt=128"`
// Icon is a relative path or external URL that specifies
// an icon to be displayed in the dashboard.
Icon string `json:"icon,omitempty"`
// VersionID is an in-progress or completed job to use as an initial version
// of the template.

View File

@ -23,6 +23,7 @@ type Template struct {
ActiveVersionID uuid.UUID `json:"active_version_id"`
WorkspaceOwnerCount uint32 `json:"workspace_owner_count"`
Description string `json:"description"`
Icon string `json:"icon"`
MaxTTLMillis int64 `json:"max_ttl_ms"`
MinAutostartIntervalMillis int64 `json:"min_autostart_interval_ms"`
CreatedByID uuid.UUID `json:"created_by_id"`
@ -36,6 +37,7 @@ type UpdateActiveTemplateVersion struct {
type UpdateTemplateMeta struct {
Name string `json:"name,omitempty" validate:"omitempty,username"`
Description string `json:"description,omitempty"`
Icon string `json:"icon,omitempty"`
MaxTTLMillis int64 `json:"max_ttl_ms,omitempty"`
MinAutostartIntervalMillis int64 `json:"min_autostart_interval_ms,omitempty"`
}

View File

@ -87,6 +87,7 @@ export interface CreateParameterRequest {
export interface CreateTemplateRequest {
readonly name: string
readonly description?: string
readonly icon?: string
readonly template_version_id: string
readonly parameter_values?: CreateParameterRequest[]
readonly max_ttl_ms?: number
@ -295,6 +296,7 @@ export interface Template {
readonly active_version_id: string
readonly workspace_owner_count: number
readonly description: string
readonly icon: string
readonly max_ttl_ms: number
readonly min_autostart_interval_ms: number
readonly created_by_id: string
@ -334,6 +336,7 @@ export interface UpdateRoles {
export interface UpdateTemplateMeta {
readonly name?: string
readonly description?: string
readonly icon?: string
readonly max_ttl_ms?: number
readonly min_autostart_interval_ms?: number
}

View File

@ -54,6 +54,7 @@ describe("TemplateSettingsPage", () => {
name: "edited-template-name",
description: "Edited description",
max_ttl_ms: 4000,
icon: "/icons/new-icon.png",
}
jest.spyOn(API, "updateTemplateMeta").mockResolvedValueOnce({
...MockTemplate,

View File

@ -155,6 +155,7 @@ export const MockTemplate: TypesGen.Template = {
min_autostart_interval_ms: 60 * 60 * 1000,
created_by_id: "test-creator-id",
created_by_name: "test_creator",
icon: "/icon/code.svg",
}
export const MockWorkspaceAutostartDisabled: TypesGen.UpdateWorkspaceAutostartRequest = {