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:
+36
-12
@@ -1,9 +1,12 @@
|
||||
package codersdk
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
"tailscale.com/types/ptr"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/util/slice"
|
||||
"github.com/coder/terraform-provider-coder/v2/provider"
|
||||
)
|
||||
|
||||
@@ -66,18 +69,8 @@ func validateBuildParameter(richParameter TemplateVersionParameter, buildParamet
|
||||
current = richParameter.DefaultValue
|
||||
}
|
||||
|
||||
if len(richParameter.Options) > 0 {
|
||||
var matched bool
|
||||
for _, opt := range richParameter.Options {
|
||||
if opt.Value == current {
|
||||
matched = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !matched {
|
||||
return xerrors.Errorf("parameter value must match one of options: %s", parameterValuesAsArray(richParameter.Options))
|
||||
}
|
||||
if len(richParameter.Options) > 0 && !inOptionSet(richParameter, current) {
|
||||
return xerrors.Errorf("parameter value must match one of options: %s", parameterValuesAsArray(richParameter.Options))
|
||||
}
|
||||
|
||||
if !validationEnabled(richParameter) {
|
||||
@@ -104,6 +97,37 @@ func validateBuildParameter(richParameter TemplateVersionParameter, buildParamet
|
||||
return validation.Valid(richParameter.Type, current, previous)
|
||||
}
|
||||
|
||||
// inOptionSet returns if the value given is in the set of options for a parameter.
|
||||
func inOptionSet(richParameter TemplateVersionParameter, value string) bool {
|
||||
optionValues := make([]string, 0, len(richParameter.Options))
|
||||
for _, option := range richParameter.Options {
|
||||
optionValues = append(optionValues, option.Value)
|
||||
}
|
||||
|
||||
// If the type is `list(string)` and the form_type is `multi-select`, then we check each individual
|
||||
// value in the list against the option set.
|
||||
isMultiSelect := richParameter.Type == provider.OptionTypeListString && richParameter.FormType == string(provider.ParameterFormTypeMultiSelect)
|
||||
|
||||
if !isMultiSelect {
|
||||
// This is the simple case. Just checking if the value is in the option set.
|
||||
return slice.Contains(optionValues, value)
|
||||
}
|
||||
|
||||
var checks []string
|
||||
err := json.Unmarshal([]byte(value), &checks)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, check := range checks {
|
||||
if !slice.Contains(optionValues, check) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func findBuildParameter(params []WorkspaceBuildParameter, parameterName string) (*WorkspaceBuildParameter, bool) {
|
||||
if params == nil {
|
||||
return nil, false
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
package codersdk
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/terraform-provider-coder/v2/provider"
|
||||
)
|
||||
|
||||
func Test_inOptionSet(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
options := func(vals ...string) []TemplateVersionParameterOption {
|
||||
opts := make([]TemplateVersionParameterOption, 0, len(vals))
|
||||
for _, val := range vals {
|
||||
opts = append(opts, TemplateVersionParameterOption{
|
||||
Name: val,
|
||||
Value: val,
|
||||
})
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
param TemplateVersionParameter
|
||||
value string
|
||||
want bool
|
||||
}{
|
||||
// The function should never be called with 0 options, but if it is,
|
||||
// it should always return false.
|
||||
{
|
||||
name: "empty",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "no-options",
|
||||
param: TemplateVersionParameter{
|
||||
Options: make([]TemplateVersionParameterOption, 0),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no-options-multi",
|
||||
param: TemplateVersionParameter{
|
||||
Type: provider.OptionTypeListString,
|
||||
FormType: string(provider.ParameterFormTypeMultiSelect),
|
||||
Options: make([]TemplateVersionParameterOption, 0),
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "no-options-list(string)",
|
||||
param: TemplateVersionParameter{
|
||||
Type: provider.OptionTypeListString,
|
||||
FormType: "",
|
||||
Options: make([]TemplateVersionParameterOption, 0),
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "list(string)-no-form",
|
||||
param: TemplateVersionParameter{
|
||||
Type: provider.OptionTypeListString,
|
||||
FormType: "",
|
||||
Options: options("red", "green", "blue"),
|
||||
},
|
||||
want: false,
|
||||
value: `["red", "blue", "green"]`,
|
||||
},
|
||||
// now for some reasonable values
|
||||
{
|
||||
name: "list(string)-multi",
|
||||
param: TemplateVersionParameter{
|
||||
Type: provider.OptionTypeListString,
|
||||
FormType: string(provider.ParameterFormTypeMultiSelect),
|
||||
Options: options("red", "green", "blue"),
|
||||
},
|
||||
want: true,
|
||||
value: `["red", "blue", "green"]`,
|
||||
},
|
||||
{
|
||||
name: "string with json",
|
||||
param: TemplateVersionParameter{
|
||||
Type: provider.OptionTypeString,
|
||||
Options: options(`["red","blue","green"]`, `["red","orange"]`),
|
||||
},
|
||||
want: true,
|
||||
value: `["red","blue","green"]`,
|
||||
},
|
||||
{
|
||||
name: "string",
|
||||
param: TemplateVersionParameter{
|
||||
Type: provider.OptionTypeString,
|
||||
Options: options("red", "green", "blue"),
|
||||
},
|
||||
want: true,
|
||||
value: "red",
|
||||
},
|
||||
// False values
|
||||
{
|
||||
name: "list(string)-multi",
|
||||
param: TemplateVersionParameter{
|
||||
Type: provider.OptionTypeListString,
|
||||
FormType: string(provider.ParameterFormTypeMultiSelect),
|
||||
Options: options("red", "green", "blue"),
|
||||
},
|
||||
want: false,
|
||||
value: `["red", "blue", "purple"]`,
|
||||
},
|
||||
{
|
||||
name: "string with json",
|
||||
param: TemplateVersionParameter{
|
||||
Type: provider.OptionTypeString,
|
||||
Options: options(`["red","blue"]`, `["red","orange"]`),
|
||||
},
|
||||
want: false,
|
||||
value: `["red","blue","green"]`,
|
||||
},
|
||||
{
|
||||
name: "string",
|
||||
param: TemplateVersionParameter{
|
||||
Type: provider.OptionTypeString,
|
||||
Options: options("red", "green", "blue"),
|
||||
},
|
||||
want: false,
|
||||
value: "purple",
|
||||
},
|
||||
{
|
||||
name: "list(string)-multi-scalar-value",
|
||||
param: TemplateVersionParameter{
|
||||
Type: provider.OptionTypeListString,
|
||||
FormType: string(provider.ParameterFormTypeMultiSelect),
|
||||
Options: options("red", "green", "blue"),
|
||||
},
|
||||
want: false,
|
||||
value: "green",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got := inOptionSet(tt.param, tt.value)
|
||||
require.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -54,22 +54,25 @@ const (
|
||||
|
||||
// TemplateVersionParameter represents a parameter for a template version.
|
||||
type TemplateVersionParameter struct {
|
||||
Name string `json:"name"`
|
||||
DisplayName string `json:"display_name,omitempty"`
|
||||
Description string `json:"description"`
|
||||
DescriptionPlaintext string `json:"description_plaintext"`
|
||||
Type string `json:"type" enums:"string,number,bool,list(string)"`
|
||||
Mutable bool `json:"mutable"`
|
||||
DefaultValue string `json:"default_value"`
|
||||
Icon string `json:"icon"`
|
||||
Options []TemplateVersionParameterOption `json:"options"`
|
||||
ValidationError string `json:"validation_error,omitempty"`
|
||||
ValidationRegex string `json:"validation_regex,omitempty"`
|
||||
ValidationMin *int32 `json:"validation_min,omitempty"`
|
||||
ValidationMax *int32 `json:"validation_max,omitempty"`
|
||||
ValidationMonotonic ValidationMonotonicOrder `json:"validation_monotonic,omitempty" enums:"increasing,decreasing"`
|
||||
Required bool `json:"required"`
|
||||
Ephemeral bool `json:"ephemeral"`
|
||||
Name string `json:"name"`
|
||||
DisplayName string `json:"display_name,omitempty"`
|
||||
Description string `json:"description"`
|
||||
DescriptionPlaintext string `json:"description_plaintext"`
|
||||
Type string `json:"type" enums:"string,number,bool,list(string)"`
|
||||
// FormType has an enum value of empty string, `""`.
|
||||
// Keep the leading comma in the enums struct tag.
|
||||
FormType string `json:"form_type" enums:",radio,dropdown,input,textarea,slider,checkbox,switch,tag-select,multi-select,error"`
|
||||
Mutable bool `json:"mutable"`
|
||||
DefaultValue string `json:"default_value"`
|
||||
Icon string `json:"icon"`
|
||||
Options []TemplateVersionParameterOption `json:"options"`
|
||||
ValidationError string `json:"validation_error,omitempty"`
|
||||
ValidationRegex string `json:"validation_regex,omitempty"`
|
||||
ValidationMin *int32 `json:"validation_min,omitempty"`
|
||||
ValidationMax *int32 `json:"validation_max,omitempty"`
|
||||
ValidationMonotonic ValidationMonotonicOrder `json:"validation_monotonic,omitempty" enums:"increasing,decreasing"`
|
||||
Required bool `json:"required"`
|
||||
Ephemeral bool `json:"ephemeral"`
|
||||
}
|
||||
|
||||
// TemplateVersionParameterOption represents a selectable option for a template parameter.
|
||||
|
||||
Reference in New Issue
Block a user