chore: add 'classic_parameter_flow' column setting to templates (#17828)

We are forcing users to try the dynamic parameter experience first.
Currently this setting only comes into effect if an experiment is
enabled.
This commit is contained in:
Steven Masley
2025-05-15 17:55:17 -05:00
committed by GitHub
parent 9063b67c4d
commit c2bc801f83
21 changed files with 229 additions and 45 deletions

3
coderd/apidoc/docs.go generated
View File

@ -15573,6 +15573,9 @@ const docTemplate = `{
"updated_at": {
"type": "string",
"format": "date-time"
},
"use_classic_parameter_flow": {
"type": "boolean"
}
}
},

View File

@ -14167,6 +14167,9 @@
"updated_at": {
"type": "string",
"format": "date-time"
},
"use_classic_parameter_flow": {
"type": "boolean"
}
}
},

View File

@ -11084,6 +11084,7 @@ func (q *FakeQuerier) UpdateTemplateMetaByID(_ context.Context, arg database.Upd
tpl.GroupACL = arg.GroupACL
tpl.AllowUserCancelWorkspaceJobs = arg.AllowUserCancelWorkspaceJobs
tpl.MaxPortSharingLevel = arg.MaxPortSharingLevel
tpl.UseClassicParameterFlow = arg.UseClassicParameterFlow
q.templates[idx] = tpl
return nil
}

View File

@ -1560,7 +1560,8 @@ CREATE TABLE templates (
require_active_version boolean DEFAULT false NOT NULL,
deprecated text DEFAULT ''::text NOT NULL,
activity_bump bigint DEFAULT '3600000000000'::bigint NOT NULL,
max_port_sharing_level app_sharing_level DEFAULT 'owner'::app_sharing_level NOT NULL
max_port_sharing_level app_sharing_level DEFAULT 'owner'::app_sharing_level NOT NULL,
use_classic_parameter_flow boolean DEFAULT false NOT NULL
);
COMMENT ON COLUMN templates.default_ttl IS 'The default duration for autostop for workspaces created from this template.';
@ -1581,6 +1582,8 @@ COMMENT ON COLUMN templates.autostart_block_days_of_week IS 'A bitmap of days of
COMMENT ON COLUMN templates.deprecated IS 'If set to a non empty string, the template will no longer be able to be used. The message will be displayed to the user.';
COMMENT ON COLUMN templates.use_classic_parameter_flow IS 'Determines whether to default to the dynamic parameter creation flow for this template or continue using the legacy classic parameter creation flow.This is a template wide setting, the template admin can revert to the classic flow if there are any issues. An escape hatch is required, as workspace creation is a core workflow and cannot break. This column will be removed when the dynamic parameter creation flow is stable.';
CREATE VIEW template_with_names AS
SELECT templates.id,
templates.created_at,
@ -1610,6 +1613,7 @@ CREATE VIEW template_with_names AS
templates.deprecated,
templates.activity_bump,
templates.max_port_sharing_level,
templates.use_classic_parameter_flow,
COALESCE(visible_users.avatar_url, ''::text) AS created_by_avatar_url,
COALESCE(visible_users.username, ''::text) AS created_by_username,
COALESCE(organizations.name, ''::text) AS organization_name,

View File

@ -0,0 +1,28 @@
DROP VIEW template_with_names;
-- Drop the column
ALTER TABLE templates DROP COLUMN use_classic_parameter_flow;
CREATE VIEW
template_with_names
AS
SELECT
templates.*,
coalesce(visible_users.avatar_url, '') AS created_by_avatar_url,
coalesce(visible_users.username, '') AS created_by_username,
coalesce(organizations.name, '') AS organization_name,
coalesce(organizations.display_name, '') AS organization_display_name,
coalesce(organizations.icon, '') AS organization_icon
FROM
templates
LEFT JOIN
visible_users
ON
templates.created_by = visible_users.id
LEFT JOIN
organizations
ON templates.organization_id = organizations.id
;
COMMENT ON VIEW template_with_names IS 'Joins in the display name information such as username, avatar, and organization name.';

View File

@ -0,0 +1,36 @@
-- Default to `false`. Users will have to manually opt back into the classic parameter flow.
-- We want the new experience to be tried first.
ALTER TABLE templates ADD COLUMN use_classic_parameter_flow BOOL NOT NULL DEFAULT false;
COMMENT ON COLUMN templates.use_classic_parameter_flow IS
'Determines whether to default to the dynamic parameter creation flow for this template '
'or continue using the legacy classic parameter creation flow.'
'This is a template wide setting, the template admin can revert to the classic flow if there are any issues. '
'An escape hatch is required, as workspace creation is a core workflow and cannot break. '
'This column will be removed when the dynamic parameter creation flow is stable.';
-- Update the template_with_names view by recreating it.
DROP VIEW template_with_names;
CREATE VIEW
template_with_names
AS
SELECT
templates.*,
coalesce(visible_users.avatar_url, '') AS created_by_avatar_url,
coalesce(visible_users.username, '') AS created_by_username,
coalesce(organizations.name, '') AS organization_name,
coalesce(organizations.display_name, '') AS organization_display_name,
coalesce(organizations.icon, '') AS organization_icon
FROM
templates
LEFT JOIN
visible_users
ON
templates.created_by = visible_users.id
LEFT JOIN
organizations
ON templates.organization_id = organizations.id
;
COMMENT ON VIEW template_with_names IS 'Joins in the display name information such as username, avatar, and organization name.';

View File

@ -117,6 +117,7 @@ func (q *sqlQuerier) GetAuthorizedTemplates(ctx context.Context, arg GetTemplate
&i.Deprecated,
&i.ActivityBump,
&i.MaxPortSharingLevel,
&i.UseClassicParameterFlow,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
&i.OrganizationName,

View File

@ -3114,6 +3114,7 @@ type Template struct {
Deprecated string `db:"deprecated" json:"deprecated"`
ActivityBump int64 `db:"activity_bump" json:"activity_bump"`
MaxPortSharingLevel AppSharingLevel `db:"max_port_sharing_level" json:"max_port_sharing_level"`
UseClassicParameterFlow bool `db:"use_classic_parameter_flow" json:"use_classic_parameter_flow"`
CreatedByAvatarURL string `db:"created_by_avatar_url" json:"created_by_avatar_url"`
CreatedByUsername string `db:"created_by_username" json:"created_by_username"`
OrganizationName string `db:"organization_name" json:"organization_name"`
@ -3159,6 +3160,8 @@ type TemplateTable struct {
Deprecated string `db:"deprecated" json:"deprecated"`
ActivityBump int64 `db:"activity_bump" json:"activity_bump"`
MaxPortSharingLevel AppSharingLevel `db:"max_port_sharing_level" json:"max_port_sharing_level"`
// Determines whether to default to the dynamic parameter creation flow for this template or continue using the legacy classic parameter creation flow.This is a template wide setting, the template admin can revert to the classic flow if there are any issues. An escape hatch is required, as workspace creation is a core workflow and cannot break. This column will be removed when the dynamic parameter creation flow is stable.
UseClassicParameterFlow bool `db:"use_classic_parameter_flow" json:"use_classic_parameter_flow"`
}
// Records aggregated usage statistics for templates/users. All usage is rounded up to the nearest minute.

View File

@ -10427,7 +10427,7 @@ func (q *sqlQuerier) GetTemplateAverageBuildTime(ctx context.Context, arg GetTem
const getTemplateByID = `-- name: GetTemplateByID :one
SELECT
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, created_by_avatar_url, created_by_username, organization_name, organization_display_name, organization_icon
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, use_classic_parameter_flow, created_by_avatar_url, created_by_username, organization_name, organization_display_name, organization_icon
FROM
template_with_names
WHERE
@ -10468,6 +10468,7 @@ func (q *sqlQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (Templat
&i.Deprecated,
&i.ActivityBump,
&i.MaxPortSharingLevel,
&i.UseClassicParameterFlow,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
&i.OrganizationName,
@ -10479,7 +10480,7 @@ func (q *sqlQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (Templat
const getTemplateByOrganizationAndName = `-- name: GetTemplateByOrganizationAndName :one
SELECT
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, created_by_avatar_url, created_by_username, organization_name, organization_display_name, organization_icon
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, use_classic_parameter_flow, created_by_avatar_url, created_by_username, organization_name, organization_display_name, organization_icon
FROM
template_with_names AS templates
WHERE
@ -10528,6 +10529,7 @@ func (q *sqlQuerier) GetTemplateByOrganizationAndName(ctx context.Context, arg G
&i.Deprecated,
&i.ActivityBump,
&i.MaxPortSharingLevel,
&i.UseClassicParameterFlow,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
&i.OrganizationName,
@ -10538,7 +10540,7 @@ func (q *sqlQuerier) GetTemplateByOrganizationAndName(ctx context.Context, arg G
}
const getTemplates = `-- name: GetTemplates :many
SELECT id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, created_by_avatar_url, created_by_username, organization_name, organization_display_name, organization_icon FROM template_with_names AS templates
SELECT id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, use_classic_parameter_flow, created_by_avatar_url, created_by_username, organization_name, organization_display_name, organization_icon FROM template_with_names AS templates
ORDER BY (name, id) ASC
`
@ -10580,6 +10582,7 @@ func (q *sqlQuerier) GetTemplates(ctx context.Context) ([]Template, error) {
&i.Deprecated,
&i.ActivityBump,
&i.MaxPortSharingLevel,
&i.UseClassicParameterFlow,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
&i.OrganizationName,
@ -10601,7 +10604,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, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, created_by_avatar_url, created_by_username, organization_name, organization_display_name, organization_icon
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, use_classic_parameter_flow, created_by_avatar_url, created_by_username, organization_name, organization_display_name, organization_icon
FROM
template_with_names AS templates
WHERE
@ -10701,6 +10704,7 @@ func (q *sqlQuerier) GetTemplatesWithFilter(ctx context.Context, arg GetTemplate
&i.Deprecated,
&i.ActivityBump,
&i.MaxPortSharingLevel,
&i.UseClassicParameterFlow,
&i.CreatedByAvatarURL,
&i.CreatedByUsername,
&i.OrganizationName,
@ -10877,7 +10881,8 @@ SET
display_name = $6,
allow_user_cancel_workspace_jobs = $7,
group_acl = $8,
max_port_sharing_level = $9
max_port_sharing_level = $9,
use_classic_parameter_flow = $10
WHERE
id = $1
`
@ -10892,6 +10897,7 @@ type UpdateTemplateMetaByIDParams struct {
AllowUserCancelWorkspaceJobs bool `db:"allow_user_cancel_workspace_jobs" json:"allow_user_cancel_workspace_jobs"`
GroupACL TemplateACL `db:"group_acl" json:"group_acl"`
MaxPortSharingLevel AppSharingLevel `db:"max_port_sharing_level" json:"max_port_sharing_level"`
UseClassicParameterFlow bool `db:"use_classic_parameter_flow" json:"use_classic_parameter_flow"`
}
func (q *sqlQuerier) UpdateTemplateMetaByID(ctx context.Context, arg UpdateTemplateMetaByIDParams) error {
@ -10905,6 +10911,7 @@ func (q *sqlQuerier) UpdateTemplateMetaByID(ctx context.Context, arg UpdateTempl
arg.AllowUserCancelWorkspaceJobs,
arg.GroupACL,
arg.MaxPortSharingLevel,
arg.UseClassicParameterFlow,
)
return err
}
@ -18197,7 +18204,7 @@ LEFT JOIN LATERAL (
) latest_build ON TRUE
LEFT JOIN LATERAL (
SELECT
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level
id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level, use_classic_parameter_flow
FROM
templates
WHERE

View File

@ -124,7 +124,8 @@ SET
display_name = $6,
allow_user_cancel_workspace_jobs = $7,
group_acl = $8,
max_port_sharing_level = $9
max_port_sharing_level = $9,
use_classic_parameter_flow = $10
WHERE
id = $1
;

View File

@ -728,6 +728,12 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
return
}
// Defaults to the existing.
classicTemplateFlow := template.UseClassicParameterFlow
if req.UseClassicParameterFlow != nil {
classicTemplateFlow = *req.UseClassicParameterFlow
}
var updated database.Template
err = api.Database.InTx(func(tx database.Store) error {
if req.Name == template.Name &&
@ -747,6 +753,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
req.TimeTilDormantAutoDeleteMillis == time.Duration(template.TimeTilDormantAutoDelete).Milliseconds() &&
req.RequireActiveVersion == template.RequireActiveVersion &&
(deprecationMessage == template.Deprecated) &&
(classicTemplateFlow == template.UseClassicParameterFlow) &&
maxPortShareLevel == template.MaxPortSharingLevel {
return nil
}
@ -788,6 +795,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
AllowUserCancelWorkspaceJobs: req.AllowUserCancelWorkspaceJobs,
GroupACL: groupACL,
MaxPortSharingLevel: maxPortShareLevel,
UseClassicParameterFlow: classicTemplateFlow,
})
if err != nil {
return xerrors.Errorf("update template metadata: %w", err)
@ -1070,6 +1078,7 @@ func (api *API) convertTemplate(
Deprecated: templateAccessControl.IsDeprecated(),
DeprecationMessage: templateAccessControl.Deprecated,
MaxPortShareLevel: maxPortShareLevel,
UseClassicParameterFlow: template.UseClassicParameterFlow,
}
}

View File

@ -1540,6 +1540,41 @@ func TestPatchTemplateMeta(t *testing.T) {
require.False(t, template.Deprecated)
})
})
t.Run("ClassicParameterFlow", func(t *testing.T) {
t.Parallel()
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)
require.False(t, template.UseClassicParameterFlow, "default is false")
bTrue := true
bFalse := false
req := codersdk.UpdateTemplateMeta{
UseClassicParameterFlow: &bTrue,
}
ctx := testutil.Context(t, testutil.WaitLong)
// set to true
updated, err := client.UpdateTemplateMeta(ctx, template.ID, req)
require.NoError(t, err)
assert.True(t, updated.UseClassicParameterFlow, "expected true")
// noop
req.UseClassicParameterFlow = nil
updated, err = client.UpdateTemplateMeta(ctx, template.ID, req)
require.NoError(t, err)
assert.True(t, updated.UseClassicParameterFlow, "expected true")
// back to false
req.UseClassicParameterFlow = &bFalse
updated, err = client.UpdateTemplateMeta(ctx, template.ID, req)
require.NoError(t, err)
assert.False(t, updated.UseClassicParameterFlow, "expected false")
})
}
func TestDeleteTemplate(t *testing.T) {

View File

@ -61,6 +61,8 @@ type Template struct {
// template version.
RequireActiveVersion bool `json:"require_active_version"`
MaxPortShareLevel WorkspaceAgentPortShareLevel `json:"max_port_share_level"`
UseClassicParameterFlow bool `json:"use_classic_parameter_flow"`
}
// WeekdaysToBitmap converts a list of weekdays to a bitmap in accordance with
@ -250,6 +252,12 @@ type UpdateTemplateMeta struct {
// of the template.
DisableEveryoneGroupAccess bool `json:"disable_everyone_group_access"`
MaxPortShareLevel *WorkspaceAgentPortShareLevel `json:"max_port_share_level,omitempty"`
// UseClassicParameterFlow is a flag that switches the default behavior to use the classic
// parameter flow when creating a workspace. This only affects deployments with the experiment
// "dynamic-parameters" enabled. This setting will live for a period after the experiment is
// made the default.
// An "opt-out" is present in case the new feature breaks some existing templates.
UseClassicParameterFlow *bool `json:"use_classic_parameter_flow,omitempty"`
}
type TemplateExample struct {

View File

@ -9,7 +9,7 @@ We track the following resources:
<!-- Code generated by 'make docs/admin/security/audit-logs.md'. DO NOT EDIT -->
| <b>Resource<b> | | |
|----------------------------------------------------------|----------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|----------------------------------------------------------|----------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| APIKey<br><i>login, logout, register, create, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>created_at</td><td>true</td></tr><tr><td>expires_at</td><td>true</td></tr><tr><td>hashed_secret</td><td>false</td></tr><tr><td>id</td><td>false</td></tr><tr><td>ip_address</td><td>false</td></tr><tr><td>last_used</td><td>true</td></tr><tr><td>lifetime_seconds</td><td>false</td></tr><tr><td>login_type</td><td>false</td></tr><tr><td>scope</td><td>false</td></tr><tr><td>token_name</td><td>false</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_id</td><td>true</td></tr></tbody></table> |
| AuditOAuthConvertState<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>created_at</td><td>true</td></tr><tr><td>expires_at</td><td>true</td></tr><tr><td>from_login_type</td><td>true</td></tr><tr><td>to_login_type</td><td>true</td></tr><tr><td>user_id</td><td>true</td></tr></tbody></table> |
| Group<br><i>create, write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>avatar_url</td><td>true</td></tr><tr><td>display_name</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>members</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>quota_allowance</td><td>true</td></tr><tr><td>source</td><td>false</td></tr></tbody></table> |
@ -26,7 +26,7 @@ We track the following resources:
| Organization<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>created_at</td><td>false</td></tr><tr><td>deleted</td><td>true</td></tr><tr><td>description</td><td>true</td></tr><tr><td>display_name</td><td>true</td></tr><tr><td>icon</td><td>true</td></tr><tr><td>id</td><td>false</td></tr><tr><td>is_default</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>updated_at</td><td>true</td></tr></tbody></table> |
| OrganizationSyncSettings<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>assign_default</td><td>true</td></tr><tr><td>field</td><td>true</td></tr><tr><td>mapping</td><td>true</td></tr></tbody></table> |
| RoleSyncSettings<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>field</td><td>true</td></tr><tr><td>mapping</td><td>true</td></tr></tbody></table> |
| Template<br><i>write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>active_version_id</td><td>true</td></tr><tr><td>activity_bump</td><td>true</td></tr><tr><td>allow_user_autostart</td><td>true</td></tr><tr><td>allow_user_autostop</td><td>true</td></tr><tr><td>allow_user_cancel_workspace_jobs</td><td>true</td></tr><tr><td>autostart_block_days_of_week</td><td>true</td></tr><tr><td>autostop_requirement_days_of_week</td><td>true</td></tr><tr><td>autostop_requirement_weeks</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>created_by</td><td>true</td></tr><tr><td>created_by_avatar_url</td><td>false</td></tr><tr><td>created_by_username</td><td>false</td></tr><tr><td>default_ttl</td><td>true</td></tr><tr><td>deleted</td><td>false</td></tr><tr><td>deprecated</td><td>true</td></tr><tr><td>description</td><td>true</td></tr><tr><td>display_name</td><td>true</td></tr><tr><td>failure_ttl</td><td>true</td></tr><tr><td>group_acl</td><td>true</td></tr><tr><td>icon</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>max_port_sharing_level</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_display_name</td><td>false</td></tr><tr><td>organization_icon</td><td>false</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>organization_name</td><td>false</td></tr><tr><td>provisioner</td><td>true</td></tr><tr><td>require_active_version</td><td>true</td></tr><tr><td>time_til_dormant</td><td>true</td></tr><tr><td>time_til_dormant_autodelete</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_acl</td><td>true</td></tr></tbody></table> |
| Template<br><i>write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>active_version_id</td><td>true</td></tr><tr><td>activity_bump</td><td>true</td></tr><tr><td>allow_user_autostart</td><td>true</td></tr><tr><td>allow_user_autostop</td><td>true</td></tr><tr><td>allow_user_cancel_workspace_jobs</td><td>true</td></tr><tr><td>autostart_block_days_of_week</td><td>true</td></tr><tr><td>autostop_requirement_days_of_week</td><td>true</td></tr><tr><td>autostop_requirement_weeks</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>created_by</td><td>true</td></tr><tr><td>created_by_avatar_url</td><td>false</td></tr><tr><td>created_by_username</td><td>false</td></tr><tr><td>default_ttl</td><td>true</td></tr><tr><td>deleted</td><td>false</td></tr><tr><td>deprecated</td><td>true</td></tr><tr><td>description</td><td>true</td></tr><tr><td>display_name</td><td>true</td></tr><tr><td>failure_ttl</td><td>true</td></tr><tr><td>group_acl</td><td>true</td></tr><tr><td>icon</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>max_port_sharing_level</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_display_name</td><td>false</td></tr><tr><td>organization_icon</td><td>false</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>organization_name</td><td>false</td></tr><tr><td>provisioner</td><td>true</td></tr><tr><td>require_active_version</td><td>true</td></tr><tr><td>time_til_dormant</td><td>true</td></tr><tr><td>time_til_dormant_autodelete</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>use_classic_parameter_flow</td><td>true</td></tr><tr><td>user_acl</td><td>true</td></tr></tbody></table> |
| TemplateVersion<br><i>create, write</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>archived</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>created_by</td><td>true</td></tr><tr><td>created_by_avatar_url</td><td>false</td></tr><tr><td>created_by_username</td><td>false</td></tr><tr><td>external_auth_providers</td><td>false</td></tr><tr><td>id</td><td>true</td></tr><tr><td>job_id</td><td>false</td></tr><tr><td>message</td><td>false</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>readme</td><td>true</td></tr><tr><td>source_example_id</td><td>false</td></tr><tr><td>template_id</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr></tbody></table> |
| User<br><i>create, write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>avatar_url</td><td>false</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>deleted</td><td>true</td></tr><tr><td>email</td><td>true</td></tr><tr><td>github_com_user_id</td><td>false</td></tr><tr><td>hashed_one_time_passcode</td><td>false</td></tr><tr><td>hashed_password</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>is_system</td><td>true</td></tr><tr><td>last_seen_at</td><td>false</td></tr><tr><td>login_type</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>one_time_passcode_expires_at</td><td>true</td></tr><tr><td>quiet_hours_schedule</td><td>true</td></tr><tr><td>rbac_roles</td><td>true</td></tr><tr><td>status</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>username</td><td>true</td></tr></tbody></table> |
| WorkspaceAgent<br><i>connect, disconnect</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>api_key_scope</td><td>false</td></tr><tr><td>api_version</td><td>false</td></tr><tr><td>architecture</td><td>false</td></tr><tr><td>auth_instance_id</td><td>false</td></tr><tr><td>auth_token</td><td>false</td></tr><tr><td>connection_timeout_seconds</td><td>false</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>directory</td><td>false</td></tr><tr><td>disconnected_at</td><td>false</td></tr><tr><td>display_apps</td><td>false</td></tr><tr><td>display_order</td><td>false</td></tr><tr><td>environment_variables</td><td>false</td></tr><tr><td>expanded_directory</td><td>false</td></tr><tr><td>first_connected_at</td><td>false</td></tr><tr><td>id</td><td>false</td></tr><tr><td>instance_metadata</td><td>false</td></tr><tr><td>last_connected_at</td><td>false</td></tr><tr><td>last_connected_replica_id</td><td>false</td></tr><tr><td>lifecycle_state</td><td>false</td></tr><tr><td>logs_length</td><td>false</td></tr><tr><td>logs_overflowed</td><td>false</td></tr><tr><td>motd_file</td><td>false</td></tr><tr><td>name</td><td>false</td></tr><tr><td>operating_system</td><td>false</td></tr><tr><td>parent_id</td><td>false</td></tr><tr><td>ready_at</td><td>false</td></tr><tr><td>resource_id</td><td>false</td></tr><tr><td>resource_metadata</td><td>false</td></tr><tr><td>started_at</td><td>false</td></tr><tr><td>subsystems</td><td>false</td></tr><tr><td>troubleshooting_url</td><td>false</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>version</td><td>false</td></tr></tbody></table> |

View File

@ -6593,7 +6593,8 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith
"require_active_version": true,
"time_til_dormant_autodelete_ms": 0,
"time_til_dormant_ms": 0,
"updated_at": "2019-08-24T14:15:22Z"
"updated_at": "2019-08-24T14:15:22Z",
"use_classic_parameter_flow": true
}
```
@ -6632,6 +6633,7 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith
| `time_til_dormant_autodelete_ms` | integer | false | | |
| `time_til_dormant_ms` | integer | false | | |
| `updated_at` | string | false | | |
| `use_classic_parameter_flow` | boolean | false | | |
#### Enumerated Values

View File

@ -78,7 +78,8 @@ To include deprecated templates, specify `deprecated:true` in the search query.
"require_active_version": true,
"time_til_dormant_autodelete_ms": 0,
"time_til_dormant_ms": 0,
"updated_at": "2019-08-24T14:15:22Z"
"updated_at": "2019-08-24T14:15:22Z",
"use_classic_parameter_flow": true
}
]
```
@ -134,6 +135,7 @@ Restarts will only happen on weekdays in this list on weeks which line up with W
|`» time_til_dormant_autodelete_ms`|integer|false|||
|`» time_til_dormant_ms`|integer|false|||
|`» updated_at`|string(date-time)|false|||
|`» use_classic_parameter_flow`|boolean|false|||
#### Enumerated Values
@ -255,7 +257,8 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa
"require_active_version": true,
"time_til_dormant_autodelete_ms": 0,
"time_til_dormant_ms": 0,
"updated_at": "2019-08-24T14:15:22Z"
"updated_at": "2019-08-24T14:15:22Z",
"use_classic_parameter_flow": true
}
```
@ -403,7 +406,8 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat
"require_active_version": true,
"time_til_dormant_autodelete_ms": 0,
"time_til_dormant_ms": 0,
"updated_at": "2019-08-24T14:15:22Z"
"updated_at": "2019-08-24T14:15:22Z",
"use_classic_parameter_flow": true
}
```
@ -802,7 +806,8 @@ To include deprecated templates, specify `deprecated:true` in the search query.
"require_active_version": true,
"time_til_dormant_autodelete_ms": 0,
"time_til_dormant_ms": 0,
"updated_at": "2019-08-24T14:15:22Z"
"updated_at": "2019-08-24T14:15:22Z",
"use_classic_parameter_flow": true
}
]
```
@ -858,6 +863,7 @@ Restarts will only happen on weekdays in this list on weeks which line up with W
|`» time_til_dormant_autodelete_ms`|integer|false|||
|`» time_til_dormant_ms`|integer|false|||
|`» updated_at`|string(date-time)|false|||
|`» use_classic_parameter_flow`|boolean|false|||
#### Enumerated Values
@ -999,7 +1005,8 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template} \
"require_active_version": true,
"time_til_dormant_autodelete_ms": 0,
"time_til_dormant_ms": 0,
"updated_at": "2019-08-24T14:15:22Z"
"updated_at": "2019-08-24T14:15:22Z",
"use_classic_parameter_flow": true
}
```
@ -1128,7 +1135,8 @@ curl -X PATCH http://coder-server:8080/api/v2/templates/{template} \
"require_active_version": true,
"time_til_dormant_autodelete_ms": 0,
"time_til_dormant_ms": 0,
"updated_at": "2019-08-24T14:15:22Z"
"updated_at": "2019-08-24T14:15:22Z",
"use_classic_parameter_flow": true
}
```

View File

@ -115,6 +115,7 @@ var auditableResourcesTypes = map[any]map[string]Action{
"deprecated": ActionTrack,
"max_port_sharing_level": ActionTrack,
"activity_bump": ActionTrack,
"use_classic_parameter_flow": ActionTrack,
},
&database.TemplateVersion{}: {
"id": ActionTrack,

View File

@ -2586,6 +2586,7 @@ export interface Template {
readonly time_til_dormant_autodelete_ms: number;
readonly require_active_version: boolean;
readonly max_port_share_level: WorkspaceAgentPortShareLevel;
readonly use_classic_parameter_flow: boolean;
}
// From codersdk/templates.go
@ -2956,6 +2957,7 @@ export interface UpdateTemplateMeta {
readonly deprecation_message?: string;
readonly disable_everyone_group_access: boolean;
readonly max_port_share_level?: WorkspaceAgentPortShareLevel;
readonly use_classic_parameter_flow?: boolean;
}
// From codersdk/users.go

View File

@ -47,6 +47,7 @@ export const validationSchema = Yup.object({
allow_user_cancel_workspace_jobs: Yup.boolean(),
icon: iconValidator,
require_active_version: Yup.boolean(),
use_classic_parameter_flow: Yup.boolean(),
deprecation_message: Yup.string(),
max_port_sharing_level: Yup.string().oneOf(WorkspaceAppSharingLevels),
});
@ -89,6 +90,7 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
deprecation_message: template.deprecation_message,
disable_everyone_group_access: false,
max_port_share_level: template.max_port_share_level,
use_classic_parameter_flow: template.use_classic_parameter_flow,
},
validationSchema,
onSubmit,
@ -222,6 +224,34 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
</StackLabel>
}
/>
<FormControlLabel
control={
<Checkbox
size="small"
id="use_classic_parameter_flow"
name="use_classic_parameter_flow"
checked={form.values.use_classic_parameter_flow}
onChange={form.handleChange}
disabled={false}
/>
}
label={
<StackLabel>
Use classic workspace creation form
<StackLabelHelperText>
<span>
Show the original workspace creation form without dynamic
parameters or live updates. Recommended if your provisioners
aren't updated or the new form causes issues.
<strong>
Users can always manually switch experiences in the
workspace creation form.
</strong>
</span>
</StackLabelHelperText>
</StackLabel>
}
/>
</FormFields>
</FormSection>

View File

@ -54,6 +54,7 @@ const validFormValues: FormValues = {
require_active_version: false,
disable_everyone_group_access: false,
max_port_share_level: "owner",
use_classic_parameter_flow: false,
};
const renderTemplateSettingsPage = async () => {

View File

@ -824,6 +824,7 @@ export const MockTemplate: TypesGen.Template = {
deprecated: false,
deprecation_message: "",
max_port_share_level: "public",
use_classic_parameter_flow: false,
};
const MockTemplateVersionFiles: TemplateVersionFiles = {