Skip to content

feat: form_type and styling metadata arguments added #375

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 35 commits into from
Apr 8, 2025
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
43253f7
feat: add form_type argument to parameters
Emyrk Mar 13, 2025
2d79a97
fix param order
Emyrk Mar 13, 2025
4bafcc6
add error type
Emyrk Mar 13, 2025
19d6749
apply default form_type to state
Emyrk Mar 13, 2025
9fc221c
fix parsing form_type from terraform
Emyrk Mar 14, 2025
b80ab8f
chore: multi select form control allows options to be string
Emyrk Mar 14, 2025
50c6a0d
chore: add slider and 'form_type_metadata' argument to parameter
Emyrk Mar 17, 2025
c0d38d3
move code block
Emyrk Mar 17, 2025
0b94955
fixup error message
Emyrk Mar 20, 2025
05e682d
add invalid boolean validation field
Emyrk Mar 20, 2025
0dfab58
tag-input -> tag-select
Emyrk Mar 24, 2025
e971837
chore: use constants over string literals for option type
Emyrk Apr 3, 2025
12aec75
remove invalid option
Emyrk Apr 3, 2025
0b28b64
fixup! remove invalid option
Emyrk Apr 3, 2025
bf2b673
testing framework for form_type
Emyrk Apr 7, 2025
c0be472
polishig up extra code and syntax
Emyrk Apr 7, 2025
cd52cae
finish all table test cases
Emyrk Apr 7, 2025
fec030d
final touches on the test
Emyrk Apr 7, 2025
f0bca91
also assert styling
Emyrk Apr 7, 2025
18c3d05
add a test case for invalid multi selecT
Emyrk Apr 7, 2025
a04ffa6
push up an example multi select
Emyrk Apr 7, 2025
2b5ff08
make gne
Emyrk Apr 7, 2025
18886f8
make fmt
Emyrk Apr 7, 2025
acf5977
use comment instead of json
Emyrk Apr 7, 2025
fcdd6bd
make gen
Emyrk Apr 7, 2025
5da70d5
spelling typo
Emyrk Apr 7, 2025
8356ff7
Merge branch 'main' into stevenmasley/form_control
aslilac Apr 7, 2025
771be40
mark the check up front
Emyrk Apr 7, 2025
bb00c25
cleanup output
Emyrk Apr 7, 2025
d25fcf6
add test dupe check up front
Emyrk Apr 7, 2025
8951940
adding the example before release
Emyrk Apr 7, 2025
5d485d6
cleanup test logs
Emyrk Apr 7, 2025
f21c732
add comment
Emyrk Apr 7, 2025
f110129
renaming from PR comments
Emyrk Apr 8, 2025
d330f21
make struct field types explicit
Emyrk Apr 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/data-sources/parameter.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,12 @@ data "coder_parameter" "home_volume_size" {
- `description` (String) Describe what this parameter does.
- `display_name` (String) The displayed name of the parameter as it will appear in the interface.
- `ephemeral` (Boolean) The value of an ephemeral parameter will not be preserved between consecutive workspace builds.
- `form_type` (String) The type of this parameter. Must be one of: [radio, slider, input, dropdown, checkbox, switch, multi-select, tag-select, textarea, error].
- `icon` (String) A URL to an icon that will display in the dashboard. View built-in icons [here](https://github.com/coder/coder/tree/main/site/static/icon). Use a built-in icon with `"${data.coder_workspace.me.access_url}/icon/<path>"`.
- `mutable` (Boolean) Whether this value can be changed after workspace creation. This can be destructive for values like region, so use with caution!
- `option` (Block List) Each `option` block defines a value for a user to select from. (see [below for nested schema](#nestedblock--option))
- `order` (Number) The order determines the position of a template parameter in the UI/CLI presentation. The lowest order is shown first and parameters with equal order are sorted by name (ascending order).
- `styling` (String) JSON encoded string containing the metadata for controlling the appearance of this parameter in the UI. This option is purely cosmetic and does not affect the function of the parameter in terraform.
- `type` (String) The type of this parameter. Must be one of: `"number"`, `"string"`, `"bool"`, or `"list(string)"`.
- `validation` (Block List, Max: 1) Validate the input of a parameter. (see [below for nested schema](#nestedblock--validation))

Expand Down
148 changes: 148 additions & 0 deletions provider/formtype.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package provider

import (
"slices"

"golang.org/x/xerrors"
)

// OptionType is a type of option that can be used in the 'type' argument of
// a parameter. These should match types as defined in terraform:
//
// https://developer.hashicorp.com/terraform/language/expressions/types
//
// The value have to be string literals, as type constraint keywords are not
// supported in providers. :'(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For future readers wishing to understand the source of our woes, it might be good to add some relevant links to existing issues.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually do not know what to link 🤔.

type OptionType string

const (
OptionTypeString OptionType = "string"
OptionTypeNumber OptionType = "number"
OptionTypeBoolean OptionType = "bool"
OptionTypeListString OptionType = "list(string)"
)

func OptionTypes() []OptionType {
return []OptionType{
OptionTypeString,
OptionTypeNumber,
OptionTypeBoolean,
OptionTypeListString,
}
}

// ParameterFormType is the list of supported form types for display in
// the Coder "create workspace" form. These form types are functional as well
// as cosmetic.
// For example, "multi-select" has the type "list(string)" but the option
// values are "string".
Comment on lines +37 to +38
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be good to explicitly document each type + option value for the possible values of ParameterFormType below.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather defer that to the truth table below. I do not want to document it in 2 places.

type ParameterFormType string

const (
ParameterFormTypeDefault ParameterFormType = ""
ParameterFormTypeRadio ParameterFormType = "radio"
ParameterFormTypeSlider ParameterFormType = "slider"
ParameterFormTypeInput ParameterFormType = "input"
ParameterFormTypeDropdown ParameterFormType = "dropdown"
ParameterFormTypeCheckbox ParameterFormType = "checkbox"
ParameterFormTypeSwitch ParameterFormType = "switch"
ParameterFormTypeMultiSelect ParameterFormType = "multi-select"
ParameterFormTypeTagSelect ParameterFormType = "tag-select"
ParameterFormTypeTextArea ParameterFormType = "textarea"
ParameterFormTypeError ParameterFormType = "error"
)

// ParameterFormTypes should be kept in sync with the enum list above.
func ParameterFormTypes() []ParameterFormType {
return []ParameterFormType{
// Intentionally omit "ParameterFormTypeDefault" from this set.
// It is a valid enum, but will always be mapped to a real value when
// being used.
ParameterFormTypeRadio,
ParameterFormTypeSlider,
ParameterFormTypeInput,
ParameterFormTypeDropdown,
ParameterFormTypeCheckbox,
ParameterFormTypeSwitch,
ParameterFormTypeMultiSelect,
ParameterFormTypeTagSelect,
ParameterFormTypeTextArea,
ParameterFormTypeError,
}
}

// formTypeTruthTable is a map of [`type`][`optionCount` > 0] to `form_type`.
// The first value in the slice is the default value assuming `form_type` is
// not specified.
//
// The boolean key indicates whether the `options` field is specified.
// | Type | Options | Specified Form Type | form_type | Notes |
// |-------------------|---------|---------------------|----------------|--------------------------------|
// | `string` `number` | Y | | `radio` | |
// | `string` `number` | Y | `dropdown` | `dropdown` | |
// | `string` `number` | N | | `input` | |
// | `string` | N | 'textarea' | `textarea` | |
// | `number` | N | 'slider' | `slider` | min/max validation |
// | `bool` | Y | | `radio` | |
// | `bool` | N | | `checkbox` | |
// | `bool` | N | `switch` | `switch` | |
// | `list(string)` | Y | | `radio` | |
// | `list(string)` | N | | `tag-select` | |
// | `list(string)` | Y | `multi-select` | `multi-select` | Option values will be `string` |
var formTypeTruthTable = map[OptionType]map[bool][]ParameterFormType{
OptionTypeString: {
true: {ParameterFormTypeRadio, ParameterFormTypeDropdown},
false: {ParameterFormTypeInput, ParameterFormTypeTextArea},
},
OptionTypeNumber: {
true: {ParameterFormTypeRadio, ParameterFormTypeDropdown},
false: {ParameterFormTypeInput, ParameterFormTypeSlider},
},
OptionTypeBoolean: {
true: {ParameterFormTypeRadio},
false: {ParameterFormTypeCheckbox, ParameterFormTypeSwitch},
},
OptionTypeListString: {
true: {ParameterFormTypeRadio, ParameterFormTypeMultiSelect},
false: {ParameterFormTypeTagSelect},
},
}

// ValidateFormType handles the truth table for the valid set of `type` and
// `form_type` options.
// The OptionType is also returned because it is possible the 'type' of the
// 'value' & 'default' fields is different from the 'type' of the options.
// The use case is when using multi-select. The options are 'string' and the
// value is 'list(string)'.
func ValidateFormType(paramType OptionType, optionCount int, specifiedFormType ParameterFormType) (OptionType, ParameterFormType, error) {
allowed, ok := formTypeTruthTable[paramType][optionCount > 0]
if !ok || len(allowed) == 0 {
return paramType, specifiedFormType, xerrors.Errorf("value type %q is not supported for 'form_types'", paramType)
}

if specifiedFormType == ParameterFormTypeDefault {
// handle the default case
specifiedFormType = allowed[0]
}

if !slices.Contains(allowed, specifiedFormType) {
return paramType, specifiedFormType, xerrors.Errorf("value type %q is not supported for 'form_types'", specifiedFormType)
}

// This is the only current special case. If 'multi-select' is selected, the type
// of 'value' and an options 'value' are different. The type of the parameter is
// `list(string)` but the type of the individual options is `string`.
if paramType == OptionTypeListString && specifiedFormType == ParameterFormTypeMultiSelect {
return OptionTypeString, ParameterFormTypeMultiSelect, nil
}

return paramType, specifiedFormType, nil
}

func toStrings[A ~string](l []A) []string {
var r []string
for _, v := range l {
r = append(r, string(v))
}
return r
}
Loading
Loading