mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
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:
Generated
+17
@@ -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"
|
||||
},
|
||||
|
||||
Generated
+17
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)),
|
||||
|
||||
@@ -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,
|
||||
|
||||
Generated
+19
@@ -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.';
|
||||
@@ -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 {
|
||||
|
||||
@@ -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{})
|
||||
|
||||
@@ -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 = ¶m.ValidationMin.Int32
|
||||
}
|
||||
if param.ValidationMax.Valid {
|
||||
validationMax = ¶m.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 {
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user