mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
feat: add template abstract metadata
This commit is contained in:
Generated
+14
@@ -17883,6 +17883,11 @@ const docTemplate = `{
|
|||||||
"template_version_id"
|
"template_version_id"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"abstract": {
|
||||||
|
"description": "Abstract is a longer-form summary surfaced to agents to help them pick\nthe right template. Up to 2048 characters.",
|
||||||
|
"type": "string",
|
||||||
|
"maxLength": 2048
|
||||||
|
},
|
||||||
"activity_bump_ms": {
|
"activity_bump_ms": {
|
||||||
"description": "ActivityBumpMillis allows optionally specifying the activity bump\nduration for all workspaces created from this template. Defaults to 1h\nbut can be set to 0 to disable activity bumping.",
|
"description": "ActivityBumpMillis allows optionally specifying the activity bump\nduration for all workspaces created from this template. Defaults to 1h\nbut can be set to 0 to disable activity bumping.",
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
@@ -23315,6 +23320,10 @@ const docTemplate = `{
|
|||||||
"codersdk.Template": {
|
"codersdk.Template": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"abstract": {
|
||||||
|
"description": "Abstract is a longer-form summary surfaced to agents to help them pick\nthe right template. Up to 2048 characters.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"active_user_count": {
|
"active_user_count": {
|
||||||
"description": "ActiveUserCount is set to -1 when loading.",
|
"description": "ActiveUserCount is set to -1 when loading.",
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
@@ -24427,6 +24436,11 @@ const docTemplate = `{
|
|||||||
"codersdk.UpdateTemplateMeta": {
|
"codersdk.UpdateTemplateMeta": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"abstract": {
|
||||||
|
"description": "Abstract is a longer-form summary surfaced to agents to help them pick\nthe right template. Up to 2048 characters.",
|
||||||
|
"type": "string",
|
||||||
|
"maxLength": 2048
|
||||||
|
},
|
||||||
"activity_bump_ms": {
|
"activity_bump_ms": {
|
||||||
"description": "ActivityBumpMillis allows optionally specifying the activity bump\nduration for all workspaces created from this template. Defaults to 1h\nbut can be set to 0 to disable activity bumping.",
|
"description": "ActivityBumpMillis allows optionally specifying the activity bump\nduration for all workspaces created from this template. Defaults to 1h\nbut can be set to 0 to disable activity bumping.",
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
|
|||||||
Generated
+14
@@ -16174,6 +16174,11 @@
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"required": ["name", "template_version_id"],
|
"required": ["name", "template_version_id"],
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"abstract": {
|
||||||
|
"description": "Abstract is a longer-form summary surfaced to agents to help them pick\nthe right template. Up to 2048 characters.",
|
||||||
|
"type": "string",
|
||||||
|
"maxLength": 2048
|
||||||
|
},
|
||||||
"activity_bump_ms": {
|
"activity_bump_ms": {
|
||||||
"description": "ActivityBumpMillis allows optionally specifying the activity bump\nduration for all workspaces created from this template. Defaults to 1h\nbut can be set to 0 to disable activity bumping.",
|
"description": "ActivityBumpMillis allows optionally specifying the activity bump\nduration for all workspaces created from this template. Defaults to 1h\nbut can be set to 0 to disable activity bumping.",
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
@@ -21411,6 +21416,10 @@
|
|||||||
"codersdk.Template": {
|
"codersdk.Template": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"abstract": {
|
||||||
|
"description": "Abstract is a longer-form summary surfaced to agents to help them pick\nthe right template. Up to 2048 characters.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"active_user_count": {
|
"active_user_count": {
|
||||||
"description": "ActiveUserCount is set to -1 when loading.",
|
"description": "ActiveUserCount is set to -1 when loading.",
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
@@ -22464,6 +22473,11 @@
|
|||||||
"codersdk.UpdateTemplateMeta": {
|
"codersdk.UpdateTemplateMeta": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"abstract": {
|
||||||
|
"description": "Abstract is a longer-form summary surfaced to agents to help them pick\nthe right template. Up to 2048 characters.",
|
||||||
|
"type": "string",
|
||||||
|
"maxLength": 2048
|
||||||
|
},
|
||||||
"activity_bump_ms": {
|
"activity_bump_ms": {
|
||||||
"description": "ActivityBumpMillis allows optionally specifying the activity bump\nduration for all workspaces created from this template. Defaults to 1h\nbut can be set to 0 to disable activity bumping.",
|
"description": "ActivityBumpMillis allows optionally specifying the activity bump\nduration for all workspaces created from this template. Defaults to 1h\nbut can be set to 0 to disable activity bumping.",
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
|
|||||||
@@ -535,6 +535,7 @@ func Template(t testing.TB, db database.Store, seed database.Template) database.
|
|||||||
Provisioner: takeFirst(seed.Provisioner, database.ProvisionerTypeEcho),
|
Provisioner: takeFirst(seed.Provisioner, database.ProvisionerTypeEcho),
|
||||||
ActiveVersionID: takeFirst(seed.ActiveVersionID, uuid.New()),
|
ActiveVersionID: takeFirst(seed.ActiveVersionID, uuid.New()),
|
||||||
Description: takeFirst(seed.Description, testutil.GetRandomName(t)),
|
Description: takeFirst(seed.Description, testutil.GetRandomName(t)),
|
||||||
|
Abstract: seed.Abstract,
|
||||||
CreatedBy: takeFirst(seed.CreatedBy, uuid.New()),
|
CreatedBy: takeFirst(seed.CreatedBy, uuid.New()),
|
||||||
Icon: takeFirst(seed.Icon, testutil.GetRandomName(t)),
|
Icon: takeFirst(seed.Icon, testutil.GetRandomName(t)),
|
||||||
UserACL: seed.UserACL,
|
UserACL: seed.UserACL,
|
||||||
|
|||||||
Generated
+5
-1
@@ -3095,7 +3095,8 @@ CREATE TABLE templates (
|
|||||||
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,
|
use_classic_parameter_flow boolean DEFAULT false NOT NULL,
|
||||||
cors_behavior cors_behavior DEFAULT 'simple'::cors_behavior NOT NULL,
|
cors_behavior cors_behavior DEFAULT 'simple'::cors_behavior NOT NULL,
|
||||||
disable_module_cache boolean DEFAULT false NOT NULL
|
disable_module_cache boolean DEFAULT false NOT NULL,
|
||||||
|
abstract character varying(2048) DEFAULT ''::character varying NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
COMMENT ON COLUMN templates.default_ttl IS 'The default duration for autostop for workspaces created from this template.';
|
COMMENT ON COLUMN templates.default_ttl IS 'The default duration for autostop for workspaces created from this template.';
|
||||||
@@ -3118,6 +3119,8 @@ COMMENT ON COLUMN templates.deprecated IS 'If set to a non empty string, the tem
|
|||||||
|
|
||||||
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.';
|
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.';
|
||||||
|
|
||||||
|
COMMENT ON COLUMN templates.abstract IS 'Longer-form summary used to help agents pick the right template.';
|
||||||
|
|
||||||
CREATE VIEW template_with_names AS
|
CREATE VIEW template_with_names AS
|
||||||
SELECT templates.id,
|
SELECT templates.id,
|
||||||
templates.created_at,
|
templates.created_at,
|
||||||
@@ -3150,6 +3153,7 @@ CREATE VIEW template_with_names AS
|
|||||||
templates.use_classic_parameter_flow,
|
templates.use_classic_parameter_flow,
|
||||||
templates.cors_behavior,
|
templates.cors_behavior,
|
||||||
templates.disable_module_cache,
|
templates.disable_module_cache,
|
||||||
|
templates.abstract,
|
||||||
COALESCE(visible_users.avatar_url, ''::text) AS created_by_avatar_url,
|
COALESCE(visible_users.avatar_url, ''::text) AS created_by_avatar_url,
|
||||||
COALESCE(visible_users.username, ''::text) AS created_by_username,
|
COALESCE(visible_users.username, ''::text) AS created_by_username,
|
||||||
COALESCE(visible_users.name, ''::text) AS created_by_name,
|
COALESCE(visible_users.name, ''::text) AS created_by_name,
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
DROP VIEW template_with_names;
|
||||||
|
|
||||||
|
ALTER TABLE templates DROP COLUMN abstract;
|
||||||
|
|
||||||
|
CREATE VIEW template_with_names AS
|
||||||
|
SELECT templates.*,
|
||||||
|
COALESCE(visible_users.avatar_url, ''::text) AS created_by_avatar_url,
|
||||||
|
COALESCE(visible_users.username, ''::text) AS created_by_username,
|
||||||
|
COALESCE(visible_users.name, ''::text) AS created_by_name,
|
||||||
|
COALESCE(organizations.name, ''::text) AS organization_name,
|
||||||
|
COALESCE(organizations.display_name, ''::text) AS organization_display_name,
|
||||||
|
COALESCE(organizations.icon, ''::text) 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.';
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
DROP VIEW template_with_names;
|
||||||
|
|
||||||
|
ALTER TABLE templates
|
||||||
|
ADD COLUMN abstract character varying(2048) DEFAULT ''::character varying NOT NULL;
|
||||||
|
|
||||||
|
COMMENT ON COLUMN templates.abstract IS 'Longer-form summary used to help agents pick the right template.';
|
||||||
|
|
||||||
|
CREATE VIEW template_with_names AS
|
||||||
|
SELECT templates.*,
|
||||||
|
COALESCE(visible_users.avatar_url, ''::text) AS created_by_avatar_url,
|
||||||
|
COALESCE(visible_users.username, ''::text) AS created_by_username,
|
||||||
|
COALESCE(visible_users.name, ''::text) AS created_by_name,
|
||||||
|
COALESCE(organizations.name, ''::text) AS organization_name,
|
||||||
|
COALESCE(organizations.display_name, ''::text) AS organization_display_name,
|
||||||
|
COALESCE(organizations.icon, ''::text) 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.';
|
||||||
@@ -129,6 +129,7 @@ func (q *sqlQuerier) GetAuthorizedTemplates(ctx context.Context, arg GetTemplate
|
|||||||
&i.UseClassicParameterFlow,
|
&i.UseClassicParameterFlow,
|
||||||
&i.CorsBehavior,
|
&i.CorsBehavior,
|
||||||
&i.DisableModuleCache,
|
&i.DisableModuleCache,
|
||||||
|
&i.Abstract,
|
||||||
&i.CreatedByAvatarURL,
|
&i.CreatedByAvatarURL,
|
||||||
&i.CreatedByUsername,
|
&i.CreatedByUsername,
|
||||||
&i.CreatedByName,
|
&i.CreatedByName,
|
||||||
|
|||||||
Generated
+3
@@ -5479,6 +5479,7 @@ type Template struct {
|
|||||||
UseClassicParameterFlow bool `db:"use_classic_parameter_flow" json:"use_classic_parameter_flow"`
|
UseClassicParameterFlow bool `db:"use_classic_parameter_flow" json:"use_classic_parameter_flow"`
|
||||||
CorsBehavior CorsBehavior `db:"cors_behavior" json:"cors_behavior"`
|
CorsBehavior CorsBehavior `db:"cors_behavior" json:"cors_behavior"`
|
||||||
DisableModuleCache bool `db:"disable_module_cache" json:"disable_module_cache"`
|
DisableModuleCache bool `db:"disable_module_cache" json:"disable_module_cache"`
|
||||||
|
Abstract string `db:"abstract" json:"abstract"`
|
||||||
CreatedByAvatarURL string `db:"created_by_avatar_url" json:"created_by_avatar_url"`
|
CreatedByAvatarURL string `db:"created_by_avatar_url" json:"created_by_avatar_url"`
|
||||||
CreatedByUsername string `db:"created_by_username" json:"created_by_username"`
|
CreatedByUsername string `db:"created_by_username" json:"created_by_username"`
|
||||||
CreatedByName string `db:"created_by_name" json:"created_by_name"`
|
CreatedByName string `db:"created_by_name" json:"created_by_name"`
|
||||||
@@ -5529,6 +5530,8 @@ type TemplateTable struct {
|
|||||||
UseClassicParameterFlow bool `db:"use_classic_parameter_flow" json:"use_classic_parameter_flow"`
|
UseClassicParameterFlow bool `db:"use_classic_parameter_flow" json:"use_classic_parameter_flow"`
|
||||||
CorsBehavior CorsBehavior `db:"cors_behavior" json:"cors_behavior"`
|
CorsBehavior CorsBehavior `db:"cors_behavior" json:"cors_behavior"`
|
||||||
DisableModuleCache bool `db:"disable_module_cache" json:"disable_module_cache"`
|
DisableModuleCache bool `db:"disable_module_cache" json:"disable_module_cache"`
|
||||||
|
// Longer-form summary used to help agents pick the right template.
|
||||||
|
Abstract string `db:"abstract" json:"abstract"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Records aggregated usage statistics for templates/users. All usage is rounded up to the nearest minute.
|
// Records aggregated usage statistics for templates/users. All usage is rounded up to the nearest minute.
|
||||||
|
|||||||
Generated
+25
-15
@@ -24767,7 +24767,7 @@ func (q *sqlQuerier) GetTemplateAverageBuildTime(ctx context.Context, templateID
|
|||||||
|
|
||||||
const getTemplateByID = `-- name: GetTemplateByID :one
|
const getTemplateByID = `-- name: GetTemplateByID :one
|
||||||
SELECT
|
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, cors_behavior, disable_module_cache, created_by_avatar_url, created_by_username, created_by_name, 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, cors_behavior, disable_module_cache, abstract, created_by_avatar_url, created_by_username, created_by_name, organization_name, organization_display_name, organization_icon
|
||||||
FROM
|
FROM
|
||||||
template_with_names
|
template_with_names
|
||||||
WHERE
|
WHERE
|
||||||
@@ -24811,6 +24811,7 @@ func (q *sqlQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (Templat
|
|||||||
&i.UseClassicParameterFlow,
|
&i.UseClassicParameterFlow,
|
||||||
&i.CorsBehavior,
|
&i.CorsBehavior,
|
||||||
&i.DisableModuleCache,
|
&i.DisableModuleCache,
|
||||||
|
&i.Abstract,
|
||||||
&i.CreatedByAvatarURL,
|
&i.CreatedByAvatarURL,
|
||||||
&i.CreatedByUsername,
|
&i.CreatedByUsername,
|
||||||
&i.CreatedByName,
|
&i.CreatedByName,
|
||||||
@@ -24823,7 +24824,7 @@ func (q *sqlQuerier) GetTemplateByID(ctx context.Context, id uuid.UUID) (Templat
|
|||||||
|
|
||||||
const getTemplateByOrganizationAndName = `-- name: GetTemplateByOrganizationAndName :one
|
const getTemplateByOrganizationAndName = `-- name: GetTemplateByOrganizationAndName :one
|
||||||
SELECT
|
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, cors_behavior, disable_module_cache, created_by_avatar_url, created_by_username, created_by_name, 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, cors_behavior, disable_module_cache, abstract, created_by_avatar_url, created_by_username, created_by_name, organization_name, organization_display_name, organization_icon
|
||||||
FROM
|
FROM
|
||||||
template_with_names AS templates
|
template_with_names AS templates
|
||||||
WHERE
|
WHERE
|
||||||
@@ -24875,6 +24876,7 @@ func (q *sqlQuerier) GetTemplateByOrganizationAndName(ctx context.Context, arg G
|
|||||||
&i.UseClassicParameterFlow,
|
&i.UseClassicParameterFlow,
|
||||||
&i.CorsBehavior,
|
&i.CorsBehavior,
|
||||||
&i.DisableModuleCache,
|
&i.DisableModuleCache,
|
||||||
|
&i.Abstract,
|
||||||
&i.CreatedByAvatarURL,
|
&i.CreatedByAvatarURL,
|
||||||
&i.CreatedByUsername,
|
&i.CreatedByUsername,
|
||||||
&i.CreatedByName,
|
&i.CreatedByName,
|
||||||
@@ -24886,7 +24888,7 @@ func (q *sqlQuerier) GetTemplateByOrganizationAndName(ctx context.Context, arg G
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getTemplates = `-- name: GetTemplates :many
|
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, use_classic_parameter_flow, cors_behavior, disable_module_cache, created_by_avatar_url, created_by_username, created_by_name, 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, cors_behavior, disable_module_cache, abstract, created_by_avatar_url, created_by_username, created_by_name, organization_name, organization_display_name, organization_icon FROM template_with_names AS templates
|
||||||
ORDER BY (name, id) ASC
|
ORDER BY (name, id) ASC
|
||||||
`
|
`
|
||||||
|
|
||||||
@@ -24931,6 +24933,7 @@ func (q *sqlQuerier) GetTemplates(ctx context.Context) ([]Template, error) {
|
|||||||
&i.UseClassicParameterFlow,
|
&i.UseClassicParameterFlow,
|
||||||
&i.CorsBehavior,
|
&i.CorsBehavior,
|
||||||
&i.DisableModuleCache,
|
&i.DisableModuleCache,
|
||||||
|
&i.Abstract,
|
||||||
&i.CreatedByAvatarURL,
|
&i.CreatedByAvatarURL,
|
||||||
&i.CreatedByUsername,
|
&i.CreatedByUsername,
|
||||||
&i.CreatedByName,
|
&i.CreatedByName,
|
||||||
@@ -24953,7 +24956,7 @@ func (q *sqlQuerier) GetTemplates(ctx context.Context) ([]Template, error) {
|
|||||||
|
|
||||||
const getTemplatesWithFilter = `-- name: GetTemplatesWithFilter :many
|
const getTemplatesWithFilter = `-- name: GetTemplatesWithFilter :many
|
||||||
SELECT
|
SELECT
|
||||||
t.id, t.created_at, t.updated_at, t.organization_id, t.deleted, t.name, t.provisioner, t.active_version_id, t.description, t.default_ttl, t.created_by, t.icon, t.user_acl, t.group_acl, t.display_name, t.allow_user_cancel_workspace_jobs, t.allow_user_autostart, t.allow_user_autostop, t.failure_ttl, t.time_til_dormant, t.time_til_dormant_autodelete, t.autostop_requirement_days_of_week, t.autostop_requirement_weeks, t.autostart_block_days_of_week, t.require_active_version, t.deprecated, t.activity_bump, t.max_port_sharing_level, t.use_classic_parameter_flow, t.cors_behavior, t.disable_module_cache, t.created_by_avatar_url, t.created_by_username, t.created_by_name, t.organization_name, t.organization_display_name, t.organization_icon
|
t.id, t.created_at, t.updated_at, t.organization_id, t.deleted, t.name, t.provisioner, t.active_version_id, t.description, t.default_ttl, t.created_by, t.icon, t.user_acl, t.group_acl, t.display_name, t.allow_user_cancel_workspace_jobs, t.allow_user_autostart, t.allow_user_autostop, t.failure_ttl, t.time_til_dormant, t.time_til_dormant_autodelete, t.autostop_requirement_days_of_week, t.autostop_requirement_weeks, t.autostart_block_days_of_week, t.require_active_version, t.deprecated, t.activity_bump, t.max_port_sharing_level, t.use_classic_parameter_flow, t.cors_behavior, t.disable_module_cache, t.abstract, t.created_by_avatar_url, t.created_by_username, t.created_by_name, t.organization_name, t.organization_display_name, t.organization_icon
|
||||||
FROM
|
FROM
|
||||||
template_with_names AS t
|
template_with_names AS t
|
||||||
LEFT JOIN
|
LEFT JOIN
|
||||||
@@ -25113,6 +25116,7 @@ func (q *sqlQuerier) GetTemplatesWithFilter(ctx context.Context, arg GetTemplate
|
|||||||
&i.UseClassicParameterFlow,
|
&i.UseClassicParameterFlow,
|
||||||
&i.CorsBehavior,
|
&i.CorsBehavior,
|
||||||
&i.DisableModuleCache,
|
&i.DisableModuleCache,
|
||||||
|
&i.Abstract,
|
||||||
&i.CreatedByAvatarURL,
|
&i.CreatedByAvatarURL,
|
||||||
&i.CreatedByUsername,
|
&i.CreatedByUsername,
|
||||||
&i.CreatedByName,
|
&i.CreatedByName,
|
||||||
@@ -25144,6 +25148,7 @@ INSERT INTO
|
|||||||
provisioner,
|
provisioner,
|
||||||
active_version_id,
|
active_version_id,
|
||||||
description,
|
description,
|
||||||
|
abstract,
|
||||||
created_by,
|
created_by,
|
||||||
icon,
|
icon,
|
||||||
user_acl,
|
user_acl,
|
||||||
@@ -25155,7 +25160,7 @@ INSERT INTO
|
|||||||
cors_behavior
|
cors_behavior
|
||||||
)
|
)
|
||||||
VALUES
|
VALUES
|
||||||
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)
|
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18)
|
||||||
`
|
`
|
||||||
|
|
||||||
type InsertTemplateParams struct {
|
type InsertTemplateParams struct {
|
||||||
@@ -25167,6 +25172,7 @@ type InsertTemplateParams struct {
|
|||||||
Provisioner ProvisionerType `db:"provisioner" json:"provisioner"`
|
Provisioner ProvisionerType `db:"provisioner" json:"provisioner"`
|
||||||
ActiveVersionID uuid.UUID `db:"active_version_id" json:"active_version_id"`
|
ActiveVersionID uuid.UUID `db:"active_version_id" json:"active_version_id"`
|
||||||
Description string `db:"description" json:"description"`
|
Description string `db:"description" json:"description"`
|
||||||
|
Abstract string `db:"abstract" json:"abstract"`
|
||||||
CreatedBy uuid.UUID `db:"created_by" json:"created_by"`
|
CreatedBy uuid.UUID `db:"created_by" json:"created_by"`
|
||||||
Icon string `db:"icon" json:"icon"`
|
Icon string `db:"icon" json:"icon"`
|
||||||
UserACL TemplateACL `db:"user_acl" json:"user_acl"`
|
UserACL TemplateACL `db:"user_acl" json:"user_acl"`
|
||||||
@@ -25188,6 +25194,7 @@ func (q *sqlQuerier) InsertTemplate(ctx context.Context, arg InsertTemplateParam
|
|||||||
arg.Provisioner,
|
arg.Provisioner,
|
||||||
arg.ActiveVersionID,
|
arg.ActiveVersionID,
|
||||||
arg.Description,
|
arg.Description,
|
||||||
|
arg.Abstract,
|
||||||
arg.CreatedBy,
|
arg.CreatedBy,
|
||||||
arg.Icon,
|
arg.Icon,
|
||||||
arg.UserACL,
|
arg.UserACL,
|
||||||
@@ -25291,15 +25298,16 @@ UPDATE
|
|||||||
SET
|
SET
|
||||||
updated_at = $2,
|
updated_at = $2,
|
||||||
description = $3,
|
description = $3,
|
||||||
name = $4,
|
abstract = $4,
|
||||||
icon = $5,
|
name = $5,
|
||||||
display_name = $6,
|
icon = $6,
|
||||||
allow_user_cancel_workspace_jobs = $7,
|
display_name = $7,
|
||||||
group_acl = $8,
|
allow_user_cancel_workspace_jobs = $8,
|
||||||
max_port_sharing_level = $9,
|
group_acl = $9,
|
||||||
use_classic_parameter_flow = $10,
|
max_port_sharing_level = $10,
|
||||||
cors_behavior = $11,
|
use_classic_parameter_flow = $11,
|
||||||
disable_module_cache = $12
|
cors_behavior = $12,
|
||||||
|
disable_module_cache = $13
|
||||||
WHERE
|
WHERE
|
||||||
id = $1
|
id = $1
|
||||||
`
|
`
|
||||||
@@ -25308,6 +25316,7 @@ type UpdateTemplateMetaByIDParams struct {
|
|||||||
ID uuid.UUID `db:"id" json:"id"`
|
ID uuid.UUID `db:"id" json:"id"`
|
||||||
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
||||||
Description string `db:"description" json:"description"`
|
Description string `db:"description" json:"description"`
|
||||||
|
Abstract string `db:"abstract" json:"abstract"`
|
||||||
Name string `db:"name" json:"name"`
|
Name string `db:"name" json:"name"`
|
||||||
Icon string `db:"icon" json:"icon"`
|
Icon string `db:"icon" json:"icon"`
|
||||||
DisplayName string `db:"display_name" json:"display_name"`
|
DisplayName string `db:"display_name" json:"display_name"`
|
||||||
@@ -25324,6 +25333,7 @@ func (q *sqlQuerier) UpdateTemplateMetaByID(ctx context.Context, arg UpdateTempl
|
|||||||
arg.ID,
|
arg.ID,
|
||||||
arg.UpdatedAt,
|
arg.UpdatedAt,
|
||||||
arg.Description,
|
arg.Description,
|
||||||
|
arg.Abstract,
|
||||||
arg.Name,
|
arg.Name,
|
||||||
arg.Icon,
|
arg.Icon,
|
||||||
arg.DisplayName,
|
arg.DisplayName,
|
||||||
@@ -35319,7 +35329,7 @@ LEFT JOIN LATERAL (
|
|||||||
) latest_build ON TRUE
|
) latest_build ON TRUE
|
||||||
LEFT JOIN LATERAL (
|
LEFT JOIN LATERAL (
|
||||||
SELECT
|
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, cors_behavior, disable_module_cache
|
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, cors_behavior, disable_module_cache, abstract
|
||||||
FROM
|
FROM
|
||||||
templates
|
templates
|
||||||
WHERE
|
WHERE
|
||||||
|
|||||||
@@ -129,6 +129,7 @@ INSERT INTO
|
|||||||
provisioner,
|
provisioner,
|
||||||
active_version_id,
|
active_version_id,
|
||||||
description,
|
description,
|
||||||
|
abstract,
|
||||||
created_by,
|
created_by,
|
||||||
icon,
|
icon,
|
||||||
user_acl,
|
user_acl,
|
||||||
@@ -140,7 +141,7 @@ INSERT INTO
|
|||||||
cors_behavior
|
cors_behavior
|
||||||
)
|
)
|
||||||
VALUES
|
VALUES
|
||||||
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17);
|
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18);
|
||||||
|
|
||||||
-- name: UpdateTemplateActiveVersionByID :exec
|
-- name: UpdateTemplateActiveVersionByID :exec
|
||||||
UPDATE
|
UPDATE
|
||||||
@@ -166,15 +167,16 @@ UPDATE
|
|||||||
SET
|
SET
|
||||||
updated_at = $2,
|
updated_at = $2,
|
||||||
description = $3,
|
description = $3,
|
||||||
name = $4,
|
abstract = $4,
|
||||||
icon = $5,
|
name = $5,
|
||||||
display_name = $6,
|
icon = $6,
|
||||||
allow_user_cancel_workspace_jobs = $7,
|
display_name = $7,
|
||||||
group_acl = $8,
|
allow_user_cancel_workspace_jobs = $8,
|
||||||
max_port_sharing_level = $9,
|
group_acl = $9,
|
||||||
use_classic_parameter_flow = $10,
|
max_port_sharing_level = $10,
|
||||||
cors_behavior = $11,
|
use_classic_parameter_flow = $11,
|
||||||
disable_module_cache = $12
|
cors_behavior = $12,
|
||||||
|
disable_module_cache = $13
|
||||||
WHERE
|
WHERE
|
||||||
id = $1
|
id = $1
|
||||||
;
|
;
|
||||||
|
|||||||
@@ -220,6 +220,7 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
|
|||||||
OrganizationID: organization.ID,
|
OrganizationID: organization.ID,
|
||||||
Name: createTemplate.Name,
|
Name: createTemplate.Name,
|
||||||
Description: createTemplate.Description,
|
Description: createTemplate.Description,
|
||||||
|
Abstract: createTemplate.Abstract,
|
||||||
CreatedBy: apiKey.UserID,
|
CreatedBy: apiKey.UserID,
|
||||||
Icon: createTemplate.Icon,
|
Icon: createTemplate.Icon,
|
||||||
DisplayName: createTemplate.DisplayName,
|
DisplayName: createTemplate.DisplayName,
|
||||||
@@ -429,6 +430,7 @@ func (api *API) postTemplateByOrganization(rw http.ResponseWriter, r *http.Reque
|
|||||||
Provisioner: importJob.Provisioner,
|
Provisioner: importJob.Provisioner,
|
||||||
ActiveVersionID: templateVersion.ID,
|
ActiveVersionID: templateVersion.ID,
|
||||||
Description: createTemplate.Description,
|
Description: createTemplate.Description,
|
||||||
|
Abstract: createTemplate.Abstract,
|
||||||
CreatedBy: apiKey.UserID,
|
CreatedBy: apiKey.UserID,
|
||||||
UserACL: database.TemplateACL{},
|
UserACL: database.TemplateACL{},
|
||||||
GroupACL: defaultsGroups,
|
GroupACL: defaultsGroups,
|
||||||
@@ -764,6 +766,7 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
|
|||||||
Name: resolved.name,
|
Name: resolved.name,
|
||||||
DisplayName: resolved.displayName,
|
DisplayName: resolved.displayName,
|
||||||
Description: resolved.description,
|
Description: resolved.description,
|
||||||
|
Abstract: resolved.abstract,
|
||||||
Icon: resolved.icon,
|
Icon: resolved.icon,
|
||||||
AllowUserCancelWorkspaceJobs: resolved.allowUserCancelWorkspaceJobs,
|
AllowUserCancelWorkspaceJobs: resolved.allowUserCancelWorkspaceJobs,
|
||||||
GroupACL: resolved.groupACL,
|
GroupACL: resolved.groupACL,
|
||||||
@@ -1017,6 +1020,7 @@ func (api *API) convertTemplate(
|
|||||||
ActiveUserCount: owners,
|
ActiveUserCount: owners,
|
||||||
BuildTimeStats: buildTimeStats,
|
BuildTimeStats: buildTimeStats,
|
||||||
Description: template.Description,
|
Description: template.Description,
|
||||||
|
Abstract: template.Abstract,
|
||||||
Icon: template.Icon,
|
Icon: template.Icon,
|
||||||
DefaultTTLMillis: time.Duration(template.DefaultTTL).Milliseconds(),
|
DefaultTTLMillis: time.Duration(template.DefaultTTL).Milliseconds(),
|
||||||
ActivityBumpMillis: time.Duration(template.ActivityBump).Milliseconds(),
|
ActivityBumpMillis: time.Duration(template.ActivityBump).Milliseconds(),
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ type templateMetaUpdate struct {
|
|||||||
name string
|
name string
|
||||||
displayName string
|
displayName string
|
||||||
description string
|
description string
|
||||||
|
abstract string
|
||||||
icon string
|
icon string
|
||||||
defaultTTLMillis int64
|
defaultTTLMillis int64
|
||||||
activityBumpMillis int64
|
activityBumpMillis int64
|
||||||
@@ -70,6 +71,7 @@ func resolveTemplateMetaUpdate(
|
|||||||
name: ptr.NilToDefault(req.Name, template.Name),
|
name: ptr.NilToDefault(req.Name, template.Name),
|
||||||
displayName: ptr.NilToDefault(req.DisplayName, template.DisplayName),
|
displayName: ptr.NilToDefault(req.DisplayName, template.DisplayName),
|
||||||
description: ptr.NilToDefault(req.Description, template.Description),
|
description: ptr.NilToDefault(req.Description, template.Description),
|
||||||
|
abstract: ptr.NilToDefault(req.Abstract, template.Abstract),
|
||||||
icon: ptr.NilToDefault(req.Icon, template.Icon),
|
icon: ptr.NilToDefault(req.Icon, template.Icon),
|
||||||
defaultTTLMillis: ptr.NilToDefault(req.DefaultTTLMillis, time.Duration(template.DefaultTTL).Milliseconds()),
|
defaultTTLMillis: ptr.NilToDefault(req.DefaultTTLMillis, time.Duration(template.DefaultTTL).Milliseconds()),
|
||||||
activityBumpMillis: ptr.NilToDefault(req.ActivityBumpMillis, time.Duration(template.ActivityBump).Milliseconds()),
|
activityBumpMillis: ptr.NilToDefault(req.ActivityBumpMillis, time.Duration(template.ActivityBump).Milliseconds()),
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ func baselineTemplate() database.Template {
|
|||||||
UseClassicParameterFlow: true,
|
UseClassicParameterFlow: true,
|
||||||
CorsBehavior: database.CorsBehaviorPassthru,
|
CorsBehavior: database.CorsBehaviorPassthru,
|
||||||
DisableModuleCache: true,
|
DisableModuleCache: true,
|
||||||
|
Abstract: "Existing abstract.",
|
||||||
GroupACL: database.TemplateACL{
|
GroupACL: database.TemplateACL{
|
||||||
orgID.String(): {"read"},
|
orgID.String(): {"read"},
|
||||||
},
|
},
|
||||||
@@ -70,6 +71,7 @@ func baselineResolved() templateMetaUpdate {
|
|||||||
name: tpl.Name,
|
name: tpl.Name,
|
||||||
displayName: tpl.DisplayName,
|
displayName: tpl.DisplayName,
|
||||||
description: tpl.Description,
|
description: tpl.Description,
|
||||||
|
abstract: tpl.Abstract,
|
||||||
icon: tpl.Icon,
|
icon: tpl.Icon,
|
||||||
defaultTTLMillis: tpl.DefaultTTL / 1e6,
|
defaultTTLMillis: tpl.DefaultTTL / 1e6,
|
||||||
activityBumpMillis: tpl.ActivityBump / 1e6,
|
activityBumpMillis: tpl.ActivityBump / 1e6,
|
||||||
@@ -148,6 +150,20 @@ func TestResolveTemplateMetaUpdate(t *testing.T) {
|
|||||||
r.description = "New description"
|
r.description = "New description"
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Abstract",
|
||||||
|
req: codersdk.UpdateTemplateMeta{Abstract: ptr.Ref("New abstract.")},
|
||||||
|
expected: expected{override: func(r *templateMetaUpdate) {
|
||||||
|
r.abstract = "New abstract."
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AbstractEmptyStringClears",
|
||||||
|
req: codersdk.UpdateTemplateMeta{Abstract: ptr.Ref("")},
|
||||||
|
expected: expected{override: func(r *templateMetaUpdate) {
|
||||||
|
r.abstract = ""
|
||||||
|
}},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Icon",
|
name: "Icon",
|
||||||
req: codersdk.UpdateTemplateMeta{Icon: ptr.Ref("/new.svg")},
|
req: codersdk.UpdateTemplateMeta{Icon: ptr.Ref("/new.svg")},
|
||||||
|
|||||||
+116
-7
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -86,6 +87,50 @@ func TestPostTemplateByOrganization(t *testing.T) {
|
|||||||
assert.Equal(t, database.AuditActionCreate, auditor.AuditLogs()[2].Action)
|
assert.Equal(t, database.AuditActionCreate, auditor.AuditLogs()[2].Action)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("Abstract", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
abstract string
|
||||||
|
wantError bool
|
||||||
|
}{
|
||||||
|
{name: "PersistsValid", abstract: strings.Repeat("a", 2048)},
|
||||||
|
{name: "RejectsOverLimit", abstract: strings.Repeat("a", 2049), wantError: true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||||
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||||
|
|
||||||
|
ctx := testutil.Context(t, testutil.WaitLong)
|
||||||
|
created, err := client.CreateTemplate(ctx, user.OrganizationID, codersdk.CreateTemplateRequest{
|
||||||
|
Name: "abstract-test",
|
||||||
|
VersionID: version.ID,
|
||||||
|
Abstract: tc.abstract,
|
||||||
|
})
|
||||||
|
if tc.wantError {
|
||||||
|
var apiErr *codersdk.Error
|
||||||
|
require.ErrorAs(t, err, &apiErr)
|
||||||
|
require.Equal(t, http.StatusBadRequest, apiErr.StatusCode())
|
||||||
|
require.Len(t, apiErr.Validations, 1)
|
||||||
|
require.Equal(t, "abstract", apiErr.Validations[0].Field)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, tc.abstract, created.Abstract)
|
||||||
|
got, err := client.Template(ctx, created.ID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, tc.abstract, got.Abstract)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("AlreadyExists", func(t *testing.T) {
|
t.Run("AlreadyExists", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
ownerClient := coderdtest.New(t, nil)
|
ownerClient := coderdtest.New(t, nil)
|
||||||
@@ -942,6 +987,50 @@ func TestPatchTemplateMeta(t *testing.T) {
|
|||||||
assert.Equal(t, database.AuditActionWrite, auditor.AuditLogs()[4].Action)
|
assert.Equal(t, database.AuditActionWrite, auditor.AuditLogs()[4].Action)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("Abstract", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
abstract string
|
||||||
|
wantError bool
|
||||||
|
}{
|
||||||
|
{name: "PersistsValid", abstract: strings.Repeat("a", 2048)},
|
||||||
|
{name: "RejectsOverLimit", abstract: strings.Repeat("a", 2049), wantError: true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||||
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
|
||||||
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
|
|
||||||
|
abstract := tc.abstract
|
||||||
|
ctx := testutil.Context(t, testutil.WaitLong)
|
||||||
|
updated, err := client.UpdateTemplateMeta(ctx, template.ID, codersdk.UpdateTemplateMeta{
|
||||||
|
Abstract: &abstract,
|
||||||
|
})
|
||||||
|
if tc.wantError {
|
||||||
|
var apiErr *codersdk.Error
|
||||||
|
require.ErrorAs(t, err, &apiErr)
|
||||||
|
require.Equal(t, http.StatusBadRequest, apiErr.StatusCode())
|
||||||
|
require.Len(t, apiErr.Validations, 1)
|
||||||
|
require.Equal(t, "abstract", apiErr.Validations[0].Field)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, tc.abstract, updated.Abstract)
|
||||||
|
got, err := client.Template(ctx, template.ID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, tc.abstract, got.Abstract)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("AlreadyExists", func(t *testing.T) {
|
t.Run("AlreadyExists", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -1336,6 +1425,7 @@ func TestPatchTemplateMeta(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, template.Name, updated.Name)
|
assert.Equal(t, template.Name, updated.Name)
|
||||||
assert.Equal(t, template.Description, updated.Description)
|
assert.Equal(t, template.Description, updated.Description)
|
||||||
|
assert.Equal(t, template.Abstract, updated.Abstract)
|
||||||
assert.Equal(t, template.Icon, updated.Icon)
|
assert.Equal(t, template.Icon, updated.Icon)
|
||||||
assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis)
|
assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis)
|
||||||
assert.Equal(t, template.ActivityBumpMillis, updated.ActivityBumpMillis)
|
assert.Equal(t, template.ActivityBumpMillis, updated.ActivityBumpMillis)
|
||||||
@@ -1371,6 +1461,7 @@ func TestPatchTemplateMeta(t *testing.T) {
|
|||||||
assert.WithinDuration(t, template.UpdatedAt, updated.UpdatedAt, time.Minute)
|
assert.WithinDuration(t, template.UpdatedAt, updated.UpdatedAt, time.Minute)
|
||||||
assert.Equal(t, template.Name, updated.Name)
|
assert.Equal(t, template.Name, updated.Name)
|
||||||
assert.Equal(t, template.Description, updated.Description)
|
assert.Equal(t, template.Description, updated.Description)
|
||||||
|
assert.Equal(t, template.Abstract, updated.Abstract)
|
||||||
assert.Equal(t, template.Icon, updated.Icon)
|
assert.Equal(t, template.Icon, updated.Icon)
|
||||||
assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis)
|
assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis)
|
||||||
})
|
})
|
||||||
@@ -1665,22 +1756,26 @@ func TestPatchTemplateMeta(t *testing.T) {
|
|||||||
|
|
||||||
displayName := "Test Display Name"
|
displayName := "Test Display Name"
|
||||||
description := "test-description"
|
description := "test-description"
|
||||||
|
abstract := "test abstract for agents"
|
||||||
icon := "/icon/icon.png"
|
icon := "/icon/icon.png"
|
||||||
defaultTTLMillis := 10 * time.Hour.Milliseconds()
|
defaultTTLMillis := 10 * time.Hour.Milliseconds()
|
||||||
|
|
||||||
reference := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
reference := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||||
ctr.DisplayName = displayName
|
ctr.DisplayName = displayName
|
||||||
ctr.Description = description
|
ctr.Description = description
|
||||||
|
ctr.Abstract = abstract
|
||||||
ctr.Icon = icon
|
ctr.Icon = icon
|
||||||
ctr.DefaultTTLMillis = ptr.Ref(defaultTTLMillis)
|
ctr.DefaultTTLMillis = ptr.Ref(defaultTTLMillis)
|
||||||
})
|
})
|
||||||
require.Equal(t, displayName, reference.DisplayName)
|
require.Equal(t, displayName, reference.DisplayName)
|
||||||
require.Equal(t, description, reference.Description)
|
require.Equal(t, description, reference.Description)
|
||||||
|
require.Equal(t, abstract, reference.Abstract)
|
||||||
require.Equal(t, icon, reference.Icon)
|
require.Equal(t, icon, reference.Icon)
|
||||||
|
|
||||||
restoreReq := codersdk.UpdateTemplateMeta{
|
restoreReq := codersdk.UpdateTemplateMeta{
|
||||||
DisplayName: &displayName,
|
DisplayName: &displayName,
|
||||||
Description: &description,
|
Description: &description,
|
||||||
|
Abstract: &abstract,
|
||||||
Icon: &icon,
|
Icon: &icon,
|
||||||
DefaultTTLMillis: ptr.Ref(defaultTTLMillis),
|
DefaultTTLMillis: ptr.Ref(defaultTTLMillis),
|
||||||
}
|
}
|
||||||
@@ -1688,6 +1783,7 @@ func TestPatchTemplateMeta(t *testing.T) {
|
|||||||
type expected struct {
|
type expected struct {
|
||||||
displayName string
|
displayName string
|
||||||
description string
|
description string
|
||||||
|
abstract string
|
||||||
icon string
|
icon string
|
||||||
defaultTTLMillis int64
|
defaultTTLMillis int64
|
||||||
}
|
}
|
||||||
@@ -1702,22 +1798,27 @@ func TestPatchTemplateMeta(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "Only update default_ttl_ms",
|
name: "Only update default_ttl_ms",
|
||||||
req: codersdk.UpdateTemplateMeta{DefaultTTLMillis: ptr.Ref(99 * time.Hour.Milliseconds())},
|
req: codersdk.UpdateTemplateMeta{DefaultTTLMillis: ptr.Ref(99 * time.Hour.Milliseconds())},
|
||||||
expected: expected{displayName: reference.DisplayName, description: reference.Description, icon: reference.Icon, defaultTTLMillis: 99 * time.Hour.Milliseconds()},
|
expected: expected{displayName: reference.DisplayName, description: reference.Description, abstract: reference.Abstract, icon: reference.Icon, defaultTTLMillis: 99 * time.Hour.Milliseconds()},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Clear display name",
|
name: "Clear display name",
|
||||||
req: codersdk.UpdateTemplateMeta{DisplayName: ptr.Ref("")},
|
req: codersdk.UpdateTemplateMeta{DisplayName: ptr.Ref("")},
|
||||||
expected: expected{displayName: "", description: reference.Description, icon: reference.Icon, defaultTTLMillis: defaultTTLMillis},
|
expected: expected{displayName: "", description: reference.Description, abstract: reference.Abstract, icon: reference.Icon, defaultTTLMillis: defaultTTLMillis},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Clear description",
|
name: "Clear description",
|
||||||
req: codersdk.UpdateTemplateMeta{Description: ptr.Ref("")},
|
req: codersdk.UpdateTemplateMeta{Description: ptr.Ref("")},
|
||||||
expected: expected{displayName: reference.DisplayName, description: "", icon: reference.Icon, defaultTTLMillis: defaultTTLMillis},
|
expected: expected{displayName: reference.DisplayName, description: "", abstract: reference.Abstract, icon: reference.Icon, defaultTTLMillis: defaultTTLMillis},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Clear abstract",
|
||||||
|
req: codersdk.UpdateTemplateMeta{Abstract: ptr.Ref("")},
|
||||||
|
expected: expected{displayName: reference.DisplayName, description: reference.Description, abstract: "", icon: reference.Icon, defaultTTLMillis: defaultTTLMillis},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Clear icon",
|
name: "Clear icon",
|
||||||
req: codersdk.UpdateTemplateMeta{Icon: ptr.Ref("")},
|
req: codersdk.UpdateTemplateMeta{Icon: ptr.Ref("")},
|
||||||
expected: expected{displayName: reference.DisplayName, description: reference.Description, icon: "", defaultTTLMillis: defaultTTLMillis},
|
expected: expected{displayName: reference.DisplayName, description: reference.Description, abstract: reference.Abstract, icon: "", defaultTTLMillis: defaultTTLMillis},
|
||||||
},
|
},
|
||||||
// A request whose only field is nil is a true no-op under the new
|
// A request whose only field is nil is a true no-op under the new
|
||||||
// PATCH semantics; the handler returns 304 Not Modified and the
|
// PATCH semantics; the handler returns 304 Not Modified and the
|
||||||
@@ -1725,17 +1826,22 @@ func TestPatchTemplateMeta(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "Nil display name is a no-op",
|
name: "Nil display name is a no-op",
|
||||||
req: codersdk.UpdateTemplateMeta{DisplayName: nil},
|
req: codersdk.UpdateTemplateMeta{DisplayName: nil},
|
||||||
expected: expected{displayName: reference.DisplayName, description: reference.Description, icon: reference.Icon, defaultTTLMillis: defaultTTLMillis},
|
expected: expected{displayName: reference.DisplayName, description: reference.Description, abstract: reference.Abstract, icon: reference.Icon, defaultTTLMillis: defaultTTLMillis},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Nil description is a no-op",
|
name: "Nil description is a no-op",
|
||||||
req: codersdk.UpdateTemplateMeta{Description: nil},
|
req: codersdk.UpdateTemplateMeta{Description: nil},
|
||||||
expected: expected{displayName: reference.DisplayName, description: reference.Description, icon: reference.Icon, defaultTTLMillis: defaultTTLMillis},
|
expected: expected{displayName: reference.DisplayName, description: reference.Description, abstract: reference.Abstract, icon: reference.Icon, defaultTTLMillis: defaultTTLMillis},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Nil abstract is a no-op",
|
||||||
|
req: codersdk.UpdateTemplateMeta{Abstract: nil},
|
||||||
|
expected: expected{displayName: reference.DisplayName, description: reference.Description, abstract: reference.Abstract, icon: reference.Icon, defaultTTLMillis: defaultTTLMillis},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Nil icon is a no-op",
|
name: "Nil icon is a no-op",
|
||||||
req: codersdk.UpdateTemplateMeta{Icon: nil},
|
req: codersdk.UpdateTemplateMeta{Icon: nil},
|
||||||
expected: expected{displayName: reference.DisplayName, description: reference.Description, icon: reference.Icon, defaultTTLMillis: defaultTTLMillis},
|
expected: expected{displayName: reference.DisplayName, description: reference.Description, abstract: reference.Abstract, icon: reference.Icon, defaultTTLMillis: defaultTTLMillis},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1757,6 +1863,7 @@ func TestPatchTemplateMeta(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, tc.expected.displayName, updated.DisplayName)
|
assert.Equal(t, tc.expected.displayName, updated.DisplayName)
|
||||||
assert.Equal(t, tc.expected.description, updated.Description)
|
assert.Equal(t, tc.expected.description, updated.Description)
|
||||||
|
assert.Equal(t, tc.expected.abstract, updated.Abstract)
|
||||||
assert.Equal(t, tc.expected.icon, updated.Icon)
|
assert.Equal(t, tc.expected.icon, updated.Icon)
|
||||||
assert.Equal(t, tc.expected.defaultTTLMillis, updated.DefaultTTLMillis)
|
assert.Equal(t, tc.expected.defaultTTLMillis, updated.DefaultTTLMillis)
|
||||||
})
|
})
|
||||||
@@ -1774,6 +1881,7 @@ func TestPatchTemplateMeta(t *testing.T) {
|
|||||||
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||||
ctr.DisplayName = "Original Display"
|
ctr.DisplayName = "Original Display"
|
||||||
ctr.Description = "Original description"
|
ctr.Description = "Original description"
|
||||||
|
ctr.Abstract = "Original abstract"
|
||||||
ctr.Icon = "/icon/original.png"
|
ctr.Icon = "/icon/original.png"
|
||||||
ctr.DefaultTTLMillis = ptr.Ref((24 * time.Hour).Milliseconds())
|
ctr.DefaultTTLMillis = ptr.Ref((24 * time.Hour).Milliseconds())
|
||||||
ctr.AllowUserCancelWorkspaceJobs = ptr.Ref(true)
|
ctr.AllowUserCancelWorkspaceJobs = ptr.Ref(true)
|
||||||
@@ -1789,6 +1897,7 @@ func TestPatchTemplateMeta(t *testing.T) {
|
|||||||
assert.Equal(t, template.Name, updated.Name)
|
assert.Equal(t, template.Name, updated.Name)
|
||||||
assert.Equal(t, template.DisplayName, updated.DisplayName)
|
assert.Equal(t, template.DisplayName, updated.DisplayName)
|
||||||
assert.Equal(t, template.Description, updated.Description)
|
assert.Equal(t, template.Description, updated.Description)
|
||||||
|
assert.Equal(t, template.Abstract, updated.Abstract)
|
||||||
assert.Equal(t, template.Icon, updated.Icon)
|
assert.Equal(t, template.Icon, updated.Icon)
|
||||||
assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis)
|
assert.Equal(t, template.DefaultTTLMillis, updated.DefaultTTLMillis)
|
||||||
assert.Equal(t, template.AllowUserCancelWorkspaceJobs, updated.AllowUserCancelWorkspaceJobs)
|
assert.Equal(t, template.AllowUserCancelWorkspaceJobs, updated.AllowUserCancelWorkspaceJobs)
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ func ListTemplates(db database.Store, organizationID uuid.UUID, options ListTemp
|
|||||||
"list_templates",
|
"list_templates",
|
||||||
"List available workspace templates. Optionally filter by a "+
|
"List available workspace templates. Optionally filter by a "+
|
||||||
"search query matching template name or description. "+
|
"search query matching template name or description. "+
|
||||||
"Use this to find a template before creating a workspace. "+
|
"Results include short UI descriptions and agent-facing abstracts when present. "+
|
||||||
"Results are ordered by number of active developers (most popular first). "+
|
"Results are ordered by number of active developers (most popular first). "+
|
||||||
"Returns 10 per page. Use the page parameter to paginate through results.",
|
"Returns 10 per page. Use the page parameter to paginate through results.",
|
||||||
func(ctx context.Context, args listTemplatesArgs, _ fantasy.ToolCall) (fantasy.ToolResponse, error) {
|
func(ctx context.Context, args listTemplatesArgs, _ fantasy.ToolCall) (fantasy.ToolResponse, error) {
|
||||||
@@ -128,6 +128,9 @@ func ListTemplates(db database.Store, organizationID uuid.UUID, options ListTemp
|
|||||||
if desc := strings.TrimSpace(t.Description); desc != "" {
|
if desc := strings.TrimSpace(t.Description); desc != "" {
|
||||||
item["description"] = truncateRunes(desc, 200)
|
item["description"] = truncateRunes(desc, 200)
|
||||||
}
|
}
|
||||||
|
if abstract := strings.TrimSpace(t.Abstract); abstract != "" {
|
||||||
|
item["abstract"] = abstract
|
||||||
|
}
|
||||||
if count, ok := ownerCounts[t.ID]; ok && count > 0 {
|
if count, ok := ownerCounts[t.ID]; ok && count > 0 {
|
||||||
item["active_developers"] = count
|
item["active_developers"] = count
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package chattool_test
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"charm.land/fantasy"
|
"charm.land/fantasy"
|
||||||
@@ -121,6 +122,120 @@ func TestListTemplates_OrganizationFilter(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestListTemplates_Abstract(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
template database.Template
|
||||||
|
want string
|
||||||
|
wantPresent bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "LongAbstract",
|
||||||
|
template: database.Template{
|
||||||
|
Name: "with-abstract",
|
||||||
|
Description: "short description",
|
||||||
|
Abstract: strings.Repeat("a", 1000),
|
||||||
|
},
|
||||||
|
want: strings.Repeat("a", 1000),
|
||||||
|
wantPresent: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ShortAbstractUntouched",
|
||||||
|
template: database.Template{
|
||||||
|
Name: "short-abstract",
|
||||||
|
Description: "short description",
|
||||||
|
Abstract: "a concise abstract",
|
||||||
|
},
|
||||||
|
want: "a concise abstract",
|
||||||
|
wantPresent: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "EmptyAbstractOmitted",
|
||||||
|
template: database.Template{
|
||||||
|
Name: "no-abstract",
|
||||||
|
Description: "short description",
|
||||||
|
Abstract: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "WhitespaceOnlyAbstractOmitted",
|
||||||
|
template: database.Template{
|
||||||
|
Name: "whitespace-abstract",
|
||||||
|
Description: "short description",
|
||||||
|
Abstract: " \t \n ",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
fixture := newListTemplatesFixture(t)
|
||||||
|
template := tc.template
|
||||||
|
template.OrganizationID = fixture.org.ID
|
||||||
|
template.CreatedBy = fixture.user.ID
|
||||||
|
tpl := dbgen.Template(t, fixture.db, template)
|
||||||
|
|
||||||
|
items := fixture.listTemplates(t, "{}")
|
||||||
|
require.Len(t, items, 1)
|
||||||
|
require.Equal(t, tpl.ID.String(), items[0]["id"].(string))
|
||||||
|
require.Equal(t, "short description", items[0]["description"].(string))
|
||||||
|
|
||||||
|
got, ok := items[0]["abstract"]
|
||||||
|
require.Equal(t, tc.wantPresent, ok)
|
||||||
|
if tc.wantPresent {
|
||||||
|
require.Equal(t, tc.want, got.(string))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type listTemplatesFixture struct {
|
||||||
|
db database.Store
|
||||||
|
user database.User
|
||||||
|
org database.Organization
|
||||||
|
}
|
||||||
|
|
||||||
|
func newListTemplatesFixture(t *testing.T) listTemplatesFixture {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
db, _ := dbtestutil.NewDB(t)
|
||||||
|
user := dbgen.User(t, db, database.User{})
|
||||||
|
org := dbgen.Organization(t, db, database.Organization{})
|
||||||
|
_ = dbgen.OrganizationMember(t, db, database.OrganizationMember{
|
||||||
|
UserID: user.ID,
|
||||||
|
OrganizationID: org.ID,
|
||||||
|
})
|
||||||
|
return listTemplatesFixture{db: db, user: user, org: org}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f listTemplatesFixture) listTemplates(t *testing.T, input string) []map[string]any {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
tool := chattool.ListTemplates(f.db, f.org.ID, chattool.ListTemplatesOptions{
|
||||||
|
OwnerID: f.user.ID,
|
||||||
|
})
|
||||||
|
resp, err := tool.Run(testutil.Context(t, testutil.WaitShort), fantasy.ToolCall{
|
||||||
|
ID: "list-templates",
|
||||||
|
Name: "list_templates",
|
||||||
|
Input: input,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, resp.IsError)
|
||||||
|
|
||||||
|
var result map[string]any
|
||||||
|
require.NoError(t, json.Unmarshal([]byte(resp.Content), &result))
|
||||||
|
templates := result["templates"].([]any)
|
||||||
|
items := make([]map[string]any, 0, len(templates))
|
||||||
|
for _, template := range templates {
|
||||||
|
items = append(items, template.(map[string]any))
|
||||||
|
}
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
//nolint:tparallel,paralleltest // Subtests share a single DB and run sequentially.
|
//nolint:tparallel,paralleltest // Subtests share a single DB and run sequentially.
|
||||||
func TestTemplateAllowlistEnforcement(t *testing.T) {
|
func TestTemplateAllowlistEnforcement(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|||||||
@@ -88,6 +88,9 @@ func ReadTemplate(db database.Store, organizationID uuid.UUID, options ReadTempl
|
|||||||
if desc := strings.TrimSpace(template.Description); desc != "" {
|
if desc := strings.TrimSpace(template.Description); desc != "" {
|
||||||
templateInfo["description"] = desc
|
templateInfo["description"] = desc
|
||||||
}
|
}
|
||||||
|
if abstract := strings.TrimSpace(template.Abstract); abstract != "" {
|
||||||
|
templateInfo["abstract"] = abstract
|
||||||
|
}
|
||||||
|
|
||||||
paramList := make([]map[string]any, 0, len(params))
|
paramList := make([]map[string]any, 0, len(params))
|
||||||
for _, p := range params {
|
for _, p := range params {
|
||||||
|
|||||||
@@ -181,3 +181,74 @@ func TestReadTemplate_NoPresets(t *testing.T) {
|
|||||||
_, hasPresets := result["presets"]
|
_, hasPresets := result["presets"]
|
||||||
require.False(t, hasPresets, "presets key should be absent when there are none")
|
require.False(t, hasPresets, "presets key should be absent when there are none")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestReadTemplate_Abstract(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
abstract string
|
||||||
|
want string
|
||||||
|
wantPresent bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Present",
|
||||||
|
abstract: "A long-form summary used by agents to pick the right template.",
|
||||||
|
want: "A long-form summary used by agents to pick the right template.",
|
||||||
|
wantPresent: true,
|
||||||
|
},
|
||||||
|
{name: "Empty"},
|
||||||
|
{name: "WhitespaceOnly", abstract: " \t \n "},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tmplInfo := readTemplateInfo(t, tc.abstract)
|
||||||
|
got, ok := tmplInfo["abstract"]
|
||||||
|
require.Equal(t, tc.wantPresent, ok)
|
||||||
|
if tc.wantPresent {
|
||||||
|
require.Equal(t, tc.want, got.(string))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readTemplateInfo(t *testing.T, abstract string) map[string]any {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
db, _ := dbtestutil.NewDB(t)
|
||||||
|
user := dbgen.User(t, db, database.User{})
|
||||||
|
org := dbgen.Organization(t, db, database.Organization{})
|
||||||
|
_ = dbgen.OrganizationMember(t, db, database.OrganizationMember{
|
||||||
|
UserID: user.ID,
|
||||||
|
OrganizationID: org.ID,
|
||||||
|
})
|
||||||
|
|
||||||
|
tv := dbgen.TemplateVersion(t, db, database.TemplateVersion{
|
||||||
|
OrganizationID: org.ID,
|
||||||
|
CreatedBy: user.ID,
|
||||||
|
})
|
||||||
|
tmpl := dbgen.Template(t, db, database.Template{
|
||||||
|
OrganizationID: org.ID,
|
||||||
|
CreatedBy: user.ID,
|
||||||
|
ActiveVersionID: tv.ID,
|
||||||
|
Abstract: abstract,
|
||||||
|
})
|
||||||
|
|
||||||
|
tool := chattool.ReadTemplate(db, org.ID, chattool.ReadTemplateOptions{
|
||||||
|
OwnerID: user.ID,
|
||||||
|
})
|
||||||
|
resp, err := tool.Run(testutil.Context(t, testutil.WaitShort), fantasy.ToolCall{
|
||||||
|
ID: "abstract",
|
||||||
|
Name: "read_template",
|
||||||
|
Input: `{"template_id":"` + tmpl.ID.String() + `"}`,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, resp.IsError)
|
||||||
|
|
||||||
|
var result map[string]any
|
||||||
|
require.NoError(t, json.Unmarshal([]byte(resp.Content), &result))
|
||||||
|
return result["template"].(map[string]any)
|
||||||
|
}
|
||||||
|
|||||||
@@ -144,6 +144,9 @@ type CreateTemplateRequest struct {
|
|||||||
// Description is a description of what the template contains. It must be
|
// Description is a description of what the template contains. It must be
|
||||||
// less than 128 bytes.
|
// less than 128 bytes.
|
||||||
Description string `json:"description,omitempty" validate:"lt=128"`
|
Description string `json:"description,omitempty" validate:"lt=128"`
|
||||||
|
// Abstract is a longer-form summary surfaced to agents to help them pick
|
||||||
|
// the right template. Up to 2048 characters.
|
||||||
|
Abstract string `json:"abstract,omitempty" validate:"max=2048"`
|
||||||
// Icon is a relative path or external URL that specifies
|
// Icon is a relative path or external URL that specifies
|
||||||
// an icon to be displayed in the dashboard.
|
// an icon to be displayed in the dashboard.
|
||||||
Icon string `json:"icon,omitempty"`
|
Icon string `json:"icon,omitempty"`
|
||||||
|
|||||||
+18
-12
@@ -27,15 +27,18 @@ type Template struct {
|
|||||||
Provisioner ProvisionerType `json:"provisioner" enums:"terraform"`
|
Provisioner ProvisionerType `json:"provisioner" enums:"terraform"`
|
||||||
ActiveVersionID uuid.UUID `json:"active_version_id" format:"uuid"`
|
ActiveVersionID uuid.UUID `json:"active_version_id" format:"uuid"`
|
||||||
// ActiveUserCount is set to -1 when loading.
|
// ActiveUserCount is set to -1 when loading.
|
||||||
ActiveUserCount int `json:"active_user_count"`
|
ActiveUserCount int `json:"active_user_count"`
|
||||||
BuildTimeStats TemplateBuildTimeStats `json:"build_time_stats"`
|
BuildTimeStats TemplateBuildTimeStats `json:"build_time_stats"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Deprecated bool `json:"deprecated"`
|
// Abstract is a longer-form summary surfaced to agents to help them pick
|
||||||
DeprecationMessage string `json:"deprecation_message"`
|
// the right template. Up to 2048 characters.
|
||||||
Deleted bool `json:"deleted"`
|
Abstract string `json:"abstract"`
|
||||||
Icon string `json:"icon"`
|
Deprecated bool `json:"deprecated"`
|
||||||
DefaultTTLMillis int64 `json:"default_ttl_ms"`
|
DeprecationMessage string `json:"deprecation_message"`
|
||||||
ActivityBumpMillis int64 `json:"activity_bump_ms"`
|
Deleted bool `json:"deleted"`
|
||||||
|
Icon string `json:"icon"`
|
||||||
|
DefaultTTLMillis int64 `json:"default_ttl_ms"`
|
||||||
|
ActivityBumpMillis int64 `json:"activity_bump_ms"`
|
||||||
// AutostopRequirement and AutostartRequirement are enterprise features. Its
|
// AutostopRequirement and AutostartRequirement are enterprise features. Its
|
||||||
// value is only used if your license is entitled to use the advanced template
|
// value is only used if your license is entitled to use the advanced template
|
||||||
// scheduling feature.
|
// scheduling feature.
|
||||||
@@ -218,9 +221,12 @@ type ACLAvailable struct {
|
|||||||
// UpdateTemplateMeta is the request body for the PATCH /templates/{template}
|
// UpdateTemplateMeta is the request body for the PATCH /templates/{template}
|
||||||
// endpoint. All fields are optional. Fields that are nil are not modified.
|
// endpoint. All fields are optional. Fields that are nil are not modified.
|
||||||
type UpdateTemplateMeta struct {
|
type UpdateTemplateMeta struct {
|
||||||
Name *string `json:"name,omitempty" validate:"omitempty,template_name"`
|
Name *string `json:"name,omitempty" validate:"omitempty,template_name"`
|
||||||
DisplayName *string `json:"display_name,omitempty" validate:"omitempty,template_display_name"`
|
DisplayName *string `json:"display_name,omitempty" validate:"omitempty,template_display_name"`
|
||||||
Description *string `json:"description,omitempty"`
|
Description *string `json:"description,omitempty"`
|
||||||
|
// Abstract is a longer-form summary surfaced to agents to help them pick
|
||||||
|
// the right template. Up to 2048 characters.
|
||||||
|
Abstract *string `json:"abstract,omitempty" validate:"omitempty,max=2048"`
|
||||||
Icon *string `json:"icon,omitempty"`
|
Icon *string `json:"icon,omitempty"`
|
||||||
DefaultTTLMillis *int64 `json:"default_ttl_ms,omitempty"`
|
DefaultTTLMillis *int64 `json:"default_ttl_ms,omitempty"`
|
||||||
// ActivityBumpMillis allows optionally specifying the activity bump
|
// ActivityBumpMillis allows optionally specifying the activity bump
|
||||||
|
|||||||
@@ -57,10 +57,17 @@ func searchTemplates(ctx context.Context, deps Deps, query string) ([]SearchResu
|
|||||||
}
|
}
|
||||||
results := make([]SearchResultItem, len(templates))
|
results := make([]SearchResultItem, len(templates))
|
||||||
for i, template := range templates {
|
for i, template := range templates {
|
||||||
|
parts := make([]string, 0, 2)
|
||||||
|
if d := strings.TrimSpace(template.Description); d != "" {
|
||||||
|
parts = append(parts, d)
|
||||||
|
}
|
||||||
|
if a := strings.TrimSpace(template.Abstract); a != "" {
|
||||||
|
parts = append(parts, a)
|
||||||
|
}
|
||||||
results[i] = SearchResultItem{
|
results[i] = SearchResultItem{
|
||||||
ID: createObjectID(ObjectTypeTemplate, template.ID.String()).String(),
|
ID: createObjectID(ObjectTypeTemplate, template.ID.String()).String(),
|
||||||
Title: template.DisplayName,
|
Title: template.DisplayName,
|
||||||
Text: template.Description,
|
Text: strings.Join(parts, "\n\n"),
|
||||||
URL: fmt.Sprintf("%s/templates/%s/%s", serverURL, template.OrganizationName, template.Name),
|
URL: fmt.Sprintf("%s/templates/%s/%s", serverURL, template.OrganizationName, template.Name),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -151,6 +151,51 @@ func TestChatGPTSearch_TemplateMultipleFilters(t *testing.T) {
|
|||||||
require.Equal(t, expectedID, result.Results[0].ID, "Should match the docker template in org2")
|
require.Equal(t, expectedID, result.Results[0].ID, "Should match the docker template in org2")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestChatGPTSearch_TemplateIncludesAbstract(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
client, store := coderdtest.NewWithDatabase(t, nil)
|
||||||
|
owner := coderdtest.CreateFirstUser(t, client)
|
||||||
|
|
||||||
|
const (
|
||||||
|
desc = "Short description."
|
||||||
|
abstract = "Detailed summary that distinguishes this template."
|
||||||
|
)
|
||||||
|
|
||||||
|
dbgen.Template(t, store, database.Template{
|
||||||
|
OrganizationID: owner.OrganizationID,
|
||||||
|
CreatedBy: owner.UserID,
|
||||||
|
Name: "with-abstract",
|
||||||
|
DisplayName: "With Abstract",
|
||||||
|
Description: desc,
|
||||||
|
Abstract: abstract,
|
||||||
|
})
|
||||||
|
dbgen.Template(t, store, database.Template{
|
||||||
|
OrganizationID: owner.OrganizationID,
|
||||||
|
CreatedBy: owner.UserID,
|
||||||
|
Name: "no-abstract",
|
||||||
|
DisplayName: "No Abstract",
|
||||||
|
Description: desc,
|
||||||
|
})
|
||||||
|
|
||||||
|
deps, err := toolsdk.NewDeps(client)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
result, err := testTool(t, toolsdk.ChatGPTSearch, deps, toolsdk.SearchArgs{Query: "templates"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, result.Results, 2)
|
||||||
|
|
||||||
|
byTitle := make(map[string]toolsdk.SearchResultItem, len(result.Results))
|
||||||
|
for _, item := range result.Results {
|
||||||
|
byTitle[item.Title] = item
|
||||||
|
}
|
||||||
|
require.Contains(t, byTitle, "With Abstract")
|
||||||
|
require.Contains(t, byTitle, "No Abstract")
|
||||||
|
require.Contains(t, byTitle["With Abstract"].Text, desc)
|
||||||
|
require.Contains(t, byTitle["With Abstract"].Text, abstract)
|
||||||
|
require.Equal(t, desc, byTitle["No Abstract"].Text)
|
||||||
|
}
|
||||||
|
|
||||||
func TestChatGPTSearch_WorkspaceSearch(t *testing.T) {
|
func TestChatGPTSearch_WorkspaceSearch(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
@@ -621,7 +621,7 @@ var ListWorkspaces = Tool[ListWorkspacesArgs, []MinimalWorkspace]{
|
|||||||
var ListTemplates = Tool[NoArgs, []MinimalTemplate]{
|
var ListTemplates = Tool[NoArgs, []MinimalTemplate]{
|
||||||
Tool: aisdk.Tool{
|
Tool: aisdk.Tool{
|
||||||
Name: ToolNameListTemplates,
|
Name: ToolNameListTemplates,
|
||||||
Description: "Lists templates for the authenticated user.",
|
Description: "Lists templates for the authenticated user. Description is short UI text; abstract is richer selection context.",
|
||||||
Schema: aisdk.Schema{
|
Schema: aisdk.Schema{
|
||||||
Properties: map[string]any{},
|
Properties: map[string]any{},
|
||||||
Required: []string{},
|
Required: []string{},
|
||||||
@@ -640,6 +640,7 @@ var ListTemplates = Tool[NoArgs, []MinimalTemplate]{
|
|||||||
ID: template.ID.String(),
|
ID: template.ID.String(),
|
||||||
Name: template.Name,
|
Name: template.Name,
|
||||||
Description: template.Description,
|
Description: template.Description,
|
||||||
|
Abstract: template.Abstract,
|
||||||
ActiveVersionID: template.ActiveVersionID,
|
ActiveVersionID: template.ActiveVersionID,
|
||||||
ActiveUserCount: template.ActiveUserCount,
|
ActiveUserCount: template.ActiveUserCount,
|
||||||
}
|
}
|
||||||
@@ -732,7 +733,7 @@ func toPresetView(p codersdk.Preset) presetView {
|
|||||||
var GetTemplate = Tool[GetTemplateArgs, TemplateDetail]{
|
var GetTemplate = Tool[GetTemplateArgs, TemplateDetail]{
|
||||||
Tool: aisdk.Tool{
|
Tool: aisdk.Tool{
|
||||||
Name: ToolNameGetTemplate,
|
Name: ToolNameGetTemplate,
|
||||||
Description: `Get details about a workspace template, including its configurable parameters and available presets for the active version.
|
Description: `Get details about a workspace template, including its abstract, configurable parameters, and available presets for the active version.
|
||||||
|
|
||||||
Use this after finding a template with coder_list_templates and before creating a workspace with coder_create_workspace. Presets, when present, can be passed to coder_create_workspace as template_version_preset_id.
|
Use this after finding a template with coder_list_templates and before creating a workspace with coder_create_workspace. Presets, when present, can be passed to coder_create_workspace as template_version_preset_id.
|
||||||
|
|
||||||
@@ -777,6 +778,7 @@ When selecting a preset: if a preset is marked default and the user has not spec
|
|||||||
ID: template.ID.String(),
|
ID: template.ID.String(),
|
||||||
Name: template.Name,
|
Name: template.Name,
|
||||||
Description: template.Description,
|
Description: template.Description,
|
||||||
|
Abstract: template.Abstract,
|
||||||
ActiveVersionID: template.ActiveVersionID,
|
ActiveVersionID: template.ActiveVersionID,
|
||||||
ActiveUserCount: template.ActiveUserCount,
|
ActiveUserCount: template.ActiveUserCount,
|
||||||
},
|
},
|
||||||
@@ -1719,6 +1721,7 @@ type MinimalTemplate struct {
|
|||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
|
Abstract string `json:"abstract,omitempty"`
|
||||||
ActiveVersionID uuid.UUID `json:"active_version_id"`
|
ActiveVersionID uuid.UUID `json:"active_version_id"`
|
||||||
ActiveUserCount int `json:"active_user_count"`
|
ActiveUserCount int `json:"active_user_count"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -309,6 +310,7 @@ func TestTools(t *testing.T) {
|
|||||||
})
|
})
|
||||||
for i, template := range result {
|
for i, template := range result {
|
||||||
require.Equal(t, expected[i].ID.String(), template.ID)
|
require.Equal(t, expected[i].ID.String(), template.ID)
|
||||||
|
require.Equal(t, expected[i].Abstract, template.Abstract)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -738,6 +740,27 @@ func TestTools(t *testing.T) {
|
|||||||
})
|
})
|
||||||
require.ErrorContains(t, err, "get template")
|
require.ErrorContains(t, err, "get template")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("Abstract", func(t *testing.T) {
|
||||||
|
ctx := testutil.Context(t, testutil.WaitShort)
|
||||||
|
absBuild := dbfake.WorkspaceBuild(t, store, database.WorkspaceTable{
|
||||||
|
OrganizationID: owner.OrganizationID,
|
||||||
|
OwnerID: member.ID,
|
||||||
|
}).Do()
|
||||||
|
abstract := strings.Repeat("b", 600)
|
||||||
|
_, err := client.UpdateTemplateMeta(ctx, absBuild.Template.ID, codersdk.UpdateTemplateMeta{
|
||||||
|
Abstract: &abstract,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
tb, err := toolsdk.NewDeps(memberClient)
|
||||||
|
require.NoError(t, err)
|
||||||
|
result, err := testTool(t, toolsdk.GetTemplate, tb, toolsdk.GetTemplateArgs{
|
||||||
|
TemplateID: absBuild.Template.ID.String(),
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, abstract, result.Abstract)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("GetWorkspaceAgentLogs", func(t *testing.T) {
|
t.Run("GetWorkspaceAgentLogs", func(t *testing.T) {
|
||||||
|
|||||||
@@ -13,40 +13,40 @@ We track the following resources:
|
|||||||
|
|
||||||
<!-- Code generated by 'make docs/admin/security/audit-logs.md'. DO NOT EDIT -->
|
<!-- Code generated by 'make docs/admin/security/audit-logs.md'. DO NOT EDIT -->
|
||||||
|
|
||||||
| <b>Resource<b> | | |
|
| <b>Resource<b> | | |
|
||||||
|-----------------------------------------------------------------|----------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|-----------------------------------------------------------------|----------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| AIGatewayKey<br><i>create, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>created_at</td><td>false</td></tr><tr><td>hashed_secret</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>last_used_at</td><td>false</td></tr><tr><td>name</td><td>true</td></tr><tr><td>secret_prefix</td><td>true</td></tr></tbody></table> |
|
| AIGatewayKey<br><i>create, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>created_at</td><td>false</td></tr><tr><td>hashed_secret</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>last_used_at</td><td>false</td></tr><tr><td>name</td><td>true</td></tr><tr><td>secret_prefix</td><td>true</td></tr></tbody></table> |
|
||||||
| AIProvider<br><i>create, write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>base_url</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>deleted</td><td>true</td></tr><tr><td>display_name</td><td>true</td></tr><tr><td>enabled</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>settings</td><td>true</td></tr><tr><td>settings_key_id</td><td>false</td></tr><tr><td>type</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr></tbody></table> |
|
| AIProvider<br><i>create, write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>base_url</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>deleted</td><td>true</td></tr><tr><td>display_name</td><td>true</td></tr><tr><td>enabled</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>settings</td><td>true</td></tr><tr><td>settings_key_id</td><td>false</td></tr><tr><td>type</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr></tbody></table> |
|
||||||
| AIProviderKey<br><i>create, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>api_key</td><td>true</td></tr><tr><td>api_key_key_id</td><td>false</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>id</td><td>true</td></tr><tr><td>provider_id</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr></tbody></table> |
|
| AIProviderKey<br><i>create, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>api_key</td><td>true</td></tr><tr><td>api_key_key_id</td><td>false</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>id</td><td>true</td></tr><tr><td>provider_id</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr></tbody></table> |
|
||||||
| APIKey<br><i>login, logout, register, create, write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>allow_list</td><td>false</td></tr><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>scopes</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> |
|
| APIKey<br><i>login, logout, register, create, write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>allow_list</td><td>false</td></tr><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>scopes</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> |
|
||||||
| AiSeatState<br><i>create</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>first_used_at</td><td>true</td></tr><tr><td>last_event_description</td><td>true</td></tr><tr><td>last_event_type</td><td>true</td></tr><tr><td>last_used_at</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> |
|
| AiSeatState<br><i>create</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>first_used_at</td><td>true</td></tr><tr><td>last_event_description</td><td>true</td></tr><tr><td>last_event_type</td><td>true</td></tr><tr><td>last_used_at</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> |
|
| 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>chat_spend_limit_micros</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> |
|
| 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>chat_spend_limit_micros</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> |
|
||||||
| AuditableGroupAiBudget<br><i>write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>created_at</td><td>false</td></tr><tr><td>group_id</td><td>false</td></tr><tr><td>group_name</td><td>false</td></tr><tr><td>spend_limit</td><td>true</td></tr><tr><td>spend_limit_micros</td><td>false</td></tr><tr><td>updated_at</td><td>false</td></tr></tbody></table> |
|
| AuditableGroupAiBudget<br><i>write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>created_at</td><td>false</td></tr><tr><td>group_id</td><td>false</td></tr><tr><td>group_name</td><td>false</td></tr><tr><td>spend_limit</td><td>true</td></tr><tr><td>spend_limit_micros</td><td>false</td></tr><tr><td>updated_at</td><td>false</td></tr></tbody></table> |
|
||||||
| AuditableOrganizationMember<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>organization_id</td><td>false</td></tr><tr><td>roles</td><td>true</td></tr><tr><td>updated_at</td><td>true</td></tr><tr><td>user_id</td><td>true</td></tr><tr><td>username</td><td>true</td></tr></tbody></table> |
|
| AuditableOrganizationMember<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>organization_id</td><td>false</td></tr><tr><td>roles</td><td>true</td></tr><tr><td>updated_at</td><td>true</td></tr><tr><td>user_id</td><td>true</td></tr><tr><td>username</td><td>true</td></tr></tbody></table> |
|
||||||
| Chat<br><i>create, write</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>agent_id</td><td>false</td></tr><tr><td>archived</td><td>true</td></tr><tr><td>build_id</td><td>false</td></tr><tr><td>client_type</td><td>false</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>dynamic_tools</td><td>false</td></tr><tr><td>group_acl</td><td>true</td></tr><tr><td>heartbeat_at</td><td>false</td></tr><tr><td>id</td><td>true</td></tr><tr><td>labels</td><td>true</td></tr><tr><td>last_error</td><td>false</td></tr><tr><td>last_injected_context</td><td>false</td></tr><tr><td>last_model_config_id</td><td>false</td></tr><tr><td>last_read_message_id</td><td>false</td></tr><tr><td>last_turn_summary</td><td>false</td></tr><tr><td>mcp_server_ids</td><td>true</td></tr><tr><td>mode</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>owner_id</td><td>true</td></tr><tr><td>owner_name</td><td>false</td></tr><tr><td>owner_username</td><td>false</td></tr><tr><td>parent_chat_id</td><td>false</td></tr><tr><td>pin_order</td><td>true</td></tr><tr><td>plan_mode</td><td>false</td></tr><tr><td>root_chat_id</td><td>false</td></tr><tr><td>started_at</td><td>false</td></tr><tr><td>status</td><td>false</td></tr><tr><td>title</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_acl</td><td>true</td></tr><tr><td>worker_id</td><td>false</td></tr><tr><td>workspace_id</td><td>true</td></tr></tbody></table> |
|
| Chat<br><i>create, write</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>agent_id</td><td>false</td></tr><tr><td>archived</td><td>true</td></tr><tr><td>build_id</td><td>false</td></tr><tr><td>client_type</td><td>false</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>dynamic_tools</td><td>false</td></tr><tr><td>group_acl</td><td>true</td></tr><tr><td>heartbeat_at</td><td>false</td></tr><tr><td>id</td><td>true</td></tr><tr><td>labels</td><td>true</td></tr><tr><td>last_error</td><td>false</td></tr><tr><td>last_injected_context</td><td>false</td></tr><tr><td>last_model_config_id</td><td>false</td></tr><tr><td>last_read_message_id</td><td>false</td></tr><tr><td>last_turn_summary</td><td>false</td></tr><tr><td>mcp_server_ids</td><td>true</td></tr><tr><td>mode</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>owner_id</td><td>true</td></tr><tr><td>owner_name</td><td>false</td></tr><tr><td>owner_username</td><td>false</td></tr><tr><td>parent_chat_id</td><td>false</td></tr><tr><td>pin_order</td><td>true</td></tr><tr><td>plan_mode</td><td>false</td></tr><tr><td>root_chat_id</td><td>false</td></tr><tr><td>started_at</td><td>false</td></tr><tr><td>status</td><td>false</td></tr><tr><td>title</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_acl</td><td>true</td></tr><tr><td>worker_id</td><td>false</td></tr><tr><td>workspace_id</td><td>true</td></tr></tbody></table> |
|
||||||
| CustomRole<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>display_name</td><td>true</td></tr><tr><td>id</td><td>false</td></tr><tr><td>is_system</td><td>false</td></tr><tr><td>member_permissions</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>org_permissions</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>site_permissions</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_permissions</td><td>true</td></tr></tbody></table> |
|
| CustomRole<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>display_name</td><td>true</td></tr><tr><td>id</td><td>false</td></tr><tr><td>is_system</td><td>false</td></tr><tr><td>member_permissions</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>org_permissions</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>site_permissions</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_permissions</td><td>true</td></tr></tbody></table> |
|
||||||
| GitSSHKey<br><i>create</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>created_at</td><td>false</td></tr><tr><td>private_key</td><td>true</td></tr><tr><td>private_key_key_id</td><td>false</td></tr><tr><td>public_key</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_id</td><td>true</td></tr></tbody></table> |
|
| GitSSHKey<br><i>create</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>created_at</td><td>false</td></tr><tr><td>private_key</td><td>true</td></tr><tr><td>private_key_key_id</td><td>false</td></tr><tr><td>public_key</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_id</td><td>true</td></tr></tbody></table> |
|
||||||
| GroupSyncSettings<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>auto_create_missing_groups</td><td>true</td></tr><tr><td>field</td><td>true</td></tr><tr><td>legacy_group_name_mapping</td><td>false</td></tr><tr><td>mapping</td><td>true</td></tr><tr><td>regex_filter</td><td>true</td></tr></tbody></table> |
|
| GroupSyncSettings<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>auto_create_missing_groups</td><td>true</td></tr><tr><td>field</td><td>true</td></tr><tr><td>legacy_group_name_mapping</td><td>false</td></tr><tr><td>mapping</td><td>true</td></tr><tr><td>regex_filter</td><td>true</td></tr></tbody></table> |
|
||||||
| HealthSettings<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>dismissed_healthchecks</td><td>true</td></tr><tr><td>id</td><td>false</td></tr></tbody></table> |
|
| HealthSettings<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>dismissed_healthchecks</td><td>true</td></tr><tr><td>id</td><td>false</td></tr></tbody></table> |
|
||||||
| License<br><i>create, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>exp</td><td>true</td></tr><tr><td>id</td><td>false</td></tr><tr><td>jwt</td><td>false</td></tr><tr><td>uploaded_at</td><td>true</td></tr><tr><td>uuid</td><td>true</td></tr></tbody></table> |
|
| License<br><i>create, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>exp</td><td>true</td></tr><tr><td>id</td><td>false</td></tr><tr><td>jwt</td><td>false</td></tr><tr><td>uploaded_at</td><td>true</td></tr><tr><td>uuid</td><td>true</td></tr></tbody></table> |
|
||||||
| NotificationTemplate<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>actions</td><td>true</td></tr><tr><td>body_template</td><td>true</td></tr><tr><td>enabled_by_default</td><td>true</td></tr><tr><td>group</td><td>true</td></tr><tr><td>id</td><td>false</td></tr><tr><td>kind</td><td>true</td></tr><tr><td>method</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>title_template</td><td>true</td></tr></tbody></table> |
|
| NotificationTemplate<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>actions</td><td>true</td></tr><tr><td>body_template</td><td>true</td></tr><tr><td>enabled_by_default</td><td>true</td></tr><tr><td>group</td><td>true</td></tr><tr><td>id</td><td>false</td></tr><tr><td>kind</td><td>true</td></tr><tr><td>method</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>title_template</td><td>true</td></tr></tbody></table> |
|
||||||
| NotificationsSettings<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>id</td><td>false</td></tr><tr><td>notifier_paused</td><td>true</td></tr></tbody></table> |
|
| NotificationsSettings<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>id</td><td>false</td></tr><tr><td>notifier_paused</td><td>true</td></tr></tbody></table> |
|
||||||
| OAuth2ProviderApp<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>callback_url</td><td>true</td></tr><tr><td>client_id_issued_at</td><td>false</td></tr><tr><td>client_secret_expires_at</td><td>true</td></tr><tr><td>client_type</td><td>true</td></tr><tr><td>client_uri</td><td>true</td></tr><tr><td>contacts</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>dynamically_registered</td><td>true</td></tr><tr><td>grant_types</td><td>true</td></tr><tr><td>icon</td><td>true</td></tr><tr><td>id</td><td>false</td></tr><tr><td>jwks</td><td>true</td></tr><tr><td>jwks_uri</td><td>true</td></tr><tr><td>logo_uri</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>policy_uri</td><td>true</td></tr><tr><td>redirect_uris</td><td>true</td></tr><tr><td>registration_access_token</td><td>true</td></tr><tr><td>registration_client_uri</td><td>true</td></tr><tr><td>response_types</td><td>true</td></tr><tr><td>scope</td><td>true</td></tr><tr><td>software_id</td><td>true</td></tr><tr><td>software_version</td><td>true</td></tr><tr><td>token_endpoint_auth_method</td><td>true</td></tr><tr><td>tos_uri</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr></tbody></table> |
|
| OAuth2ProviderApp<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>callback_url</td><td>true</td></tr><tr><td>client_id_issued_at</td><td>false</td></tr><tr><td>client_secret_expires_at</td><td>true</td></tr><tr><td>client_type</td><td>true</td></tr><tr><td>client_uri</td><td>true</td></tr><tr><td>contacts</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>dynamically_registered</td><td>true</td></tr><tr><td>grant_types</td><td>true</td></tr><tr><td>icon</td><td>true</td></tr><tr><td>id</td><td>false</td></tr><tr><td>jwks</td><td>true</td></tr><tr><td>jwks_uri</td><td>true</td></tr><tr><td>logo_uri</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>policy_uri</td><td>true</td></tr><tr><td>redirect_uris</td><td>true</td></tr><tr><td>registration_access_token</td><td>true</td></tr><tr><td>registration_client_uri</td><td>true</td></tr><tr><td>response_types</td><td>true</td></tr><tr><td>scope</td><td>true</td></tr><tr><td>software_id</td><td>true</td></tr><tr><td>software_version</td><td>true</td></tr><tr><td>token_endpoint_auth_method</td><td>true</td></tr><tr><td>tos_uri</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr></tbody></table> |
|
||||||
| OAuth2ProviderAppSecret<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>app_id</td><td>false</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>display_secret</td><td>false</td></tr><tr><td>hashed_secret</td><td>false</td></tr><tr><td>id</td><td>false</td></tr><tr><td>last_used_at</td><td>false</td></tr><tr><td>secret_prefix</td><td>false</td></tr></tbody></table> |
|
| OAuth2ProviderAppSecret<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>app_id</td><td>false</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>display_secret</td><td>false</td></tr><tr><td>hashed_secret</td><td>false</td></tr><tr><td>id</td><td>false</td></tr><tr><td>last_used_at</td><td>false</td></tr><tr><td>secret_prefix</td><td>false</td></tr></tbody></table> |
|
||||||
| 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>shareable_workspace_owners</td><td>true</td></tr><tr><td>updated_at</td><td>true</td></tr></tbody></table> |
|
| 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>shareable_workspace_owners</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> |
|
| 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> |
|
||||||
| PrebuildsSettings<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>id</td><td>false</td></tr><tr><td>reconciliation_paused</td><td>true</td></tr></tbody></table> |
|
| PrebuildsSettings<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>id</td><td>false</td></tr><tr><td>reconciliation_paused</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> |
|
| 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> |
|
||||||
| TaskTable<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_at</td><td>false</td></tr><tr><td>display_name</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>owner_id</td><td>true</td></tr><tr><td>prompt</td><td>true</td></tr><tr><td>template_parameters</td><td>true</td></tr><tr><td>template_version_id</td><td>true</td></tr><tr><td>workspace_id</td><td>true</td></tr></tbody></table> |
|
| TaskTable<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_at</td><td>false</td></tr><tr><td>display_name</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>owner_id</td><td>true</td></tr><tr><td>prompt</td><td>true</td></tr><tr><td>template_parameters</td><td>true</td></tr><tr><td>template_version_id</td><td>true</td></tr><tr><td>workspace_id</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>cors_behavior</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_name</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>disable_module_cache</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> |
|
| Template<br><i>write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>abstract</td><td>true</td></tr><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>cors_behavior</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_name</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>disable_module_cache</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_name</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>has_ai_task</td><td>false</td></tr><tr><td>has_external_agent</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> |
|
| 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_name</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>has_ai_task</td><td>false</td></tr><tr><td>has_external_agent</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>chat_spend_limit_micros</td><td>true</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_service_account</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> |
|
| 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>chat_spend_limit_micros</td><td>true</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_service_account</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> |
|
||||||
| UserSecret<br><i>create, write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>created_at</td><td>false</td></tr><tr><td>description</td><td>true</td></tr><tr><td>env_name</td><td>true</td></tr><tr><td>file_path</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_id</td><td>true</td></tr><tr><td>value</td><td>true</td></tr><tr><td>value_key_id</td><td>false</td></tr></tbody></table> |
|
| UserSecret<br><i>create, write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>created_at</td><td>false</td></tr><tr><td>description</td><td>true</td></tr><tr><td>env_name</td><td>true</td></tr><tr><td>file_path</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_id</td><td>true</td></tr><tr><td>value</td><td>true</td></tr><tr><td>value_key_id</td><td>false</td></tr></tbody></table> |
|
||||||
| UserSkill<br><i>create, write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>content</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>description</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_id</td><td>true</td></tr></tbody></table> |
|
| UserSkill<br><i>create, write, delete</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>content</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>description</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_id</td><td>true</td></tr></tbody></table> |
|
||||||
| WorkspaceBuild<br><i>start, stop</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>build_number</td><td>false</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>daily_cost</td><td>false</td></tr><tr><td>deadline</td><td>false</td></tr><tr><td>has_ai_task</td><td>false</td></tr><tr><td>has_external_agent</td><td>false</td></tr><tr><td>id</td><td>false</td></tr><tr><td>initiator_by_avatar_url</td><td>false</td></tr><tr><td>initiator_by_name</td><td>false</td></tr><tr><td>initiator_by_username</td><td>false</td></tr><tr><td>initiator_id</td><td>false</td></tr><tr><td>job_id</td><td>false</td></tr><tr><td>max_deadline</td><td>false</td></tr><tr><td>reason</td><td>false</td></tr><tr><td>template_version_id</td><td>true</td></tr><tr><td>template_version_preset_id</td><td>false</td></tr><tr><td>transition</td><td>false</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>workspace_id</td><td>false</td></tr></tbody></table> |
|
| WorkspaceBuild<br><i>start, stop</i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>build_number</td><td>false</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>daily_cost</td><td>false</td></tr><tr><td>deadline</td><td>false</td></tr><tr><td>has_ai_task</td><td>false</td></tr><tr><td>has_external_agent</td><td>false</td></tr><tr><td>id</td><td>false</td></tr><tr><td>initiator_by_avatar_url</td><td>false</td></tr><tr><td>initiator_by_name</td><td>false</td></tr><tr><td>initiator_by_username</td><td>false</td></tr><tr><td>initiator_id</td><td>false</td></tr><tr><td>job_id</td><td>false</td></tr><tr><td>max_deadline</td><td>false</td></tr><tr><td>reason</td><td>false</td></tr><tr><td>template_version_id</td><td>true</td></tr><tr><td>template_version_preset_id</td><td>false</td></tr><tr><td>transition</td><td>false</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>workspace_id</td><td>false</td></tr></tbody></table> |
|
||||||
| WorkspaceProxy<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>deleted</td><td>false</td></tr><tr><td>derp_enabled</td><td>true</td></tr><tr><td>derp_only</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>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>region_id</td><td>true</td></tr><tr><td>token_hashed_secret</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>url</td><td>true</td></tr><tr><td>version</td><td>true</td></tr><tr><td>wildcard_hostname</td><td>true</td></tr></tbody></table> |
|
| WorkspaceProxy<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>deleted</td><td>false</td></tr><tr><td>derp_enabled</td><td>true</td></tr><tr><td>derp_only</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>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>region_id</td><td>true</td></tr><tr><td>token_hashed_secret</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>url</td><td>true</td></tr><tr><td>version</td><td>true</td></tr><tr><td>wildcard_hostname</td><td>true</td></tr></tbody></table> |
|
||||||
| WorkspaceTable<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>automatic_updates</td><td>true</td></tr><tr><td>autostart_schedule</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>deleted</td><td>false</td></tr><tr><td>deleting_at</td><td>true</td></tr><tr><td>dormant_at</td><td>true</td></tr><tr><td>favorite</td><td>true</td></tr><tr><td>group_acl</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>last_used_at</td><td>false</td></tr><tr><td>name</td><td>true</td></tr><tr><td>next_start_at</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>owner_id</td><td>true</td></tr><tr><td>template_id</td><td>true</td></tr><tr><td>ttl</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> |
|
| WorkspaceTable<br><i></i> | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody> | <tr><td>automatic_updates</td><td>true</td></tr><tr><td>autostart_schedule</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>deleted</td><td>false</td></tr><tr><td>deleting_at</td><td>true</td></tr><tr><td>dormant_at</td><td>true</td></tr><tr><td>favorite</td><td>true</td></tr><tr><td>group_acl</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>last_used_at</td><td>false</td></tr><tr><td>name</td><td>true</td></tr><tr><td>next_start_at</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>owner_id</td><td>true</td></tr><tr><td>template_id</td><td>true</td></tr><tr><td>ttl</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> |
|
||||||
|
|
||||||
<!-- End generated by 'make docs/admin/security/audit-logs.md'. -->
|
<!-- End generated by 'make docs/admin/security/audit-logs.md'. -->
|
||||||
|
|
||||||
|
|||||||
Generated
+6
@@ -4876,6 +4876,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
"abstract": "string",
|
||||||
"activity_bump_ms": 0,
|
"activity_bump_ms": 0,
|
||||||
"allow_user_autostart": true,
|
"allow_user_autostart": true,
|
||||||
"allow_user_autostop": true,
|
"allow_user_autostop": true,
|
||||||
@@ -4912,6 +4913,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
|||||||
|
|
||||||
| Name | Type | Required | Restrictions | Description |
|
| Name | Type | Required | Restrictions | Description |
|
||||||
|---------------------------------------|--------------------------------------------------------------------------------|----------|--------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|---------------------------------------|--------------------------------------------------------------------------------|----------|--------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| `abstract` | string | false | | Abstract is a longer-form summary surfaced to agents to help them pick the right template. Up to 2048 characters. |
|
||||||
| `activity_bump_ms` | integer | false | | Activity bump ms allows optionally specifying the activity bump duration for all workspaces created from this template. Defaults to 1h but can be set to 0 to disable activity bumping. |
|
| `activity_bump_ms` | integer | false | | Activity bump ms allows optionally specifying the activity bump duration for all workspaces created from this template. Defaults to 1h but can be set to 0 to disable activity bumping. |
|
||||||
| `allow_user_autostart` | boolean | false | | Allow user autostart allows users to set a schedule for autostarting their workspace. By default this is true. This can only be disabled when using an enterprise license. |
|
| `allow_user_autostart` | boolean | false | | Allow user autostart allows users to set a schedule for autostarting their workspace. By default this is true. This can only be disabled when using an enterprise license. |
|
||||||
| `allow_user_autostop` | boolean | false | | Allow user autostop allows users to set a custom workspace TTL to use in place of the template's DefaultTTL field. By default this is true. If false, the DefaultTTL will always be used. This can only be disabled when using an enterprise license. |
|
| `allow_user_autostop` | boolean | false | | Allow user autostop allows users to set a custom workspace TTL to use in place of the template's DefaultTTL field. By default this is true. If false, the DefaultTTL will always be used. This can only be disabled when using an enterprise license. |
|
||||||
@@ -11969,6 +11971,7 @@ Only certain features set these fields: - FeatureManagedAgentLimit|
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
"abstract": "string",
|
||||||
"active_user_count": 0,
|
"active_user_count": 0,
|
||||||
"active_version_id": "eae64611-bd53-4a80-bb77-df1e432c0fbc",
|
"active_version_id": "eae64611-bd53-4a80-bb77-df1e432c0fbc",
|
||||||
"activity_bump_ms": 0,
|
"activity_bump_ms": 0,
|
||||||
@@ -12029,6 +12032,7 @@ Only certain features set these fields: - FeatureManagedAgentLimit|
|
|||||||
|
|
||||||
| Name | Type | Required | Restrictions | Description |
|
| Name | Type | Required | Restrictions | Description |
|
||||||
|------------------------------------|--------------------------------------------------------------------------------|----------|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|------------------------------------|--------------------------------------------------------------------------------|----------|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| `abstract` | string | false | | Abstract is a longer-form summary surfaced to agents to help them pick the right template. Up to 2048 characters. |
|
||||||
| `active_user_count` | integer | false | | Active user count is set to -1 when loading. |
|
| `active_user_count` | integer | false | | Active user count is set to -1 when loading. |
|
||||||
| `active_version_id` | string | false | | |
|
| `active_version_id` | string | false | | |
|
||||||
| `activity_bump_ms` | integer | false | | |
|
| `activity_bump_ms` | integer | false | | |
|
||||||
@@ -13212,6 +13216,7 @@ Restarts will only happen on weekdays in this list on weeks which line up with W
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
"abstract": "string",
|
||||||
"activity_bump_ms": 0,
|
"activity_bump_ms": 0,
|
||||||
"allow_user_autostart": true,
|
"allow_user_autostart": true,
|
||||||
"allow_user_autostop": true,
|
"allow_user_autostop": true,
|
||||||
@@ -13251,6 +13256,7 @@ Restarts will only happen on weekdays in this list on weeks which line up with W
|
|||||||
|
|
||||||
| Name | Type | Required | Restrictions | Description |
|
| Name | Type | Required | Restrictions | Description |
|
||||||
|------------------------------------|--------------------------------------------------------------------------------|----------|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|------------------------------------|--------------------------------------------------------------------------------|----------|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| `abstract` | string | false | | Abstract is a longer-form summary surfaced to agents to help them pick the right template. Up to 2048 characters. |
|
||||||
| `activity_bump_ms` | integer | false | | Activity bump ms allows optionally specifying the activity bump duration for all workspaces created from this template. Defaults to 1h but can be set to 0 to disable activity bumping. |
|
| `activity_bump_ms` | integer | false | | Activity bump ms allows optionally specifying the activity bump duration for all workspaces created from this template. Defaults to 1h but can be set to 0 to disable activity bumping. |
|
||||||
| `allow_user_autostart` | boolean | false | | |
|
| `allow_user_autostart` | boolean | false | | |
|
||||||
| `allow_user_autostop` | boolean | false | | |
|
| `allow_user_autostop` | boolean | false | | |
|
||||||
|
|||||||
Generated
+10
@@ -30,6 +30,7 @@ To include deprecated templates, specify `deprecated:true` in the search query.
|
|||||||
```json
|
```json
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
"abstract": "string",
|
||||||
"active_user_count": 0,
|
"active_user_count": 0,
|
||||||
"active_version_id": "eae64611-bd53-4a80-bb77-df1e432c0fbc",
|
"active_version_id": "eae64611-bd53-4a80-bb77-df1e432c0fbc",
|
||||||
"activity_bump_ms": 0,
|
"activity_bump_ms": 0,
|
||||||
@@ -100,6 +101,7 @@ Status Code **200**
|
|||||||
| Name | Type | Required | Restrictions | Description |
|
| Name | Type | Required | Restrictions | Description |
|
||||||
|--------------------------------------|------------------------------------------------------------------------------------------|----------|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|--------------------------------------|------------------------------------------------------------------------------------------|----------|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `[array item]` | array | false | | |
|
| `[array item]` | array | false | | |
|
||||||
|
| `» abstract` | string | false | | Abstract is a longer-form summary surfaced to agents to help them pick the right template. Up to 2048 characters. |
|
||||||
| `» active_user_count` | integer | false | | Active user count is set to -1 when loading. |
|
| `» active_user_count` | integer | false | | Active user count is set to -1 when loading. |
|
||||||
| `» active_version_id` | string(uuid) | false | | |
|
| `» active_version_id` | string(uuid) | false | | |
|
||||||
| `» activity_bump_ms` | integer | false | | |
|
| `» activity_bump_ms` | integer | false | | |
|
||||||
@@ -171,6 +173,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
"abstract": "string",
|
||||||
"activity_bump_ms": 0,
|
"activity_bump_ms": 0,
|
||||||
"allow_user_autostart": true,
|
"allow_user_autostart": true,
|
||||||
"allow_user_autostop": true,
|
"allow_user_autostop": true,
|
||||||
@@ -216,6 +219,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
"abstract": "string",
|
||||||
"active_user_count": 0,
|
"active_user_count": 0,
|
||||||
"active_version_id": "eae64611-bd53-4a80-bb77-df1e432c0fbc",
|
"active_version_id": "eae64611-bd53-4a80-bb77-df1e432c0fbc",
|
||||||
"activity_bump_ms": 0,
|
"activity_bump_ms": 0,
|
||||||
@@ -368,6 +372,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
"abstract": "string",
|
||||||
"active_user_count": 0,
|
"active_user_count": 0,
|
||||||
"active_version_id": "eae64611-bd53-4a80-bb77-df1e432c0fbc",
|
"active_version_id": "eae64611-bd53-4a80-bb77-df1e432c0fbc",
|
||||||
"activity_bump_ms": 0,
|
"activity_bump_ms": 0,
|
||||||
@@ -790,6 +795,7 @@ To include deprecated templates, specify `deprecated:true` in the search query.
|
|||||||
```json
|
```json
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
"abstract": "string",
|
||||||
"active_user_count": 0,
|
"active_user_count": 0,
|
||||||
"active_version_id": "eae64611-bd53-4a80-bb77-df1e432c0fbc",
|
"active_version_id": "eae64611-bd53-4a80-bb77-df1e432c0fbc",
|
||||||
"activity_bump_ms": 0,
|
"activity_bump_ms": 0,
|
||||||
@@ -860,6 +866,7 @@ Status Code **200**
|
|||||||
| Name | Type | Required | Restrictions | Description |
|
| Name | Type | Required | Restrictions | Description |
|
||||||
|--------------------------------------|------------------------------------------------------------------------------------------|----------|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|--------------------------------------|------------------------------------------------------------------------------------------|----------|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `[array item]` | array | false | | |
|
| `[array item]` | array | false | | |
|
||||||
|
| `» abstract` | string | false | | Abstract is a longer-form summary surfaced to agents to help them pick the right template. Up to 2048 characters. |
|
||||||
| `» active_user_count` | integer | false | | Active user count is set to -1 when loading. |
|
| `» active_user_count` | integer | false | | Active user count is set to -1 when loading. |
|
||||||
| `» active_version_id` | string(uuid) | false | | |
|
| `» active_version_id` | string(uuid) | false | | |
|
||||||
| `» activity_bump_ms` | integer | false | | |
|
| `» activity_bump_ms` | integer | false | | |
|
||||||
@@ -994,6 +1001,7 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template} \
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
"abstract": "string",
|
||||||
"active_user_count": 0,
|
"active_user_count": 0,
|
||||||
"active_version_id": "eae64611-bd53-4a80-bb77-df1e432c0fbc",
|
"active_version_id": "eae64611-bd53-4a80-bb77-df1e432c0fbc",
|
||||||
"activity_bump_ms": 0,
|
"activity_bump_ms": 0,
|
||||||
@@ -1120,6 +1128,7 @@ curl -X PATCH http://coder-server:8080/api/v2/templates/{template} \
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
"abstract": "string",
|
||||||
"activity_bump_ms": 0,
|
"activity_bump_ms": 0,
|
||||||
"allow_user_autostart": true,
|
"allow_user_autostart": true,
|
||||||
"allow_user_autostop": true,
|
"allow_user_autostop": true,
|
||||||
@@ -1168,6 +1177,7 @@ curl -X PATCH http://coder-server:8080/api/v2/templates/{template} \
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
"abstract": "string",
|
||||||
"active_user_count": 0,
|
"active_user_count": 0,
|
||||||
"active_version_id": "eae64611-bd53-4a80-bb77-df1e432c0fbc",
|
"active_version_id": "eae64611-bd53-4a80-bb77-df1e432c0fbc",
|
||||||
"activity_bump_ms": 0,
|
"activity_bump_ms": 0,
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ var auditableResourcesTypes = map[any]map[string]Action{
|
|||||||
"provisioner": ActionTrack,
|
"provisioner": ActionTrack,
|
||||||
"active_version_id": ActionTrack,
|
"active_version_id": ActionTrack,
|
||||||
"description": ActionTrack,
|
"description": ActionTrack,
|
||||||
|
"abstract": ActionTrack,
|
||||||
"icon": ActionTrack,
|
"icon": ActionTrack,
|
||||||
"default_ttl": ActionTrack,
|
"default_ttl": ActionTrack,
|
||||||
"autostart_block_days_of_week": ActionTrack,
|
"autostart_block_days_of_week": ActionTrack,
|
||||||
|
|||||||
Generated
+15
@@ -3476,6 +3476,11 @@ export interface CreateTemplateRequest {
|
|||||||
* less than 128 bytes.
|
* less than 128 bytes.
|
||||||
*/
|
*/
|
||||||
readonly description?: string;
|
readonly description?: string;
|
||||||
|
/**
|
||||||
|
* Abstract is a longer-form summary surfaced to agents to help them pick
|
||||||
|
* the right template. Up to 2048 characters.
|
||||||
|
*/
|
||||||
|
readonly abstract?: string;
|
||||||
/**
|
/**
|
||||||
* Icon is a relative path or external URL that specifies
|
* Icon is a relative path or external URL that specifies
|
||||||
* an icon to be displayed in the dashboard.
|
* an icon to be displayed in the dashboard.
|
||||||
@@ -8025,6 +8030,11 @@ export interface Template {
|
|||||||
readonly active_user_count: number;
|
readonly active_user_count: number;
|
||||||
readonly build_time_stats: TemplateBuildTimeStats;
|
readonly build_time_stats: TemplateBuildTimeStats;
|
||||||
readonly description: string;
|
readonly description: string;
|
||||||
|
/**
|
||||||
|
* Abstract is a longer-form summary surfaced to agents to help them pick
|
||||||
|
* the right template. Up to 2048 characters.
|
||||||
|
*/
|
||||||
|
readonly abstract: string;
|
||||||
readonly deprecated: boolean;
|
readonly deprecated: boolean;
|
||||||
readonly deprecation_message: string;
|
readonly deprecation_message: string;
|
||||||
readonly deleted: boolean;
|
readonly deleted: boolean;
|
||||||
@@ -8841,6 +8851,11 @@ export interface UpdateTemplateMeta {
|
|||||||
readonly name?: string;
|
readonly name?: string;
|
||||||
readonly display_name?: string;
|
readonly display_name?: string;
|
||||||
readonly description?: string;
|
readonly description?: string;
|
||||||
|
/**
|
||||||
|
* Abstract is a longer-form summary surfaced to agents to help them pick
|
||||||
|
* the right template. Up to 2048 characters.
|
||||||
|
*/
|
||||||
|
readonly abstract?: string;
|
||||||
readonly icon?: string;
|
readonly icon?: string;
|
||||||
readonly default_ttl_ms?: number;
|
readonly default_ttl_ms?: number;
|
||||||
/**
|
/**
|
||||||
|
|||||||
+17
@@ -38,6 +38,8 @@ import {
|
|||||||
|
|
||||||
const MAX_DESCRIPTION_CHAR_LIMIT = 128;
|
const MAX_DESCRIPTION_CHAR_LIMIT = 128;
|
||||||
const MAX_DESCRIPTION_MESSAGE = `Please enter a description that is no longer than ${MAX_DESCRIPTION_CHAR_LIMIT} characters.`;
|
const MAX_DESCRIPTION_MESSAGE = `Please enter a description that is no longer than ${MAX_DESCRIPTION_CHAR_LIMIT} characters.`;
|
||||||
|
const MAX_ABSTRACT_CHAR_LIMIT = 2048;
|
||||||
|
const MAX_ABSTRACT_MESSAGE = `Please enter an abstract that is no longer than ${MAX_ABSTRACT_CHAR_LIMIT} characters.`;
|
||||||
|
|
||||||
export const validationSchema = Yup.object({
|
export const validationSchema = Yup.object({
|
||||||
name: nameValidator("Name"),
|
name: nameValidator("Name"),
|
||||||
@@ -46,6 +48,7 @@ export const validationSchema = Yup.object({
|
|||||||
MAX_DESCRIPTION_CHAR_LIMIT,
|
MAX_DESCRIPTION_CHAR_LIMIT,
|
||||||
MAX_DESCRIPTION_MESSAGE,
|
MAX_DESCRIPTION_MESSAGE,
|
||||||
),
|
),
|
||||||
|
abstract: Yup.string().max(MAX_ABSTRACT_CHAR_LIMIT, MAX_ABSTRACT_MESSAGE),
|
||||||
allow_user_cancel_workspace_jobs: Yup.boolean(),
|
allow_user_cancel_workspace_jobs: Yup.boolean(),
|
||||||
icon: iconValidator,
|
icon: iconValidator,
|
||||||
require_active_version: Yup.boolean(),
|
require_active_version: Yup.boolean(),
|
||||||
@@ -85,6 +88,7 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
|
|||||||
name: template.name,
|
name: template.name,
|
||||||
display_name: template.display_name,
|
display_name: template.display_name,
|
||||||
description: template.description,
|
description: template.description,
|
||||||
|
abstract: template.abstract,
|
||||||
icon: template.icon,
|
icon: template.icon,
|
||||||
allow_user_cancel_workspace_jobs:
|
allow_user_cancel_workspace_jobs:
|
||||||
template.allow_user_cancel_workspace_jobs,
|
template.allow_user_cancel_workspace_jobs,
|
||||||
@@ -148,6 +152,19 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
|
|||||||
rows={2}
|
rows={2}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<TextField
|
||||||
|
{...getFieldHelpers("abstract", {
|
||||||
|
helperText:
|
||||||
|
"Detailed summary for agents to help choose the right template.",
|
||||||
|
maxLength: MAX_ABSTRACT_CHAR_LIMIT,
|
||||||
|
})}
|
||||||
|
multiline
|
||||||
|
disabled={isSubmitting}
|
||||||
|
fullWidth
|
||||||
|
label="Abstract"
|
||||||
|
rows={5}
|
||||||
|
/>
|
||||||
|
|
||||||
<IconField
|
<IconField
|
||||||
{...getFieldHelpers("icon")}
|
{...getFieldHelpers("icon")}
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
|
|||||||
+28
@@ -27,6 +27,7 @@ const validFormValues: FormValues = {
|
|||||||
name: "Name",
|
name: "Name",
|
||||||
display_name: "A display name",
|
display_name: "A display name",
|
||||||
description: "A description",
|
description: "A description",
|
||||||
|
abstract: "An agent-facing summary",
|
||||||
icon: "vscode.png",
|
icon: "vscode.png",
|
||||||
allow_user_cancel_workspace_jobs: false,
|
allow_user_cancel_workspace_jobs: false,
|
||||||
allow_user_autostart: false,
|
allow_user_autostart: false,
|
||||||
@@ -74,6 +75,7 @@ const fillAndSubmitForm = async ({
|
|||||||
name,
|
name,
|
||||||
display_name,
|
display_name,
|
||||||
description,
|
description,
|
||||||
|
abstract,
|
||||||
icon,
|
icon,
|
||||||
allow_user_cancel_workspace_jobs,
|
allow_user_cancel_workspace_jobs,
|
||||||
}: FormValues) => {
|
}: FormValues) => {
|
||||||
@@ -89,6 +91,12 @@ const fillAndSubmitForm = async ({
|
|||||||
await userEvent.clear(descriptionField);
|
await userEvent.clear(descriptionField);
|
||||||
await userEvent.type(descriptionField, description);
|
await userEvent.type(descriptionField, description);
|
||||||
|
|
||||||
|
const abstractField = await screen.findByLabelText("Abstract");
|
||||||
|
await userEvent.clear(abstractField);
|
||||||
|
if (abstract !== "") {
|
||||||
|
await userEvent.type(abstractField, abstract);
|
||||||
|
}
|
||||||
|
|
||||||
const iconField = await screen.findByLabelText("Icon");
|
const iconField = await screen.findByLabelText("Icon");
|
||||||
await userEvent.clear(iconField);
|
await userEvent.clear(iconField);
|
||||||
await userEvent.type(iconField, icon);
|
await userEvent.type(iconField, icon);
|
||||||
@@ -118,6 +126,10 @@ describe("TemplateSettingsPage", { timeout: 20_000 }, () => {
|
|||||||
});
|
});
|
||||||
await fillAndSubmitForm(validFormValues);
|
await fillAndSubmitForm(validFormValues);
|
||||||
await waitFor(() => expect(API.updateTemplateMeta).toBeCalledTimes(1));
|
await waitFor(() => expect(API.updateTemplateMeta).toBeCalledTimes(1));
|
||||||
|
expect(API.updateTemplateMeta).toHaveBeenCalledWith(
|
||||||
|
MockTemplate.id,
|
||||||
|
expect.objectContaining({ abstract: validFormValues.abstract }),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("displays an error if the name is taken", async () => {
|
it("displays an error if the name is taken", async () => {
|
||||||
@@ -165,6 +177,22 @@ describe("TemplateSettingsPage", { timeout: 20_000 }, () => {
|
|||||||
expect(validate).toThrowError();
|
expect(validate).toThrowError();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it.each<[string, string, boolean]>([
|
||||||
|
["allows the maximum abstract length", "a".repeat(2048), false],
|
||||||
|
["rejects an over-limit abstract", "a".repeat(2049), true],
|
||||||
|
])("%s", (_, abstract, wantError) => {
|
||||||
|
const values: UpdateTemplateMeta = {
|
||||||
|
...validFormValues,
|
||||||
|
abstract,
|
||||||
|
};
|
||||||
|
const validate = () => validationSchema.validateSync(values);
|
||||||
|
if (wantError) {
|
||||||
|
expect(validate).toThrowError();
|
||||||
|
} else {
|
||||||
|
expect(validate).not.toThrowError();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
describe("Deprecate template", () => {
|
describe("Deprecate template", () => {
|
||||||
it("deprecates a template when has access control", async () => {
|
it("deprecates a template when has access control", async () => {
|
||||||
server.use(
|
server.use(
|
||||||
|
|||||||
+10
@@ -57,3 +57,13 @@ export const NoEntitlementsExpiredSettings: Story = {
|
|||||||
advancedSchedulingEnabled: false,
|
advancedSchedulingEnabled: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const WithAbstract: Story = {
|
||||||
|
args: {
|
||||||
|
template: {
|
||||||
|
...MockTemplate,
|
||||||
|
abstract:
|
||||||
|
"This template provisions a remote VS Code environment backed by a Kubernetes pod. Agents should prefer it when the user mentions Kubernetes, k8s, or remote development.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|||||||
@@ -894,6 +894,7 @@ export const MockTemplate: TypesGen.Template = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
description: "This is a test description.",
|
description: "This is a test description.",
|
||||||
|
abstract: "",
|
||||||
default_ttl_ms: 24 * 60 * 60 * 1000,
|
default_ttl_ms: 24 * 60 * 60 * 1000,
|
||||||
activity_bump_ms: 1 * 60 * 60 * 1000,
|
activity_bump_ms: 1 * 60 * 60 * 1000,
|
||||||
autostop_requirement: {
|
autostop_requirement: {
|
||||||
|
|||||||
Reference in New Issue
Block a user