mirror of https://github.com/coder/coder.git
feat: cli: allow editing template metadata (#2159)
This PR adds a CLI command template edit which allows updating the following metadata fields of a template: - Description - Max TTL - Min Autostart Interval
This commit is contained in:
parent
b65259f95e
commit
8cfe223192
|
@ -0,0 +1,61 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/cli/cliui"
|
||||
"github.com/coder/coder/codersdk"
|
||||
)
|
||||
|
||||
func templateEdit() *cobra.Command {
|
||||
var (
|
||||
description string
|
||||
maxTTL time.Duration
|
||||
minAutostartInterval time.Duration
|
||||
)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "edit <template> [flags]",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Short: "Edit the metadata of a template by name.",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
client, err := createClient(cmd)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("create client: %w", err)
|
||||
}
|
||||
organization, err := currentOrganization(cmd, client)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("get current organization: %w", err)
|
||||
}
|
||||
template, err := client.TemplateByName(cmd.Context(), organization.ID, args[0])
|
||||
if err != nil {
|
||||
return xerrors.Errorf("get workspace template: %w", err)
|
||||
}
|
||||
|
||||
// NOTE: coderd will ignore empty fields.
|
||||
req := codersdk.UpdateTemplateMeta{
|
||||
Description: description,
|
||||
MaxTTLMillis: maxTTL.Milliseconds(),
|
||||
MinAutostartIntervalMillis: minAutostartInterval.Milliseconds(),
|
||||
}
|
||||
|
||||
_, err = client.UpdateTemplateMeta(cmd.Context(), template.ID, req)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("update template metadata: %w", err)
|
||||
}
|
||||
_, _ = fmt.Printf("Updated template metadata!\n")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVarP(&description, "description", "", "", "Edit the template description")
|
||||
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)
|
||||
|
||||
return cmd
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
package cli_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/cli/clitest"
|
||||
"github.com/coder/coder/coderd/coderdtest"
|
||||
"github.com/coder/coder/coderd/util/ptr"
|
||||
"github.com/coder/coder/codersdk"
|
||||
)
|
||||
|
||||
func TestTemplateEdit(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("Modified", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerD: true})
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||
ctr.Description = "original description"
|
||||
ctr.MaxTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds())
|
||||
ctr.MinAutostartIntervalMillis = ptr.Ref(time.Hour.Milliseconds())
|
||||
})
|
||||
|
||||
// Test the cli command.
|
||||
desc := "lorem ipsum dolor sit amet et cetera"
|
||||
maxTTL := 12 * time.Hour
|
||||
minAutostartInterval := time.Minute
|
||||
cmdArgs := []string{
|
||||
"templates",
|
||||
"edit",
|
||||
template.Name,
|
||||
"--description", desc,
|
||||
"--max_ttl", maxTTL.String(),
|
||||
"--min_autostart_interval", minAutostartInterval.String(),
|
||||
}
|
||||
cmd, root := clitest.New(t, cmdArgs...)
|
||||
clitest.SetupConfig(t, client, root)
|
||||
|
||||
err := cmd.Execute()
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
// Assert that the template metadata changed.
|
||||
updated, err := client.Template(context.Background(), template.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, desc, updated.Description)
|
||||
assert.Equal(t, maxTTL.Milliseconds(), updated.MaxTTLMillis)
|
||||
assert.Equal(t, minAutostartInterval.Milliseconds(), updated.MinAutostartIntervalMillis)
|
||||
})
|
||||
|
||||
t.Run("NotModified", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerD: true})
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||
ctr.Description = "original description"
|
||||
ctr.MaxTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds())
|
||||
ctr.MinAutostartIntervalMillis = ptr.Ref(time.Hour.Milliseconds())
|
||||
})
|
||||
|
||||
// Test the cli command.
|
||||
cmdArgs := []string{
|
||||
"templates",
|
||||
"edit",
|
||||
template.Name,
|
||||
"--description", template.Description,
|
||||
"--max_ttl", (time.Duration(template.MaxTTLMillis) * time.Millisecond).String(),
|
||||
"--min_autostart_interval", (time.Duration(template.MinAutostartIntervalMillis) * time.Millisecond).String(),
|
||||
}
|
||||
cmd, root := clitest.New(t, cmdArgs...)
|
||||
clitest.SetupConfig(t, client, root)
|
||||
|
||||
err := cmd.Execute()
|
||||
|
||||
require.ErrorContains(t, err, "not modified")
|
||||
|
||||
// Assert that the template metadata did not change.
|
||||
updated, err := client.Template(context.Background(), template.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, template.Description, updated.Description)
|
||||
assert.Equal(t, template.MaxTTLMillis, updated.MaxTTLMillis)
|
||||
assert.Equal(t, template.MinAutostartIntervalMillis, updated.MinAutostartIntervalMillis)
|
||||
})
|
||||
}
|
|
@ -26,6 +26,7 @@ func templates() *cobra.Command {
|
|||
}
|
||||
cmd.AddCommand(
|
||||
templateCreate(),
|
||||
templateEdit(),
|
||||
templateInit(),
|
||||
templateList(),
|
||||
templatePlan(),
|
||||
|
|
|
@ -199,6 +199,7 @@ func New(options *Options) *API {
|
|||
|
||||
r.Get("/", api.template)
|
||||
r.Delete("/", api.deleteTemplate)
|
||||
r.Patch("/", api.patchTemplateMeta)
|
||||
r.Route("/versions", func(r chi.Router) {
|
||||
r.Get("/", api.templateVersionsByTemplate)
|
||||
r.Patch("/", api.patchActiveTemplateVersion)
|
||||
|
|
|
@ -742,6 +742,25 @@ func (q *fakeQuerier) GetTemplateByOrganizationAndName(_ context.Context, arg da
|
|||
return database.Template{}, sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *fakeQuerier) UpdateTemplateMetaByID(_ context.Context, arg database.UpdateTemplateMetaByIDParams) error {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
for idx, tpl := range q.templates {
|
||||
if tpl.ID != arg.ID {
|
||||
continue
|
||||
}
|
||||
tpl.UpdatedAt = database.Now()
|
||||
tpl.Description = arg.Description
|
||||
tpl.MaxTtl = arg.MaxTtl
|
||||
tpl.MinAutostartInterval = arg.MinAutostartInterval
|
||||
q.templates[idx] = tpl
|
||||
return nil
|
||||
}
|
||||
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *fakeQuerier) GetTemplateVersionsByTemplateID(_ context.Context, arg database.GetTemplateVersionsByTemplateIDParams) (version []database.TemplateVersion, err error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
|
|
@ -108,6 +108,7 @@ type querier interface {
|
|||
UpdateProvisionerJobWithCompleteByID(ctx context.Context, arg UpdateProvisionerJobWithCompleteByIDParams) error
|
||||
UpdateTemplateActiveVersionByID(ctx context.Context, arg UpdateTemplateActiveVersionByIDParams) error
|
||||
UpdateTemplateDeletedByID(ctx context.Context, arg UpdateTemplateDeletedByIDParams) error
|
||||
UpdateTemplateMetaByID(ctx context.Context, arg UpdateTemplateMetaByIDParams) error
|
||||
UpdateTemplateVersionByID(ctx context.Context, arg UpdateTemplateVersionByIDParams) error
|
||||
UpdateTemplateVersionDescriptionByJobID(ctx context.Context, arg UpdateTemplateVersionDescriptionByJobIDParams) error
|
||||
UpdateUserHashedPassword(ctx context.Context, arg UpdateUserHashedPasswordParams) error
|
||||
|
|
|
@ -1862,6 +1862,39 @@ func (q *sqlQuerier) UpdateTemplateDeletedByID(ctx context.Context, arg UpdateTe
|
|||
return err
|
||||
}
|
||||
|
||||
const updateTemplateMetaByID = `-- name: UpdateTemplateMetaByID :exec
|
||||
UPDATE
|
||||
templates
|
||||
SET
|
||||
updated_at = $2,
|
||||
description = $3,
|
||||
max_ttl = $4,
|
||||
min_autostart_interval = $5
|
||||
WHERE
|
||||
id = $1
|
||||
RETURNING
|
||||
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, max_ttl, min_autostart_interval
|
||||
`
|
||||
|
||||
type UpdateTemplateMetaByIDParams struct {
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
||||
Description string `db:"description" json:"description"`
|
||||
MaxTtl int64 `db:"max_ttl" json:"max_ttl"`
|
||||
MinAutostartInterval int64 `db:"min_autostart_interval" json:"min_autostart_interval"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) UpdateTemplateMetaByID(ctx context.Context, arg UpdateTemplateMetaByIDParams) error {
|
||||
_, err := q.db.ExecContext(ctx, updateTemplateMetaByID,
|
||||
arg.ID,
|
||||
arg.UpdatedAt,
|
||||
arg.Description,
|
||||
arg.MaxTtl,
|
||||
arg.MinAutostartInterval,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const getTemplateVersionByID = `-- name: GetTemplateVersionByID :one
|
||||
SELECT
|
||||
id, template_id, organization_id, created_at, updated_at, name, readme, job_id
|
||||
|
|
|
@ -69,3 +69,16 @@ SET
|
|||
deleted = $2
|
||||
WHERE
|
||||
id = $1;
|
||||
|
||||
-- name: UpdateTemplateMetaByID :exec
|
||||
UPDATE
|
||||
templates
|
||||
SET
|
||||
updated_at = $2,
|
||||
description = $3,
|
||||
max_ttl = $4,
|
||||
min_autostart_interval = $5
|
||||
WHERE
|
||||
id = $1
|
||||
RETURNING
|
||||
*;
|
||||
|
|
|
@ -307,6 +307,102 @@ func (api *API) templateByOrganizationAndName(rw http.ResponseWriter, r *http.Re
|
|||
httpapi.Write(rw, http.StatusOK, convertTemplate(template, count))
|
||||
}
|
||||
|
||||
func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
|
||||
template := httpmw.TemplateParam(r)
|
||||
if !api.Authorize(rw, r, rbac.ActionUpdate, template) {
|
||||
return
|
||||
}
|
||||
|
||||
var req codersdk.UpdateTemplateMeta
|
||||
if !httpapi.Read(rw, r, &req) {
|
||||
return
|
||||
}
|
||||
|
||||
var validErrs []httpapi.Error
|
||||
if req.MaxTTLMillis < 0 {
|
||||
validErrs = append(validErrs, httpapi.Error{Field: "max_ttl_ms", Detail: "Must be a positive integer."})
|
||||
}
|
||||
if req.MinAutostartIntervalMillis < 0 {
|
||||
validErrs = append(validErrs, httpapi.Error{Field: "min_autostart_interval_ms", Detail: "Must be a positive integer."})
|
||||
}
|
||||
|
||||
if len(validErrs) > 0 {
|
||||
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
|
||||
Message: "Invalid request to update template metadata!",
|
||||
Validations: validErrs,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
count := uint32(0)
|
||||
var updated database.Template
|
||||
err := api.Database.InTx(func(s database.Store) error {
|
||||
// Fetch workspace counts
|
||||
workspaceCounts, err := s.GetWorkspaceOwnerCountsByTemplateIDs(r.Context(), []uuid.UUID{template.ID})
|
||||
if xerrors.Is(err, sql.ErrNoRows) {
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(workspaceCounts) > 0 {
|
||||
count = uint32(workspaceCounts[0].Count)
|
||||
}
|
||||
|
||||
if req.Description == template.Description &&
|
||||
req.MaxTTLMillis == time.Duration(template.MaxTtl).Milliseconds() &&
|
||||
req.MinAutostartIntervalMillis == time.Duration(template.MinAutostartInterval).Milliseconds() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update template metadata -- empty fields are not overwritten.
|
||||
desc := req.Description
|
||||
maxTTL := time.Duration(req.MaxTTLMillis) * time.Millisecond
|
||||
minAutostartInterval := time.Duration(req.MinAutostartIntervalMillis) * time.Millisecond
|
||||
|
||||
if desc == "" {
|
||||
desc = template.Description
|
||||
}
|
||||
if maxTTL == 0 {
|
||||
maxTTL = time.Duration(template.MaxTtl)
|
||||
}
|
||||
if minAutostartInterval == 0 {
|
||||
minAutostartInterval = time.Duration(template.MinAutostartInterval)
|
||||
}
|
||||
|
||||
if err := s.UpdateTemplateMetaByID(r.Context(), database.UpdateTemplateMetaByIDParams{
|
||||
ID: template.ID,
|
||||
UpdatedAt: database.Now(),
|
||||
Description: desc,
|
||||
MaxTtl: int64(maxTTL),
|
||||
MinAutostartInterval: int64(minAutostartInterval),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
updated, err = s.GetTemplateByID(r.Context(), template.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
|
||||
Message: "Internal error updating template metadata.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if updated.UpdatedAt.IsZero() {
|
||||
httpapi.Write(rw, http.StatusNotModified, nil)
|
||||
return
|
||||
}
|
||||
|
||||
httpapi.Write(rw, http.StatusOK, convertTemplate(updated, count))
|
||||
}
|
||||
|
||||
func convertTemplates(templates []database.Template, workspaceCounts []database.GetWorkspaceOwnerCountsByTemplateIDsRow) []codersdk.Template {
|
||||
apiTemplates := make([]codersdk.Template, 0, len(templates))
|
||||
for _, template := range templates {
|
||||
|
|
|
@ -4,12 +4,14 @@ import (
|
|||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/coderd/coderdtest"
|
||||
"github.com/coder/coder/coderd/util/ptr"
|
||||
"github.com/coder/coder/codersdk"
|
||||
)
|
||||
|
||||
|
@ -145,6 +147,99 @@ func TestTemplateByOrganizationAndName(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestPatchTemplateMeta(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("Modified", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
client := coderdtest.New(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
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.MaxTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds())
|
||||
ctr.MinAutostartIntervalMillis = ptr.Ref(time.Hour.Milliseconds())
|
||||
})
|
||||
req := codersdk.UpdateTemplateMeta{
|
||||
Description: "lorem ipsum dolor sit amet et cetera",
|
||||
MaxTTLMillis: 12 * time.Hour.Milliseconds(),
|
||||
MinAutostartIntervalMillis: time.Minute.Milliseconds(),
|
||||
}
|
||||
updated, err := client.UpdateTemplateMeta(ctx, template.ID, req)
|
||||
require.NoError(t, err)
|
||||
assert.Greater(t, updated.UpdatedAt, template.UpdatedAt)
|
||||
assert.Equal(t, req.Description, updated.Description)
|
||||
assert.Equal(t, req.MaxTTLMillis, updated.MaxTTLMillis)
|
||||
assert.Equal(t, req.MinAutostartIntervalMillis, updated.MinAutostartIntervalMillis)
|
||||
|
||||
// Extra paranoid: did it _really_ happen?
|
||||
updated, err = client.Template(ctx, template.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Greater(t, updated.UpdatedAt, template.UpdatedAt)
|
||||
assert.Equal(t, req.Description, updated.Description)
|
||||
assert.Equal(t, req.MaxTTLMillis, updated.MaxTTLMillis)
|
||||
assert.Equal(t, req.MinAutostartIntervalMillis, updated.MinAutostartIntervalMillis)
|
||||
})
|
||||
|
||||
t.Run("NotModified", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
client := coderdtest.New(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
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.MaxTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds())
|
||||
ctr.MinAutostartIntervalMillis = ptr.Ref(time.Hour.Milliseconds())
|
||||
})
|
||||
req := codersdk.UpdateTemplateMeta{
|
||||
Description: template.Description,
|
||||
MaxTTLMillis: template.MaxTTLMillis,
|
||||
MinAutostartIntervalMillis: template.MinAutostartIntervalMillis,
|
||||
}
|
||||
_, err := client.UpdateTemplateMeta(ctx, template.ID, req)
|
||||
require.ErrorContains(t, err, "not modified")
|
||||
updated, err := client.Template(ctx, template.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, updated.UpdatedAt, template.UpdatedAt)
|
||||
assert.Equal(t, template.Description, updated.Description)
|
||||
assert.Equal(t, template.MaxTTLMillis, updated.MaxTTLMillis)
|
||||
assert.Equal(t, template.MinAutostartIntervalMillis, updated.MinAutostartIntervalMillis)
|
||||
})
|
||||
|
||||
t.Run("Invalid", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
client := coderdtest.New(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
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.MaxTTLMillis = ptr.Ref(24 * time.Hour.Milliseconds())
|
||||
ctr.MinAutostartIntervalMillis = ptr.Ref(time.Hour.Milliseconds())
|
||||
})
|
||||
req := codersdk.UpdateTemplateMeta{
|
||||
MaxTTLMillis: -int64(time.Hour),
|
||||
MinAutostartIntervalMillis: -int64(time.Hour),
|
||||
}
|
||||
_, err := client.UpdateTemplateMeta(ctx, template.ID, req)
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Contains(t, apiErr.Message, "Invalid request")
|
||||
require.Len(t, apiErr.Validations, 2)
|
||||
assert.Equal(t, apiErr.Validations[0].Field, "max_ttl_ms")
|
||||
assert.Equal(t, apiErr.Validations[1].Field, "min_autostart_interval_ms")
|
||||
|
||||
updated, err := client.Template(ctx, template.ID)
|
||||
require.NoError(t, err)
|
||||
assert.WithinDuration(t, template.UpdatedAt, updated.UpdatedAt, time.Minute)
|
||||
assert.Equal(t, template.Description, updated.Description)
|
||||
assert.Equal(t, template.MaxTTLMillis, updated.MaxTTLMillis)
|
||||
assert.Equal(t, template.MinAutostartIntervalMillis, updated.MinAutostartIntervalMillis)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDeleteTemplate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// Template is the JSON representation of a Coder template. This type matches the
|
||||
|
@ -30,6 +31,12 @@ type UpdateActiveTemplateVersion struct {
|
|||
ID uuid.UUID `json:"id" validate:"required"`
|
||||
}
|
||||
|
||||
type UpdateTemplateMeta struct {
|
||||
Description string `json:"description,omitempty"`
|
||||
MaxTTLMillis int64 `json:"max_ttl_ms,omitempty"`
|
||||
MinAutostartIntervalMillis int64 `json:"min_autostart_interval_ms,omitempty"`
|
||||
}
|
||||
|
||||
// Template returns a single template.
|
||||
func (c *Client) Template(ctx context.Context, template uuid.UUID) (Template, error) {
|
||||
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/templates/%s", template), nil)
|
||||
|
@ -56,6 +63,22 @@ func (c *Client) DeleteTemplate(ctx context.Context, template uuid.UUID) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) UpdateTemplateMeta(ctx context.Context, templateID uuid.UUID, req UpdateTemplateMeta) (Template, error) {
|
||||
res, err := c.Request(ctx, http.MethodPatch, fmt.Sprintf("/api/v2/templates/%s", templateID), req)
|
||||
if err != nil {
|
||||
return Template{}, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode == http.StatusNotModified {
|
||||
return Template{}, xerrors.New("template metadata not modified")
|
||||
}
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return Template{}, readBodyAsError(res)
|
||||
}
|
||||
var updated Template
|
||||
return updated, json.NewDecoder(res.Body).Decode(&updated)
|
||||
}
|
||||
|
||||
// UpdateActiveTemplateVersion updates the active template version to the ID provided.
|
||||
// The template version must be attached to the template.
|
||||
func (c *Client) UpdateActiveTemplateVersion(ctx context.Context, template uuid.UUID, req UpdateActiveTemplateVersion) error {
|
||||
|
|
|
@ -234,7 +234,7 @@ export interface Role {
|
|||
readonly display_name: string
|
||||
}
|
||||
|
||||
// From codersdk/templates.go:15:6
|
||||
// From codersdk/templates.go:16:6
|
||||
export interface Template {
|
||||
readonly id: string
|
||||
readonly created_at: string
|
||||
|
@ -276,12 +276,12 @@ export interface TemplateVersionParameter {
|
|||
readonly default_source_value: boolean
|
||||
}
|
||||
|
||||
// From codersdk/templates.go:75:6
|
||||
// From codersdk/templates.go:98:6
|
||||
export interface TemplateVersionsByTemplateRequest extends Pagination {
|
||||
readonly template_id: string
|
||||
}
|
||||
|
||||
// From codersdk/templates.go:29:6
|
||||
// From codersdk/templates.go:30:6
|
||||
export interface UpdateActiveTemplateVersion {
|
||||
readonly id: string
|
||||
}
|
||||
|
@ -291,6 +291,13 @@ export interface UpdateRoles {
|
|||
readonly roles: string[]
|
||||
}
|
||||
|
||||
// From codersdk/templates.go:34:6
|
||||
export interface UpdateTemplateMeta {
|
||||
readonly description?: string
|
||||
readonly max_ttl_ms?: number
|
||||
readonly min_autostart_interval_ms?: number
|
||||
}
|
||||
|
||||
// From codersdk/users.go:66:6
|
||||
export interface UpdateUserPasswordRequest {
|
||||
readonly old_password: string
|
||||
|
|
Loading…
Reference in New Issue