Skip to content

Commit

Permalink
Merge branch 'main' of github.com:auth0/auth0-cli into main
Browse files Browse the repository at this point in the history
  • Loading branch information
Widcket committed Apr 2, 2021
2 parents 5a2bfab + b14a9e6 commit fcb9008
Show file tree
Hide file tree
Showing 3 changed files with 211 additions and 12 deletions.
53 changes: 52 additions & 1 deletion internal/cli/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package cli
import (
"fmt"

"github.com/AlecAivazis/survey/v2"
"github.com/auth0/auth0-cli/internal/prompt"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -55,6 +57,56 @@ func (f *Flag) SelectU(cmd *cobra.Command, value interface{}, options []string,
return selectFlag(cmd, f, value, options, defaultValue, true)
}

func (f *Flag) EditorPrompt(cmd *cobra.Command, value *string, initialValue, filename string, infoFn func()) error {
out, err := prompt.CaptureInputViaEditor(
initialValue,
filename,
infoFn,
)
if err != nil {
return err
}

*value = out
return nil
}

func (f *Flag) EditorPromptU(cmd *cobra.Command, value *string, initialValue, filename string, infoFn func()) error {
response := map[string]interface{}{}

questions := []*survey.Question{
{
Name: f.Name,
Prompt: &prompt.Editor{
BlankAllowed: true,
Editor: &survey.Editor{
Help: f.Help,
Message: f.Name,
FileName: filename,
Default: initialValue,
HideDefault: true,
AppendDefault: true,
},
},
},
}

if err := survey.Ask(questions, &response); err != nil {
return err
}

// Since we have BlankAllowed=true, an empty answer means we'll use the
// initialValue provided since this path is for the Update path.
answer, ok := response[f.Name].(string)
if ok && answer != "" {
*value = answer
} else {
*value = initialValue
}

return nil
}

func (f *Flag) RegisterString(cmd *cobra.Command, value *string, defaultValue string) {
registerString(cmd, f, value, defaultValue, false)
}
Expand Down Expand Up @@ -152,7 +204,6 @@ func shouldAsk(cmd *cobra.Command, f *Flag, isUpdate bool) bool {
if !f.IsRequired && !f.AlwaysPrompt {
return false
}

return shouldPromptWhenFlagless(cmd, f.LongForm)
}

Expand Down
35 changes: 24 additions & 11 deletions internal/cli/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (

"github.com/auth0/auth0-cli/internal/ansi"
"github.com/auth0/auth0-cli/internal/auth0"
"github.com/auth0/auth0-cli/internal/prompt"
"github.com/spf13/cobra"
"gopkg.in/auth0.v5/management"
)
Expand Down Expand Up @@ -39,6 +38,14 @@ var (
Help: "Enable (or disable) a rule.",
}

ruleScript = Flag{
Name: "Script",
LongForm: "script",
ShortForm: "s",
Help: "Script contents for the rule.",
IsRequired: true,
}

ruleTemplateOptions = pickerOptions{
{"Empty rule", ruleTemplateEmptyRule},
{"Add email to access token", ruleTemplateAddEmailToAccessToken},
Expand Down Expand Up @@ -101,6 +108,7 @@ func createRuleCmd(cli *cli) *cobra.Command {
var inputs struct {
Name string
Template string
Script string
Enabled bool
}

Expand Down Expand Up @@ -128,20 +136,20 @@ auth0 rules create -n "My Rule" -t "Empty rule" --enabled=false`,
// TODO(cyx): we can re-think this once we have
// `--stdin` based commands. For now we don't have
// those yet, so keeping this simple.
script, err := prompt.CaptureInputViaEditor(
err := ruleScript.EditorPrompt(
cmd,
&inputs.Script,
ruleTemplateOptions.getValue(inputs.Template),
inputs.Name+".*.js",
func() {
cli.renderer.Infof("%s once you close the editor, the rule will be saved. To cancel, CTRL+C.", ansi.Faint("Hint:"))
},
cli.ruleEditorHint,
)
if err != nil {
return fmt.Errorf("Failed to capture input from the editor: %w", err)
}

rule := &management.Rule{
Name: &inputs.Name,
Script: auth0.String(script),
Script: auth0.String(inputs.Script),
Enabled: &inputs.Enabled,
}

Expand Down Expand Up @@ -254,6 +262,7 @@ func updateRuleCmd(cli *cli) *cobra.Command {
var inputs struct {
ID string
Name string
Script string
Enabled bool
}

Expand Down Expand Up @@ -295,12 +304,12 @@ auth0 rules update <rule-id> -n "My Updated Rule" --enabled=false`,
// TODO(cyx): we can re-think this once we have
// `--stdin` based commands. For now we don't have
// those yet, so keeping this simple.
script, err := prompt.CaptureInputViaEditor(
err = ruleScript.EditorPromptU(
cmd,
&inputs.Script,
rule.GetScript(),
rule.GetName()+".*.js",
func() {
cli.renderer.Infof("%s once you close the editor, the rule will be saved. To cancel, CTRL+C.", ansi.Faint("Hint:"))
},
cli.ruleEditorHint,
)
if err != nil {
return fmt.Errorf("Failed to capture input from the editor: %w", err)
Expand All @@ -316,7 +325,7 @@ auth0 rules update <rule-id> -n "My Updated Rule" --enabled=false`,
// display.
rule = &management.Rule{
Name: &inputs.Name,
Script: &script,
Script: &inputs.Script,
Enabled: &inputs.Enabled,
}

Expand Down Expand Up @@ -356,3 +365,7 @@ func (c *cli) rulePickerOptions() (pickerOptions, error) {

return opts, nil
}

func (c *cli) ruleEditorHint() {
c.renderer.Infof("%s once you close the editor, the rule will be saved. To cancel, CTRL+C.", ansi.Faint("Hint:"))
}
135 changes: 135 additions & 0 deletions internal/prompt/surveyext.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package prompt

import (
"path/filepath"

"github.com/AlecAivazis/survey/v2"
"github.com/AlecAivazis/survey/v2/terminal"
)

// EXTENDED to enable different prompting behavior
type Editor struct {
*survey.Editor
EditorCommand string
BlankAllowed bool
}

func (e *Editor) editorCommand() string {
if e.EditorCommand == "" {
return getDefaultEditor()
}

return e.EditorCommand
}

// EXTENDED to change prompt text
var EditorQuestionTemplate = `
{{- if .ShowHelp }}{{- color .Config.Icons.Help.Format }}{{ .Config.Icons.Help.Text }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}}
{{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}}
{{- color "default+hb"}}{{ .Message }} {{color "reset"}}
{{- if .ShowAnswer}}
{{- color "cyan"}}{{.Answer}}{{color "reset"}}{{"\n"}}
{{- else }}
{{- if and .Help (not .ShowHelp)}}{{color "cyan"}}[{{ .Config.HelpInput }} for help]{{color "reset"}} {{end}}
{{- if and .Default (not .HideDefault)}}{{color "white"}}({{.Default}}) {{color "reset"}}{{end}}
{{- color "cyan"}}[(e) to launch {{ .EditorCommand }}{{- if .BlankAllowed }}, enter to skip{{ end }}] {{color "reset"}}
{{- end}}`

// EXTENDED to pass editor name (to use in prompt)
type EditorTemplateData struct {
survey.Editor
EditorCommand string
BlankAllowed bool
Answer string
ShowAnswer bool
ShowHelp bool
Config *survey.PromptConfig
}

// EXTENDED to augment prompt text and keypress handling
func (e *Editor) prompt(initialValue string, config *survey.PromptConfig) (interface{}, error) {
err := e.Render(
EditorQuestionTemplate,
// EXTENDED to support printing editor in prompt and BlankAllowed
EditorTemplateData{
Editor: *e.Editor,
BlankAllowed: e.BlankAllowed,
EditorCommand: filepath.Base(e.editorCommand()),
Config: config,
},
)
if err != nil {
return "", err
}

// start reading runes from the standard in
rr := e.NewRuneReader()
_ = rr.SetTermMode()
defer func() { _ = rr.RestoreTermMode() }()

cursor := e.NewCursor()
cursor.Hide()
defer cursor.Show()

for {
// EXTENDED to handle the e to edit / enter to skip behavior + BlankAllowed
r, _, err := rr.ReadRune()
if err != nil {
return "", err
}
if r == 'e' {
break
}
if r == '\r' || r == '\n' {
if e.BlankAllowed {
return "", nil
} else {
continue
}
}
if r == terminal.KeyInterrupt {
return "", terminal.InterruptErr
}
if r == terminal.KeyEndTransmission {
break
}
if string(r) == config.HelpInput && e.Help != "" {
err = e.Render(
EditorQuestionTemplate,
EditorTemplateData{
// EXTENDED to support printing editor in prompt, BlankAllowed
Editor: *e.Editor,
BlankAllowed: e.BlankAllowed,
EditorCommand: filepath.Base(e.editorCommand()),
ShowHelp: true,
Config: config,
},
)
if err != nil {
return "", err
}
}
continue
}

text, err := CaptureInputViaEditor(initialValue, e.FileName, nil)
if err != nil {
return "", err
}

// check length, return default value on empty
if len(text) == 0 && !e.AppendDefault {
return e.Default, nil
}

return text, nil
}

// EXTENDED This is straight copypasta from survey to get our overridden prompt called.
func (e *Editor) Prompt(config *survey.PromptConfig) (interface{}, error) {
initialValue := ""
if e.Default != "" && e.AppendDefault {
initialValue = e.Default
}
return e.prompt(initialValue, config)
}

0 comments on commit fcb9008

Please sign in to comment.