From a8a79e08e5124a29137861169b32bb2c6cd0b9b7 Mon Sep 17 00:00:00 2001 From: Luke Kingland Date: Tue, 8 Feb 2022 08:28:27 +0900 Subject: [PATCH] fix: defaults for prompt from flags then options --- cmd/create.go | 2 +- cmd/invoke.go | 6 +++--- cmd/root.go | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 4 deletions(-) diff --git a/cmd/create.go b/cmd/create.go index 8ef2d74001..48bbc3c61a 100644 --- a/cmd/create.go +++ b/cmd/create.go @@ -446,7 +446,7 @@ func (c createConfig) prompt(client *fn.Client) (createConfig, error) { Prompt: &survey.Select{ Message: "Language Runtime:", Options: runtimes, - Default: runtimes[0], + Default: surveySelectDefault(c.Runtime, runtimes), }, }} if err := survey.Ask(qs, &c); err != nil { diff --git a/cmd/invoke.go b/cmd/invoke.go index 6d64a0472a..52b8c1ca45 100644 --- a/cmd/invoke.go +++ b/cmd/invoke.go @@ -306,7 +306,7 @@ func (c invokeConfig) prompt(client *fn.Client) (invokeConfig, error) { if err := survey.Ask(qs, &c); err != nil { return c, err } - + formatOptions := []string{"", "http", "cloudevent"} qs = []*survey.Question{ { Name: "Target", @@ -319,8 +319,8 @@ func (c invokeConfig) prompt(client *fn.Client) (invokeConfig, error) { Name: "Format", Prompt: &survey.Select{ Message: "(Optional) Format Override", - Options: []string{"", "http", "cloudevent"}, - Default: c.Format, + Options: formatOptions, + Default: surveySelectDefault(c.Format, formatOptions), }, }, } diff --git a/cmd/root.go b/cmd/root.go index fdbd86bdde..4f9b609b70 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -358,3 +358,61 @@ func (v Version) String() string { } } } + +// surveySelectDefault returns 'value' if defined and exists in 'options'. +// Otherwise, options[0] is returned if it exists. Empty string otherwise. +// +// Usage Example: +// +// languages := []string{ "go", "node", "rust" }, +// survey.Select{ +// Options: options, +// Default: surveySelectDefaut(cfg.Language, languages), +// } +// +// Summary: +// +// This protects against an incorrectly initialized survey.Select when the user +// has provided a nonexistant option (validation is handled elsewhere) or +// when a value is required but there exists no defaults (no default value on +// the associated flag). +// +// Explanation: +// +// The above example chooses the default for the Survey (--confirm) question +// in a way that works with user-provided flag and environment variable values. +// `cfg.Language` is the current value set in the config struct, which is +// populated from (in ascending order of precedence): +// static flag default, associated environment variable, or command flag. +// `languages` are the options which are being used by the survey select. +// +// This cascade allows for the Survey questions to be properly pre-initialzed +// with their associated environment variables or flags. For example, +// A user whose default language is set to 'node' using the global environment +// variable FUNC_LANGUAGE will have that option pre-selected when running +// `func create -c`. +// +// The 'survey' package expects the value of the Default member to exist +// in the 'Options' member. This is not possible when user-provided data is +// allowed for the default, hence this logic is necessary. +// +// For example, when the user is using prompts (--confirm) to select from a set +// of options, but the associated flag either has an unrecognized value, or no +// value at all, without this logic the resulting select prompt would be +// initialized with this as the default value, and the act of what appears to +// be choose the first option displayed does not overwrite the invalid default. +// It could perhaps be argued this is a shortcoming in the survey package, but +// it is also clearly an error to provide invalid data for a default. +func surveySelectDefault(value string, options []string) string { + for _, v := range options { + if value == v { + return v // The provided value is acceptable + } + } + if len(options) > 0 { + return options[0] // Sync with the option which will be shown by the UX + } + // Either the value is not an option or there are no options. Either of + // which should fail proper validation + return "" +}