Skip to content
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

Add enum support for bundle templates #668

Merged
merged 18 commits into from
Sep 8, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
54 changes: 54 additions & 0 deletions libs/cmdio/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"strings"

"github.com/databricks/cli/libs/flags"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
shreyas-goenka marked this conversation as resolved.
Show resolved Hide resolved
)

// This is the interface for all io interactions with a user
Expand Down Expand Up @@ -104,6 +106,58 @@ func AskYesOrNo(ctx context.Context, question string) (bool, error) {
return false, nil
}

func AskChoice(ctx context.Context, question string, defaultVal string, choices []string) (string, error) {
logger, ok := FromContext(ctx)
if !ok {
logger = Default()
}

// Find index of the default value
defaultIndex := ""
for i, v := range choices {
if v == defaultVal {
defaultIndex = fmt.Sprint(i + 1)
break
}
}

// If default value is not present, return error
if defaultIndex == "" {
return "", fmt.Errorf("failed to find default value %q among choices: %#v", defaultVal, choices)
}

indexToChoice := make(map[string]string, 0)
question += ":\n"
for index, choice := range choices {
// Map choices against a string representation of their indices.
// This helps resolve the choice corresponding to the index the user enters.
choiceIndex := fmt.Sprint(index + 1)
indexToChoice[choiceIndex] = choice

// Add this choice as a option in the prompt text.
question += fmt.Sprintf("%s. %s\n", choiceIndex, choice)

}

// Add text informing user of the list of valid options to choose from
question += fmt.Sprintf("Choose from %s", strings.Join(maps.Keys(indexToChoice), ", "))
shreyas-goenka marked this conversation as resolved.
Show resolved Hide resolved

// prompt the user.
ans, err := logger.Ask(question, defaultIndex)
if err != nil {
return "", err
}

choice, ok := indexToChoice[ans]
if !ok {
expectedOptions := maps.Keys(indexToChoice)
slices.Sort(expectedOptions)
return "", fmt.Errorf("expected one of %s. Got: %s", strings.Join(expectedOptions, ", "), ans)
}

return choice, nil
}

func (l *Logger) Ask(question string, defaultVal string) (string, error) {
if l.Mode == flags.ModeJson {
return "", fmt.Errorf("question prompts are not supported in json mode")
Expand Down
18 changes: 18 additions & 0 deletions libs/cmdio/logger_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cmdio

import (
"context"
"testing"

"github.com/databricks/cli/libs/flags"
Expand All @@ -12,3 +13,20 @@ func TestAskFailedInJsonMode(t *testing.T) {
_, err := l.Ask("What is your spirit animal?", "")
assert.ErrorContains(t, err, "question prompts are not supported in json mode")
}

func TestAskChoiceFailsInJsonMode(t *testing.T) {
l := NewLogger(flags.ModeJson)
ctx := NewContext(context.Background(), l)

_, err := AskChoice(ctx, "what is a question?", "a", []string{"b", "c", "a"})
assert.EqualError(t, err, "question prompts are not supported in json mode")
}

func TestAskChoiceDefaultValueAbsent(t *testing.T) {
l := NewLogger(flags.ModeJson)
ctx := NewContext(context.Background(), l)

// Expect error that default value is missing from choices.
_, err := AskChoice(ctx, "what is a question?", "a", []string{"b", "c", "d"})
assert.EqualError(t, err, "failed to find default value \"a\" among choices: []string{\"b\", \"c\", \"d\"}")
}
4 changes: 4 additions & 0 deletions libs/jsonschema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ type Schema struct {

// Default value for the property / object
Default any `json:"default,omitempty"`

// If specified this is the list of valid values for the object. Values
// here should be consistent with the type defined in the schema.
Enum []any `json:"enum,omitempty"`
shreyas-goenka marked this conversation as resolved.
Show resolved Hide resolved
}

type Type string
Expand Down
24 changes: 21 additions & 3 deletions libs/template/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,27 @@ func (c *config) promptForValues() error {
}

// Get user input by running the prompt
userInput, err := cmdio.Ask(c.ctx, property.Description, defaultVal)
if err != nil {
return err
var userInput string
if property.Enum != nil {
// convert list of enums to string
shreyas-goenka marked this conversation as resolved.
Show resolved Hide resolved
enums := []string{}
for _, enum := range property.Enum {
v, err := toString(enum, property.Type)
if err != nil {
return err
}
enums = append(enums, v)
}
userInput, err = cmdio.AskChoice(c.ctx, property.Description, defaultVal, enums)
if err != nil {
return err
}
} else {
userInput, err = cmdio.Ask(c.ctx, property.Description, defaultVal)
if err != nil {
return err
}

}

// Convert user input string back to a value
Expand Down