chore: add form_type parameter argument to db (#17920)

`form_type` is a new parameter field in the terraform provider. Bring
that field into coder/coder.

Validation for `multi-select` has also been added.
This commit is contained in:
Steven Masley
2025-05-29 08:55:19 -05:00
committed by GitHub
parent 776c144128
commit 8387dd27ab
35 changed files with 1556 additions and 822 deletions
+17
View File
@@ -16095,6 +16095,23 @@ const docTemplate = `{
"ephemeral": {
"type": "boolean"
},
"form_type": {
"description": "FormType has an enum value of empty string, ` + "`" + `\"\"` + "`" + `.\nKeep the leading comma in the enums struct tag.",
"type": "string",
"enum": [
"",
"radio",
"dropdown",
"input",
"textarea",
"slider",
"checkbox",
"switch",
"tag-select",
"multi-select",
"error"
]
},
"icon": {
"type": "string"
},
+17
View File
@@ -14662,6 +14662,23 @@
"ephemeral": {
"type": "boolean"
},
"form_type": {
"description": "FormType has an enum value of empty string, `\"\"`.\nKeep the leading comma in the enums struct tag.",
"type": "string",
"enum": [
"",
"radio",
"dropdown",
"input",
"textarea",
"slider",
"checkbox",
"switch",
"tag-select",
"multi-select",
"error"
]
},
"icon": {
"type": "string"
},
+7 -5
View File
@@ -92,13 +92,13 @@ func WorkspaceBuildParameters(params []database.WorkspaceBuildParameter) []coder
}
func TemplateVersionParameters(params []database.TemplateVersionParameter) ([]codersdk.TemplateVersionParameter, error) {
out := make([]codersdk.TemplateVersionParameter, len(params))
var err error
for i, p := range params {
out[i], err = TemplateVersionParameter(p)
out := make([]codersdk.TemplateVersionParameter, 0, len(params))
for _, p := range params {
np, err := TemplateVersionParameter(p)
if err != nil {
return nil, xerrors.Errorf("convert template version parameter %q: %w", p.Name, err)
}
out = append(out, np)
}
return out, nil
@@ -131,6 +131,7 @@ func TemplateVersionParameter(param database.TemplateVersionParameter) (codersdk
Description: param.Description,
DescriptionPlaintext: descriptionPlaintext,
Type: param.Type,
FormType: string(param.FormType),
Mutable: param.Mutable,
DefaultValue: param.DefaultValue,
Icon: param.Icon,
@@ -293,7 +294,8 @@ func templateVersionParameterOptions(rawOptions json.RawMessage) ([]codersdk.Tem
if err != nil {
return nil, err
}
var options []codersdk.TemplateVersionParameterOption
options := make([]codersdk.TemplateVersionParameterOption, 0)
for _, option := range protoOptions {
options = append(options, codersdk.TemplateVersionParameterOption{
Name: option.Name,
+1
View File
@@ -992,6 +992,7 @@ func TemplateVersionParameter(t testing.TB, db database.Store, orig database.Tem
Name: takeFirst(orig.Name, testutil.GetRandomName(t)),
Description: takeFirst(orig.Description, testutil.GetRandomName(t)),
Type: takeFirst(orig.Type, "string"),
FormType: orig.FormType, // empty string is ok!
Mutable: takeFirst(orig.Mutable, false),
DefaultValue: takeFirst(orig.DefaultValue, testutil.GetRandomName(t)),
Icon: takeFirst(orig.Icon, testutil.GetRandomName(t)),
+1
View File
@@ -9393,6 +9393,7 @@ func (q *FakeQuerier) InsertTemplateVersionParameter(_ context.Context, arg data
DisplayName: arg.DisplayName,
Description: arg.Description,
Type: arg.Type,
FormType: arg.FormType,
Mutable: arg.Mutable,
DefaultValue: arg.DefaultValue,
Icon: arg.Icon,
+19
View File
@@ -132,6 +132,22 @@ CREATE TYPE parameter_destination_scheme AS ENUM (
'provisioner_variable'
);
CREATE TYPE parameter_form_type AS ENUM (
'',
'error',
'radio',
'dropdown',
'input',
'textarea',
'slider',
'checkbox',
'switch',
'tag-select',
'multi-select'
);
COMMENT ON TYPE parameter_form_type IS 'Enum set should match the terraform provider set. This is defined as future form_types are not supported, and should be rejected. Always include the empty string for using the default form type.';
CREATE TYPE parameter_scope AS ENUM (
'template',
'import_job',
@@ -1434,6 +1450,7 @@ CREATE TABLE template_version_parameters (
display_name text DEFAULT ''::text NOT NULL,
display_order integer DEFAULT 0 NOT NULL,
ephemeral boolean DEFAULT false NOT NULL,
form_type parameter_form_type DEFAULT ''::parameter_form_type NOT NULL,
CONSTRAINT validation_monotonic_order CHECK ((validation_monotonic = ANY (ARRAY['increasing'::text, 'decreasing'::text, ''::text])))
);
@@ -1469,6 +1486,8 @@ COMMENT ON COLUMN template_version_parameters.display_order IS 'Specifies the or
COMMENT ON COLUMN template_version_parameters.ephemeral IS 'The value of an ephemeral parameter will not be preserved between consecutive workspace builds.';
COMMENT ON COLUMN template_version_parameters.form_type IS 'Specify what form_type should be used to render the parameter in the UI. Unsupported values are rejected.';
CREATE TABLE template_version_preset_parameters (
id uuid DEFAULT gen_random_uuid() NOT NULL,
template_version_preset_id uuid NOT NULL,
@@ -0,0 +1,2 @@
ALTER TABLE template_version_parameters DROP COLUMN form_type;
DROP TYPE parameter_form_type;
@@ -0,0 +1,11 @@
CREATE TYPE parameter_form_type AS ENUM ('', 'error', 'radio', 'dropdown', 'input', 'textarea', 'slider', 'checkbox', 'switch', 'tag-select', 'multi-select');
COMMENT ON TYPE parameter_form_type
IS 'Enum set should match the terraform provider set. This is defined as future form_types are not supported, and should be rejected. '
'Always include the empty string for using the default form type.';
-- Intentionally leaving the default blank. The provisioner will not re-run any
-- imports to backfill these values. Missing values just have to be handled.
ALTER TABLE template_version_parameters ADD COLUMN form_type parameter_form_type NOT NULL DEFAULT '';
COMMENT ON COLUMN template_version_parameters.form_type
IS 'Specify what form_type should be used to render the parameter in the UI. Unsupported values are rejected.';
+88
View File
@@ -1108,6 +1108,92 @@ func AllParameterDestinationSchemeValues() []ParameterDestinationScheme {
}
}
// Enum set should match the terraform provider set. This is defined as future form_types are not supported, and should be rejected. Always include the empty string for using the default form type.
type ParameterFormType string
const (
ParameterFormTypeValue0 ParameterFormType = ""
ParameterFormTypeError ParameterFormType = "error"
ParameterFormTypeRadio ParameterFormType = "radio"
ParameterFormTypeDropdown ParameterFormType = "dropdown"
ParameterFormTypeInput ParameterFormType = "input"
ParameterFormTypeTextarea ParameterFormType = "textarea"
ParameterFormTypeSlider ParameterFormType = "slider"
ParameterFormTypeCheckbox ParameterFormType = "checkbox"
ParameterFormTypeSwitch ParameterFormType = "switch"
ParameterFormTypeTagSelect ParameterFormType = "tag-select"
ParameterFormTypeMultiSelect ParameterFormType = "multi-select"
)
func (e *ParameterFormType) Scan(src interface{}) error {
switch s := src.(type) {
case []byte:
*e = ParameterFormType(s)
case string:
*e = ParameterFormType(s)
default:
return fmt.Errorf("unsupported scan type for ParameterFormType: %T", src)
}
return nil
}
type NullParameterFormType struct {
ParameterFormType ParameterFormType `json:"parameter_form_type"`
Valid bool `json:"valid"` // Valid is true if ParameterFormType is not NULL
}
// Scan implements the Scanner interface.
func (ns *NullParameterFormType) Scan(value interface{}) error {
if value == nil {
ns.ParameterFormType, ns.Valid = "", false
return nil
}
ns.Valid = true
return ns.ParameterFormType.Scan(value)
}
// Value implements the driver Valuer interface.
func (ns NullParameterFormType) Value() (driver.Value, error) {
if !ns.Valid {
return nil, nil
}
return string(ns.ParameterFormType), nil
}
func (e ParameterFormType) Valid() bool {
switch e {
case ParameterFormTypeValue0,
ParameterFormTypeError,
ParameterFormTypeRadio,
ParameterFormTypeDropdown,
ParameterFormTypeInput,
ParameterFormTypeTextarea,
ParameterFormTypeSlider,
ParameterFormTypeCheckbox,
ParameterFormTypeSwitch,
ParameterFormTypeTagSelect,
ParameterFormTypeMultiSelect:
return true
}
return false
}
func AllParameterFormTypeValues() []ParameterFormType {
return []ParameterFormType{
ParameterFormTypeValue0,
ParameterFormTypeError,
ParameterFormTypeRadio,
ParameterFormTypeDropdown,
ParameterFormTypeInput,
ParameterFormTypeTextarea,
ParameterFormTypeSlider,
ParameterFormTypeCheckbox,
ParameterFormTypeSwitch,
ParameterFormTypeTagSelect,
ParameterFormTypeMultiSelect,
}
}
type ParameterScope string
const (
@@ -3308,6 +3394,8 @@ type TemplateVersionParameter struct {
DisplayOrder int32 `db:"display_order" json:"display_order"`
// The value of an ephemeral parameter will not be preserved between consecutive workspace builds.
Ephemeral bool `db:"ephemeral" json:"ephemeral"`
// Specify what form_type should be used to render the parameter in the UI. Unsupported values are rejected.
FormType ParameterFormType `db:"form_type" json:"form_type"`
}
type TemplateVersionPreset struct {
+26 -20
View File
@@ -11178,7 +11178,7 @@ func (q *sqlQuerier) UpdateTemplateScheduleByID(ctx context.Context, arg UpdateT
}
const getTemplateVersionParameters = `-- name: GetTemplateVersionParameters :many
SELECT template_version_id, name, description, type, mutable, default_value, icon, options, validation_regex, validation_min, validation_max, validation_error, validation_monotonic, required, display_name, display_order, ephemeral FROM template_version_parameters WHERE template_version_id = $1 ORDER BY display_order ASC, LOWER(name) ASC
SELECT template_version_id, name, description, type, mutable, default_value, icon, options, validation_regex, validation_min, validation_max, validation_error, validation_monotonic, required, display_name, display_order, ephemeral, form_type FROM template_version_parameters WHERE template_version_id = $1 ORDER BY display_order ASC, LOWER(name) ASC
`
func (q *sqlQuerier) GetTemplateVersionParameters(ctx context.Context, templateVersionID uuid.UUID) ([]TemplateVersionParameter, error) {
@@ -11208,6 +11208,7 @@ func (q *sqlQuerier) GetTemplateVersionParameters(ctx context.Context, templateV
&i.DisplayName,
&i.DisplayOrder,
&i.Ephemeral,
&i.FormType,
); err != nil {
return nil, err
}
@@ -11229,6 +11230,7 @@ INSERT INTO
name,
description,
type,
form_type,
mutable,
default_value,
icon,
@@ -11261,28 +11263,30 @@ VALUES
$14,
$15,
$16,
$17
) RETURNING template_version_id, name, description, type, mutable, default_value, icon, options, validation_regex, validation_min, validation_max, validation_error, validation_monotonic, required, display_name, display_order, ephemeral
$17,
$18
) RETURNING template_version_id, name, description, type, mutable, default_value, icon, options, validation_regex, validation_min, validation_max, validation_error, validation_monotonic, required, display_name, display_order, ephemeral, form_type
`
type InsertTemplateVersionParameterParams struct {
TemplateVersionID uuid.UUID `db:"template_version_id" json:"template_version_id"`
Name string `db:"name" json:"name"`
Description string `db:"description" json:"description"`
Type string `db:"type" json:"type"`
Mutable bool `db:"mutable" json:"mutable"`
DefaultValue string `db:"default_value" json:"default_value"`
Icon string `db:"icon" json:"icon"`
Options json.RawMessage `db:"options" json:"options"`
ValidationRegex string `db:"validation_regex" json:"validation_regex"`
ValidationMin sql.NullInt32 `db:"validation_min" json:"validation_min"`
ValidationMax sql.NullInt32 `db:"validation_max" json:"validation_max"`
ValidationError string `db:"validation_error" json:"validation_error"`
ValidationMonotonic string `db:"validation_monotonic" json:"validation_monotonic"`
Required bool `db:"required" json:"required"`
DisplayName string `db:"display_name" json:"display_name"`
DisplayOrder int32 `db:"display_order" json:"display_order"`
Ephemeral bool `db:"ephemeral" json:"ephemeral"`
TemplateVersionID uuid.UUID `db:"template_version_id" json:"template_version_id"`
Name string `db:"name" json:"name"`
Description string `db:"description" json:"description"`
Type string `db:"type" json:"type"`
FormType ParameterFormType `db:"form_type" json:"form_type"`
Mutable bool `db:"mutable" json:"mutable"`
DefaultValue string `db:"default_value" json:"default_value"`
Icon string `db:"icon" json:"icon"`
Options json.RawMessage `db:"options" json:"options"`
ValidationRegex string `db:"validation_regex" json:"validation_regex"`
ValidationMin sql.NullInt32 `db:"validation_min" json:"validation_min"`
ValidationMax sql.NullInt32 `db:"validation_max" json:"validation_max"`
ValidationError string `db:"validation_error" json:"validation_error"`
ValidationMonotonic string `db:"validation_monotonic" json:"validation_monotonic"`
Required bool `db:"required" json:"required"`
DisplayName string `db:"display_name" json:"display_name"`
DisplayOrder int32 `db:"display_order" json:"display_order"`
Ephemeral bool `db:"ephemeral" json:"ephemeral"`
}
func (q *sqlQuerier) InsertTemplateVersionParameter(ctx context.Context, arg InsertTemplateVersionParameterParams) (TemplateVersionParameter, error) {
@@ -11291,6 +11295,7 @@ func (q *sqlQuerier) InsertTemplateVersionParameter(ctx context.Context, arg Ins
arg.Name,
arg.Description,
arg.Type,
arg.FormType,
arg.Mutable,
arg.DefaultValue,
arg.Icon,
@@ -11324,6 +11329,7 @@ func (q *sqlQuerier) InsertTemplateVersionParameter(ctx context.Context, arg Ins
&i.DisplayName,
&i.DisplayOrder,
&i.Ephemeral,
&i.FormType,
)
return i, err
}
@@ -5,6 +5,7 @@ INSERT INTO
name,
description,
type,
form_type,
mutable,
default_value,
icon,
@@ -37,7 +38,8 @@ VALUES
$14,
$15,
$16,
$17
$17,
$18
) RETURNING *;
-- name: GetTemplateVersionParameters :many
@@ -28,6 +28,7 @@ import (
protobuf "google.golang.org/protobuf/proto"
"cdr.dev/slog"
"github.com/coder/coder/v2/coderd/util/slice"
"github.com/coder/coder/v2/codersdk/drpcsdk"
@@ -1453,12 +1454,24 @@ func (s *server) completeTemplateImportJob(ctx context.Context, job database.Pro
}
}
pft, err := sdkproto.ProviderFormType(richParameter.FormType)
if err != nil {
return xerrors.Errorf("parameter %q: %w", richParameter.Name, err)
}
dft := database.ParameterFormType(pft)
if !dft.Valid() {
list := strings.Join(slice.ToStrings(database.AllParameterFormTypeValues()), ", ")
return xerrors.Errorf("parameter %q field 'form_type' not valid, currently supported: %s", richParameter.Name, list)
}
_, err = db.InsertTemplateVersionParameter(ctx, database.InsertTemplateVersionParameterParams{
TemplateVersionID: input.TemplateVersionID,
Name: richParameter.Name,
DisplayName: richParameter.DisplayName,
Description: richParameter.Description,
Type: richParameter.Type,
FormType: dft,
Mutable: richParameter.Mutable,
DefaultValue: richParameter.DefaultValue,
Icon: richParameter.Icon,
@@ -1384,6 +1384,60 @@ func TestCompleteJob(t *testing.T) {
})
})
t.Run("WorkspaceBuild_BadFormType", func(t *testing.T) {
t.Parallel()
srv, db, _, pd := setup(t, false, &overrides{})
jobID := uuid.New()
versionID := uuid.New()
err := db.InsertTemplateVersion(ctx, database.InsertTemplateVersionParams{
ID: versionID,
JobID: jobID,
OrganizationID: pd.OrganizationID,
})
require.NoError(t, err)
job, err := db.InsertProvisionerJob(ctx, database.InsertProvisionerJobParams{
ID: jobID,
Provisioner: database.ProvisionerTypeEcho,
Input: []byte(`{"template_version_id": "` + versionID.String() + `"}`),
StorageMethod: database.ProvisionerStorageMethodFile,
Type: database.ProvisionerJobTypeWorkspaceBuild,
OrganizationID: pd.OrganizationID,
})
require.NoError(t, err)
_, err = db.AcquireProvisionerJob(ctx, database.AcquireProvisionerJobParams{
OrganizationID: pd.OrganizationID,
WorkerID: uuid.NullUUID{
UUID: pd.ID,
Valid: true,
},
Types: []database.ProvisionerType{database.ProvisionerTypeEcho},
})
require.NoError(t, err)
_, err = srv.CompleteJob(ctx, &proto.CompletedJob{
JobId: job.ID.String(),
Type: &proto.CompletedJob_TemplateImport_{
TemplateImport: &proto.CompletedJob_TemplateImport{
StartResources: []*sdkproto.Resource{{
Name: "hello",
Type: "aws_instance",
}},
StopResources: []*sdkproto.Resource{},
RichParameters: []*sdkproto.RichParameter{
{
Name: "parameter",
Type: "string",
FormType: -1,
},
},
Plan: []byte("{}"),
},
},
})
require.Error(t, err)
require.ErrorContains(t, err, "unsupported form type")
})
t.Run("TemplateImport_MissingGitAuth", func(t *testing.T) {
t.Parallel()
srv, db, _, pd := setup(t, false, &overrides{})
+1 -64
View File
@@ -31,14 +31,12 @@ import (
"github.com/coder/coder/v2/coderd/provisionerdserver"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/coderd/rbac/policy"
"github.com/coder/coder/v2/coderd/render"
"github.com/coder/coder/v2/coderd/tracing"
"github.com/coder/coder/v2/coderd/util/ptr"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/examples"
"github.com/coder/coder/v2/provisioner/terraform/tfparse"
"github.com/coder/coder/v2/provisionersdk"
sdkproto "github.com/coder/coder/v2/provisionersdk/proto"
)
// @Summary Get template version by ID
@@ -307,7 +305,7 @@ func (api *API) templateVersionRichParameters(rw http.ResponseWriter, r *http.Re
return
}
templateVersionParameters, err := convertTemplateVersionParameters(dbTemplateVersionParameters)
templateVersionParameters, err := db2sdk.TemplateVersionParameters(dbTemplateVersionParameters)
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Internal error converting template version parameter.",
@@ -1869,67 +1867,6 @@ func convertTemplateVersion(version database.TemplateVersion, job codersdk.Provi
}
}
func convertTemplateVersionParameters(dbParams []database.TemplateVersionParameter) ([]codersdk.TemplateVersionParameter, error) {
params := make([]codersdk.TemplateVersionParameter, 0)
for _, dbParameter := range dbParams {
param, err := convertTemplateVersionParameter(dbParameter)
if err != nil {
return nil, err
}
params = append(params, param)
}
return params, nil
}
func convertTemplateVersionParameter(param database.TemplateVersionParameter) (codersdk.TemplateVersionParameter, error) {
var protoOptions []*sdkproto.RichParameterOption
err := json.Unmarshal(param.Options, &protoOptions)
if err != nil {
return codersdk.TemplateVersionParameter{}, err
}
options := make([]codersdk.TemplateVersionParameterOption, 0)
for _, option := range protoOptions {
options = append(options, codersdk.TemplateVersionParameterOption{
Name: option.Name,
Description: option.Description,
Value: option.Value,
Icon: option.Icon,
})
}
descriptionPlaintext, err := render.PlaintextFromMarkdown(param.Description)
if err != nil {
return codersdk.TemplateVersionParameter{}, err
}
var validationMin, validationMax *int32
if param.ValidationMin.Valid {
validationMin = &param.ValidationMin.Int32
}
if param.ValidationMax.Valid {
validationMax = &param.ValidationMax.Int32
}
return codersdk.TemplateVersionParameter{
Name: param.Name,
DisplayName: param.DisplayName,
Description: param.Description,
DescriptionPlaintext: descriptionPlaintext,
Type: param.Type,
Mutable: param.Mutable,
DefaultValue: param.DefaultValue,
Icon: param.Icon,
Options: options,
ValidationRegex: param.ValidationRegex,
ValidationMin: validationMin,
ValidationMax: validationMax,
ValidationError: param.ValidationError,
ValidationMonotonic: codersdk.ValidationMonotonicOrder(param.ValidationMonotonic),
Required: param.Required,
Ephemeral: param.Ephemeral,
}, nil
}
func convertTemplateVersionVariables(dbVariables []database.TemplateVersionVariable) []codersdk.TemplateVersionVariable {
variables := make([]codersdk.TemplateVersionVariable, 0)
for _, dbVariable := range dbVariables {
+98 -1
View File
@@ -42,6 +42,7 @@ import (
"github.com/coder/coder/v2/provisioner/echo"
"github.com/coder/coder/v2/provisionersdk/proto"
"github.com/coder/coder/v2/testutil"
"github.com/coder/terraform-provider-coder/v2/provider"
)
func TestWorkspace(t *testing.T) {
@@ -3527,6 +3528,12 @@ func TestWorkspaceWithRichParameters(t *testing.T) {
secondParameterDescription = "_This_ is second *parameter*"
secondParameterValue = "2"
secondParameterValidationMonotonic = codersdk.MonotonicOrderIncreasing
thirdParameterName = "third_parameter"
thirdParameterType = "list(string)"
thirdParameterFormType = proto.ParameterFormType_MULTISELECT
thirdParameterDefault = `["red"]`
thirdParameterOption = "red"
)
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
@@ -3542,6 +3549,7 @@ func TestWorkspaceWithRichParameters(t *testing.T) {
Name: firstParameterName,
Type: firstParameterType,
Description: firstParameterDescription,
FormType: proto.ParameterFormType_INPUT,
},
{
Name: secondParameterName,
@@ -3551,6 +3559,19 @@ func TestWorkspaceWithRichParameters(t *testing.T) {
ValidationMin: ptr.Ref(int32(1)),
ValidationMax: ptr.Ref(int32(3)),
ValidationMonotonic: string(secondParameterValidationMonotonic),
FormType: proto.ParameterFormType_INPUT,
},
{
Name: thirdParameterName,
Type: thirdParameterType,
DefaultValue: thirdParameterDefault,
Options: []*proto.RichParameterOption{
{
Name: thirdParameterOption,
Value: thirdParameterOption,
},
},
FormType: thirdParameterFormType,
},
},
},
@@ -3575,12 +3596,13 @@ func TestWorkspaceWithRichParameters(t *testing.T) {
templateRichParameters, err := client.TemplateVersionRichParameters(ctx, version.ID)
require.NoError(t, err)
require.Len(t, templateRichParameters, 2)
require.Len(t, templateRichParameters, 3)
require.Equal(t, firstParameterName, templateRichParameters[0].Name)
require.Equal(t, firstParameterType, templateRichParameters[0].Type)
require.Equal(t, firstParameterDescription, templateRichParameters[0].Description)
require.Equal(t, firstParameterDescriptionPlaintext, templateRichParameters[0].DescriptionPlaintext)
require.Equal(t, codersdk.ValidationMonotonicOrder(""), templateRichParameters[0].ValidationMonotonic) // no validation for string
require.Equal(t, secondParameterName, templateRichParameters[1].Name)
require.Equal(t, secondParameterDisplayName, templateRichParameters[1].DisplayName)
require.Equal(t, secondParameterType, templateRichParameters[1].Type)
@@ -3588,9 +3610,18 @@ func TestWorkspaceWithRichParameters(t *testing.T) {
require.Equal(t, secondParameterDescriptionPlaintext, templateRichParameters[1].DescriptionPlaintext)
require.Equal(t, secondParameterValidationMonotonic, templateRichParameters[1].ValidationMonotonic)
third := templateRichParameters[2]
require.Equal(t, thirdParameterName, third.Name)
require.Equal(t, thirdParameterType, third.Type)
require.Equal(t, string(database.ParameterFormTypeMultiSelect), third.FormType)
require.Equal(t, thirdParameterDefault, third.DefaultValue)
require.Equal(t, thirdParameterOption, third.Options[0].Name)
require.Equal(t, thirdParameterOption, third.Options[0].Value)
expectedBuildParameters := []codersdk.WorkspaceBuildParameter{
{Name: firstParameterName, Value: firstParameterValue},
{Name: secondParameterName, Value: secondParameterValue},
{Name: thirdParameterName, Value: thirdParameterDefault},
}
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
@@ -3606,6 +3637,72 @@ func TestWorkspaceWithRichParameters(t *testing.T) {
require.ElementsMatch(t, expectedBuildParameters, workspaceBuildParameters)
}
func TestWorkspaceWithMultiSelectFailure(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, &echo.Responses{
Parse: echo.ParseComplete,
ProvisionPlan: []*proto.Response{
{
Type: &proto.Response_Plan{
Plan: &proto.PlanComplete{
Parameters: []*proto.RichParameter{
{
Name: "param",
Type: provider.OptionTypeListString,
DefaultValue: `["red"]`,
Options: []*proto.RichParameterOption{
{
Name: "red",
Value: "red",
},
},
FormType: proto.ParameterFormType_MULTISELECT,
},
},
},
},
},
},
ProvisionApply: []*proto.Response{{
Type: &proto.Response_Apply{
Apply: &proto.ApplyComplete{},
},
}},
})
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()
templateRichParameters, err := client.TemplateVersionRichParameters(ctx, version.ID)
require.NoError(t, err)
require.Len(t, templateRichParameters, 1)
expectedBuildParameters := []codersdk.WorkspaceBuildParameter{
// purple is not in the response set
{Name: "param", Value: `["red", "purple"]`},
}
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
req := codersdk.CreateWorkspaceRequest{
TemplateID: template.ID,
Name: coderdtest.RandomUsername(t),
AutostartSchedule: ptr.Ref("CRON_TZ=US/Central 30 9 * * 1-5"),
TTLMillis: ptr.Ref((8 * time.Hour).Milliseconds()),
AutomaticUpdates: codersdk.AutomaticUpdatesNever,
RichParameterValues: expectedBuildParameters,
}
_, err = client.CreateUserWorkspace(context.Background(), codersdk.Me, req)
require.Error(t, err)
var apiError *codersdk.Error
require.ErrorAs(t, err, &apiError)
require.Equal(t, http.StatusBadRequest, apiError.StatusCode())
}
func TestWorkspaceWithOptionalRichParameters(t *testing.T) {
t.Parallel()