From e1e926b4b11ad5e104b0f7f1337c959e8bbe1aee Mon Sep 17 00:00:00 2001 From: Rita Zerrizuela Date: Wed, 27 Jan 2021 00:19:55 -0300 Subject: [PATCH 1/4] Make interactivity tty-based for APIs --- go.mod | 1 + internal/ansi/ansi.go | 13 ++-- internal/cli/apis.go | 136 ++++++++++++++++++++++++++++-------- internal/cli/cli.go | 18 +++++ internal/cli/clients.go | 2 +- internal/cli/connections.go | 2 +- internal/cli/rules.go | 2 +- internal/display/apis.go | 5 ++ internal/display/display.go | 2 +- internal/prompt/prompt.go | 8 ++- 10 files changed, 143 insertions(+), 46 deletions(-) diff --git a/go.mod b/go.mod index 44c92913e..e57964e91 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/jsanda/tablewriter v0.0.2-0.20190614032957-c4e45dc9c708 github.com/logrusorgru/aurora v2.0.3+incompatible github.com/mattn/go-colorable v0.1.6 // indirect + github.com/mattn/go-isatty v0.0.12 github.com/mattn/go-runewidth v0.0.10 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/spf13/cobra v1.0.0 diff --git a/internal/ansi/ansi.go b/internal/ansi/ansi.go index 3ab65b763..e8b456142 100644 --- a/internal/ansi/ansi.go +++ b/internal/ansi/ansi.go @@ -6,8 +6,8 @@ import ( "os" "github.com/logrusorgru/aurora" + "github.com/mattn/go-isatty" "github.com/tidwall/pretty" - "golang.org/x/crypto/ssh/terminal" ) var darkTerminalStyle = &pretty.Style{ @@ -100,17 +100,12 @@ func StrikeThrough(text string) string { return color.Sprintf(color.StrikeThrough(text)) } -func isTerminal(w io.Writer) bool { - switch v := w.(type) { - case *os.File: - return terminal.IsTerminal(int(v.Fd())) - default: - return false - } +func IsTerminal() bool { + return isatty.IsTerminal(os.Stdout.Fd()) } func shouldUseColors(w io.Writer) bool { - useColors := ForceColors || isTerminal(w) + useColors := ForceColors || IsTerminal() if EnvironmentOverrideColors { force, ok := os.LookupEnv("CLICOLOR_FORCE") diff --git a/internal/cli/apis.go b/internal/cli/apis.go index eeeda6430..a6a478861 100644 --- a/internal/cli/apis.go +++ b/internal/cli/apis.go @@ -1,21 +1,28 @@ package cli import ( - "github.com/AlecAivazis/survey/v2" "github.com/auth0/auth0-cli/internal/ansi" "github.com/auth0/auth0-cli/internal/prompt" "github.com/spf13/cobra" "gopkg.in/auth0.v5/management" ) +const ( + id = "id" + name = "name" + identifier = "identifier" +) + func apisCmd(cli *cli) *cobra.Command { cmd := &cobra.Command{ Use: "apis", Short: "manage resources for APIs.", + Aliases: []string{"resource-servers"}, } cmd.SetUsageTemplate(resourceUsageTemplate()) cmd.AddCommand(listApisCmd(cli)) + cmd.AddCommand(showApiCmd(cli)) cmd.AddCommand(createApiCmd(cli)) cmd.AddCommand(updateApiCmd(cli)) cmd.AddCommand(deleteApiCmd(cli)) @@ -35,9 +42,8 @@ Lists your existing APIs. To create one try: RunE: func(cmd *cobra.Command, args []string) error { var list *management.ResourceServerList - err := ansi.Spinner("Getting APIs", func() error { + err := ansi.Spinner("Loading APIs", func() error { var err error - list, err = cli.api.ResourceServer.List() return err }) @@ -54,6 +60,53 @@ Lists your existing APIs. To create one try: return cmd } +func showApiCmd(cli *cli) *cobra.Command { + var flags struct { + ID string + } + + cmd := &cobra.Command{ + Use: "show", + Short: "Show an API", + Long: `Shows an API: + +auth0 apis show --id id +`, + PreRun: func(cmd *cobra.Command, args []string) { + prepareInteractivity(cmd) + }, + RunE: func(cmd *cobra.Command, args []string) error { + if shouldPrompt(cmd, id) { + input := prompt.TextInput(id, "Id:", "Id of the API.", "", true) + + if err := prompt.AskOne(input, &flags); err != nil { + return err + } + } + + api := &management.ResourceServer{ID: &flags.ID} + + err := ansi.Spinner("Loading API", func() error { + var err error + api, err = cli.api.ResourceServer.Read(flags.ID) + return err + }) + + if err != nil { + return err + } + + cli.renderer.ApiShow(api) + return nil + }, + } + + cmd.Flags().StringVarP(&flags.ID, id, "i", "", "ID of the API.") + mustRequireFlags(cmd, id) + + return cmd +} + func createApiCmd(cli *cli) *cobra.Command { var flags struct { Name string @@ -67,24 +120,30 @@ func createApiCmd(cli *cli) *cobra.Command { auth0 apis create --name myapi --identifier http://my-api `, - PreRun: func(cmd *cobra.Command, args []string) { - checkFlags(cmd) + PreRun: func(cmd *cobra.Command, args []string) { + prepareInteractivity(cmd) }, RunE: func(cmd *cobra.Command, args []string) error { - if !hasFlags(cmd) { - name := prompt.TextInput( - "name", "Name:", + if shouldPrompt(cmd, name) { + input := prompt.TextInput( + name, "Name:", "Name of the API. You can change the API name later in the API settings.", "", true) - identifier := prompt.TextInput( - "identifier", "Identifier:", + if err := prompt.AskOne(input, &flags); err != nil { + return err + } + } + + if shouldPrompt(cmd, identifier) { + input := prompt.TextInput( + identifier, "Identifier:", "Identifier of the API. Cannot be changed once set.", "", true) - if err := prompt.Ask([]*survey.Question {name, identifier}, &flags); err != nil { + if err := prompt.AskOne(input, &flags); err != nil { return err } } @@ -107,10 +166,9 @@ auth0 apis create --name myapi --identifier http://my-api }, } - cmd.Flags().StringVarP(&flags.Name, "name", "n", "", "Name of the API.") - cmd.Flags().StringVarP(&flags.Identifier, "identifier", "i", "", "Identifier of the API.") - - mustRequireFlags(cmd, "name", "identifier") + cmd.Flags().StringVarP(&flags.Name, name, "n", "", "Name of the API.") + cmd.Flags().StringVarP(&flags.Identifier, identifier, "i", "", "Identifier of the API.") + mustRequireFlags(cmd, name, identifier) return cmd } @@ -128,15 +186,22 @@ func updateApiCmd(cli *cli) *cobra.Command { auth0 apis update --id id --name myapi `, - PreRun: func(cmd *cobra.Command, args []string) { - checkFlags(cmd) + PreRun: func(cmd *cobra.Command, args []string) { + prepareInteractivity(cmd) }, RunE: func(cmd *cobra.Command, args []string) error { - if !hasFlags(cmd) { - id := prompt.TextInput("id", "Id:", "Id of the API.", "", true) - name := prompt.TextInput("name", "Name:", "Name of the API.", "", true) + if shouldPrompt(cmd, id) { + input := prompt.TextInput(id, "Id:", "Id of the API.", "", true) - if err := prompt.Ask([]*survey.Question {id, name}, &flags); err != nil { + if err := prompt.AskOne(input, &flags); err != nil { + return err + } + } + + if shouldPrompt(cmd, name) { + input := prompt.TextInput(name, "Name:", "Name of the API.", "", true) + + if err := prompt.AskOne(input, &flags); err != nil { return err } } @@ -156,17 +221,16 @@ auth0 apis update --id id --name myapi }, } - cmd.Flags().StringVarP(&flags.ID, "id", "i", "", "ID of the API.") - cmd.Flags().StringVarP(&flags.Name, "name", "n", "", "Name of the API.") - - mustRequireFlags(cmd, "id", "name") + cmd.Flags().StringVarP(&flags.ID, id, "i", "", "ID of the API.") + cmd.Flags().StringVarP(&flags.Name, name, "n", "", "Name of the API.") + mustRequireFlags(cmd, id, name) return cmd } func deleteApiCmd(cli *cli) *cobra.Command { var flags struct { - id string + ID string force bool } @@ -177,15 +241,26 @@ func deleteApiCmd(cli *cli) *cobra.Command { auth0 apis delete --id id --force `, + PreRun: func(cmd *cobra.Command, args []string) { + prepareInteractivity(cmd) + }, RunE: func(cmd *cobra.Command, args []string) error { - if !flags.force { + if shouldPrompt(cmd, id) { + input := prompt.TextInput(id, "Id:", "Id of the API.", "", true) + + if err := prompt.AskOne(input, &flags); err != nil { + return err + } + } + + if !flags.force && canPrompt() { if confirmed := prompt.Confirm("Are you sure you want to proceed?"); !confirmed { return nil } } err := ansi.Spinner("Deleting API", func() error { - return cli.api.ResourceServer.Delete(flags.id) + return cli.api.ResourceServer.Delete(flags.ID) }) if err != nil { @@ -196,10 +271,9 @@ auth0 apis delete --id id --force }, } - cmd.Flags().StringVarP(&flags.id, "id", "i", "", "ID of the API.") + cmd.Flags().StringVarP(&flags.ID, id, "i", "", "ID of the API.") cmd.Flags().BoolVarP(&flags.force, "force", "f", false, "Do not ask for confirmation.") - - mustRequireFlags(cmd, "id") + mustRequireFlags(cmd, id) return cmd } diff --git a/internal/cli/cli.go b/internal/cli/cli.go index 335f60301..72201ef87 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -12,9 +12,11 @@ import ( "sync" "time" + "github.com/auth0/auth0-cli/internal/ansi" "github.com/auth0/auth0-cli/internal/auth0" "github.com/auth0/auth0-cli/internal/display" "github.com/spf13/cobra" + "github.com/spf13/pflag" "gopkg.in/auth0.v5/management" ) @@ -238,3 +240,19 @@ func mustRequireFlags(cmd *cobra.Command, flags ...string) { } } } + +func canPrompt() bool { + return ansi.IsTerminal() +} + +func shouldPrompt(cmd *cobra.Command, flag string) bool { + return ansi.IsTerminal() && !cmd.Flags().Changed(flag) +} + +func prepareInteractivity(cmd *cobra.Command) { + if canPrompt() { + cmd.Flags().VisitAll(func(flag *pflag.Flag) { + cmd.Flags().SetAnnotation(flag.Name, cobra.BashCompOneRequiredFlag, []string{"false"}) + }) + } +} diff --git a/internal/cli/clients.go b/internal/cli/clients.go index 67b3e0628..34fef721f 100644 --- a/internal/cli/clients.go +++ b/internal/cli/clients.go @@ -34,7 +34,7 @@ Lists your existing clients. To create one try: `, RunE: func(cmd *cobra.Command, args []string) error { var list *management.ClientList - err := ansi.Spinner("Getting clients", func() error { + err := ansi.Spinner("Loading clients", func() error { var err error list, err = cli.api.Client.List() return err diff --git a/internal/cli/connections.go b/internal/cli/connections.go index a0201be50..44c405bc8 100644 --- a/internal/cli/connections.go +++ b/internal/cli/connections.go @@ -29,7 +29,7 @@ Lists your existing connections. To create one try: `, RunE: func(cmd *cobra.Command, args []string) error { var list *management.ConnectionList - err := ansi.Spinner("Getting connections", func() error { + err := ansi.Spinner("Loading connections", func() error { var err error list, err = cli.api.Connection.List() return err diff --git a/internal/cli/rules.go b/internal/cli/rules.go index b16054dca..f8fc98938 100644 --- a/internal/cli/rules.go +++ b/internal/cli/rules.go @@ -35,7 +35,7 @@ func listRulesCmd(cli *cli) *cobra.Command { Long: `Lists the rules in your current tenant.`, RunE: func(cmd *cobra.Command, args []string) error { var rules *management.RuleList - err := ansi.Spinner("Getting rules", func() error { + err := ansi.Spinner("Loading rules", func() error { var err error rules, err = getRules(cli) return err diff --git a/internal/display/apis.go b/internal/display/apis.go index 1ec1ea122..f0f3fdf4c 100644 --- a/internal/display/apis.go +++ b/internal/display/apis.go @@ -32,6 +32,11 @@ func (r *Renderer) ApiList(apis []*management.ResourceServer) { r.Results(res) } +func (r *Renderer) ApiShow(api *management.ResourceServer) { + r.Heading(ansi.Bold(r.Tenant), "API\n") + r.Results([]View{makeView(api)}) +} + func (r *Renderer) ApiCreate(api *management.ResourceServer) { r.Heading(ansi.Bold(r.Tenant), "API created\n") r.Results([]View{makeView(api)}) diff --git a/internal/display/display.go b/internal/display/display.go index 80badf674..1023a5265 100644 --- a/internal/display/display.go +++ b/internal/display/display.go @@ -55,7 +55,7 @@ func (r *Renderer) Errorf(format string, a ...interface{}) { } func (r *Renderer) Heading(text ...string) { - fmt.Fprintf(r.MessageWriter, "%s %s\n", ansi.Faint("==="), strings.Join(text, " ")) + fmt.Fprintf(r.MessageWriter, "\n%s %s\n", ansi.Faint("==="), strings.Join(text, " ")) } type View interface { diff --git a/internal/prompt/prompt.go b/internal/prompt/prompt.go index aa2b34311..07c117eeb 100644 --- a/internal/prompt/prompt.go +++ b/internal/prompt/prompt.go @@ -16,7 +16,11 @@ func Ask(inputs []*survey.Question, response interface{}) error { return survey.Ask(inputs, response, stdErrWriter, icons) } -func AskOne(prompt survey.Prompt, response interface{}) error { +func AskOne(input *survey.Question, response interface{}) error { + return survey.Ask([]*survey.Question {input}, response, stdErrWriter, icons) +} + +func askOne(prompt survey.Prompt, response interface{}) error { return survey.AskOne(prompt, response, stdErrWriter, icons) } @@ -53,7 +57,7 @@ func Confirm(message string) bool { Message: message, } - if err := AskOne(prompt, &result); err != nil { + if err := askOne(prompt, &result); err != nil { return false } From c205e83eeb85b9126276e0c4dbe1b72e22093d50 Mon Sep 17 00:00:00 2001 From: Rita Zerrizuela Date: Wed, 27 Jan 2021 01:32:57 -0300 Subject: [PATCH 2/4] Make interactivity tty-based on rules --- internal/cli/apis.go | 82 ++++++++-------- internal/cli/cli.go | 12 +-- internal/cli/rules.go | 194 ++++++++++++++++++++++++++------------ internal/prompt/prompt.go | 8 +- vendor/modules.txt | 1 + 5 files changed, 179 insertions(+), 118 deletions(-) diff --git a/internal/cli/apis.go b/internal/cli/apis.go index a6a478861..9c33009b5 100644 --- a/internal/cli/apis.go +++ b/internal/cli/apis.go @@ -8,15 +8,15 @@ import ( ) const ( - id = "id" - name = "name" - identifier = "identifier" + apiID = "id" + apiName = "name" + apiIdentifier = "identifier" ) func apisCmd(cli *cli) *cobra.Command { cmd := &cobra.Command{ - Use: "apis", - Short: "manage resources for APIs.", + Use: "apis", + Short: "manage resources for APIs.", Aliases: []string{"resource-servers"}, } @@ -62,7 +62,7 @@ Lists your existing APIs. To create one try: func showApiCmd(cli *cli) *cobra.Command { var flags struct { - ID string + ID string } cmd := &cobra.Command{ @@ -72,12 +72,12 @@ func showApiCmd(cli *cli) *cobra.Command { auth0 apis show --id id `, - PreRun: func(cmd *cobra.Command, args []string) { + PreRun: func(cmd *cobra.Command, args []string) { prepareInteractivity(cmd) }, RunE: func(cmd *cobra.Command, args []string) error { - if shouldPrompt(cmd, id) { - input := prompt.TextInput(id, "Id:", "Id of the API.", "", true) + if shouldPrompt(cmd, apiID) { + input := prompt.TextInput(apiID, "Id:", "Id of the API.", "", true) if err := prompt.AskOne(input, &flags); err != nil { return err @@ -101,8 +101,8 @@ auth0 apis show --id id }, } - cmd.Flags().StringVarP(&flags.ID, id, "i", "", "ID of the API.") - mustRequireFlags(cmd, id) + cmd.Flags().StringVarP(&flags.ID, apiID, "i", "", "ID of the API.") + mustRequireFlags(cmd, apiID) return cmd } @@ -120,15 +120,15 @@ func createApiCmd(cli *cli) *cobra.Command { auth0 apis create --name myapi --identifier http://my-api `, - PreRun: func(cmd *cobra.Command, args []string) { + PreRun: func(cmd *cobra.Command, args []string) { prepareInteractivity(cmd) }, RunE: func(cmd *cobra.Command, args []string) error { - if shouldPrompt(cmd, name) { + if shouldPrompt(cmd, apiName) { input := prompt.TextInput( - name, "Name:", - "Name of the API. You can change the API name later in the API settings.", - "", + apiName, "Name:", + "Name of the API. You can change the API name later in the API settings.", + "", true) if err := prompt.AskOne(input, &flags); err != nil { @@ -136,11 +136,11 @@ auth0 apis create --name myapi --identifier http://my-api } } - if shouldPrompt(cmd, identifier) { + if shouldPrompt(cmd, apiIdentifier) { input := prompt.TextInput( - identifier, "Identifier:", - "Identifier of the API. Cannot be changed once set.", - "", + apiIdentifier, "Identifier:", + "Identifier of the API. Cannot be changed once set.", + "", true) if err := prompt.AskOne(input, &flags); err != nil { @@ -166,9 +166,9 @@ auth0 apis create --name myapi --identifier http://my-api }, } - cmd.Flags().StringVarP(&flags.Name, name, "n", "", "Name of the API.") - cmd.Flags().StringVarP(&flags.Identifier, identifier, "i", "", "Identifier of the API.") - mustRequireFlags(cmd, name, identifier) + cmd.Flags().StringVarP(&flags.Name, apiName, "n", "", "Name of the API.") + cmd.Flags().StringVarP(&flags.Identifier, apiIdentifier, "i", "", "Identifier of the API.") + mustRequireFlags(cmd, apiName, apiIdentifier) return cmd } @@ -186,20 +186,20 @@ func updateApiCmd(cli *cli) *cobra.Command { auth0 apis update --id id --name myapi `, - PreRun: func(cmd *cobra.Command, args []string) { + PreRun: func(cmd *cobra.Command, args []string) { prepareInteractivity(cmd) }, RunE: func(cmd *cobra.Command, args []string) error { - if shouldPrompt(cmd, id) { - input := prompt.TextInput(id, "Id:", "Id of the API.", "", true) + if shouldPrompt(cmd, apiID) { + input := prompt.TextInput(apiID, "Id:", "Id of the API.", "", true) if err := prompt.AskOne(input, &flags); err != nil { return err } } - if shouldPrompt(cmd, name) { - input := prompt.TextInput(name, "Name:", "Name of the API.", "", true) + if shouldPrompt(cmd, apiName) { + input := prompt.TextInput(apiName, "Name:", "Name of the API.", "", true) if err := prompt.AskOne(input, &flags); err != nil { return err @@ -221,17 +221,16 @@ auth0 apis update --id id --name myapi }, } - cmd.Flags().StringVarP(&flags.ID, id, "i", "", "ID of the API.") - cmd.Flags().StringVarP(&flags.Name, name, "n", "", "Name of the API.") - mustRequireFlags(cmd, id, name) + cmd.Flags().StringVarP(&flags.ID, apiID, "i", "", "ID of the API.") + cmd.Flags().StringVarP(&flags.Name, apiName, "n", "", "Name of the API.") + mustRequireFlags(cmd, apiID, apiName) return cmd } func deleteApiCmd(cli *cli) *cobra.Command { var flags struct { - ID string - force bool + ID string } cmd := &cobra.Command{ @@ -239,26 +238,26 @@ func deleteApiCmd(cli *cli) *cobra.Command { Short: "Delete an API", Long: `Deletes an API: -auth0 apis delete --id id --force +auth0 apis delete --id id `, - PreRun: func(cmd *cobra.Command, args []string) { + PreRun: func(cmd *cobra.Command, args []string) { prepareInteractivity(cmd) }, RunE: func(cmd *cobra.Command, args []string) error { - if shouldPrompt(cmd, id) { - input := prompt.TextInput(id, "Id:", "Id of the API.", "", true) + if shouldPrompt(cmd, apiID) { + input := prompt.TextInput(apiID, "Id:", "Id of the API.", "", true) if err := prompt.AskOne(input, &flags); err != nil { return err } } - if !flags.force && canPrompt() { + if !cli.force && canPrompt() { if confirmed := prompt.Confirm("Are you sure you want to proceed?"); !confirmed { return nil } } - + err := ansi.Spinner("Deleting API", func() error { return cli.api.ResourceServer.Delete(flags.ID) }) @@ -271,9 +270,8 @@ auth0 apis delete --id id --force }, } - cmd.Flags().StringVarP(&flags.ID, id, "i", "", "ID of the API.") - cmd.Flags().BoolVarP(&flags.force, "force", "f", false, "Do not ask for confirmation.") - mustRequireFlags(cmd, id) + cmd.Flags().StringVarP(&flags.ID, apiID, "i", "", "ID of the API.") + mustRequireFlags(cmd, apiID) return cmd } diff --git a/internal/cli/cli.go b/internal/cli/cli.go index 72201ef87..933439f7b 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -223,16 +223,6 @@ func (c *cli) initContext() (err error) { return nil } -func hasFlags(cmd *cobra.Command) bool { - return cmd.Flags().NFlag() > 0 -} - -func checkFlags(cmd *cobra.Command) { - if (!hasFlags(cmd)) { - cmd.ResetFlags() - } -} - func mustRequireFlags(cmd *cobra.Command, flags ...string) { for _, f := range flags { if err := cmd.MarkFlagRequired(f); err != nil { @@ -246,7 +236,7 @@ func canPrompt() bool { } func shouldPrompt(cmd *cobra.Command, flag string) bool { - return ansi.IsTerminal() && !cmd.Flags().Changed(flag) + return canPrompt() && !cmd.Flags().Changed(flag) } func prepareInteractivity(cmd *cobra.Command) { diff --git a/internal/cli/rules.go b/internal/cli/rules.go index f8fc98938..32d5665d8 100644 --- a/internal/cli/rules.go +++ b/internal/cli/rules.go @@ -4,7 +4,6 @@ import ( "fmt" "regexp" - "github.com/AlecAivazis/survey/v2" "github.com/auth0/auth0-cli/internal/ansi" "github.com/auth0/auth0-cli/internal/auth0" "github.com/auth0/auth0-cli/internal/prompt" @@ -12,6 +11,14 @@ import ( "gopkg.in/auth0.v5/management" ) +const ( + ruleID = "id" + ruleName = "name" + ruleScript = "script" + ruleOrder = "order" + ruleEnabled = "enabled" +) + func rulesCmd(cli *cli) *cobra.Command { cmd := &cobra.Command{ Use: "rules", @@ -54,11 +61,29 @@ func listRulesCmd(cli *cli) *cobra.Command { } func enableRuleCmd(cli *cli) *cobra.Command { - var name string + var flags struct { + Name string + } + cmd := &cobra.Command{ Use: "enable", - Short: "enable rule", + Short: "Emable a rule", + PreRun: func(cmd *cobra.Command, args []string) { + prepareInteractivity(cmd) + }, RunE: func(cmd *cobra.Command, args []string) error { + if shouldPrompt(cmd, ruleName) { + input := prompt.TextInput( + ruleName, "Name:", + "Name of the rule.", + "", + true) + + if err := prompt.AskOne(input, &flags); err != nil { + return err + } + } + err := ansi.Spinner("Enabling rule", func() error { var err error data, err := getRules(cli) @@ -66,14 +91,14 @@ func enableRuleCmd(cli *cli) *cobra.Command { return err } - rule := findRuleByName(name, data.Rules) + rule := findRuleByName(flags.Name, data.Rules) if rule != nil { err := enableRule(rule, cli) if err != nil { return err } } else { - return fmt.Errorf("No rule found with name: %q", name) + return fmt.Errorf("No rule found with name: %q", flags.Name) } return nil }) @@ -95,17 +120,36 @@ func enableRuleCmd(cli *cli) *cobra.Command { }, } - cmd.Flags().StringVarP(&name, "name", "n", "", "rule name") - mustRequireFlags(cmd, "name") + cmd.Flags().StringVarP(&flags.Name, ruleName, "n", "", "Name of the rule.") + mustRequireFlags(cmd, ruleName) + return cmd } func disableRuleCmd(cli *cli) *cobra.Command { - var name string + var flags struct { + Name string + } + cmd := &cobra.Command{ Use: "disable", - Short: "disable rule", + Short: "Disable a rule", + PreRun: func(cmd *cobra.Command, args []string) { + prepareInteractivity(cmd) + }, RunE: func(cmd *cobra.Command, args []string) error { + if shouldPrompt(cmd, ruleName) { + input := prompt.TextInput( + ruleName, "Name:", + "Name of the rule.", + "", + true) + + if err := prompt.AskOne(input, &flags); err != nil { + return err + } + } + err := ansi.Spinner("Disabling rule", func() error { var err error data, err := getRules(cli) @@ -113,13 +157,13 @@ func disableRuleCmd(cli *cli) *cobra.Command { return err } - rule := findRuleByName(name, data.Rules) + rule := findRuleByName(flags.Name, data.Rules) if rule != nil { if err := disableRule(rule, cli); err != nil { return err } } else { - return fmt.Errorf("No rule found with name: %q", name) + return fmt.Errorf("No rule found with name: %q", flags.Name) } return nil }) @@ -141,8 +185,8 @@ func disableRuleCmd(cli *cli) *cobra.Command { }, } - cmd.Flags().StringVarP(&name, "name", "n", "", "rule name") - mustRequireFlags(cmd, "name") + cmd.Flags().StringVarP(&flags.Name, ruleName, "n", "", "rule name") + mustRequireFlags(cmd, ruleName) return cmd } @@ -163,21 +207,41 @@ func createRulesCmd(cli *cli) *cobra.Command { auth0 rules create --name "My Rule" --script "function (user, context, callback) { console.log( 'Hello, world!' ); return callback(null, user, context); }" `, PreRun: func(cmd *cobra.Command, args []string) { - checkFlags(cmd) + prepareInteractivity(cmd) }, RunE: func(cmd *cobra.Command, args []string) error { - if !hasFlags(cmd) { - name := prompt.TextInput( - "name", "Name:", - "Name of the rule. You can change the rule name later in the rule settings.", - "", + if shouldPrompt(cmd, ruleName) { + input := prompt.TextInput( + "name", "Name:", + "Name of the rule. You can change the rule name later in the rule settings.", + "", true) - script := prompt.TextInput("script", "Script:", "Script of the rule.", "", true) - order := prompt.TextInput("order", "Order:", "Order of the rule.", "0", false) - enabled := prompt.BoolInput("enabled", "Enabled:", "Enable the rule.", false) + if err := prompt.AskOne(input, &flags); err != nil { + return err + } + } + + if shouldPrompt(cmd, ruleScript) { + input := prompt.TextInput(ruleScript, "Script:", "Script of the rule.", "", true) - if err := prompt.Ask([]*survey.Question {name, script, order, enabled}, &flags); err != nil { + if err := prompt.AskOne(input, &flags); err != nil { + return err + } + } + + if shouldPrompt(cmd, ruleOrder) { + input := prompt.TextInput(ruleOrder, "Order:", "Order of the rule.", "0", false) + + if err := prompt.AskOne(input, &flags); err != nil { + return err + } + } + + if shouldPrompt(cmd, ruleEnabled) { + input := prompt.BoolInput(ruleEnabled, "Enabled:", "Enable the rule.", false) + + if err := prompt.AskOne(input, &flags); err != nil { return err } } @@ -202,19 +266,19 @@ func createRulesCmd(cli *cli) *cobra.Command { }, } - cmd.Flags().StringVarP(&flags.Name, "name", "n", "", "Name of this rule (required)") - cmd.Flags().StringVarP(&flags.Script, "script", "s", "", "Code to be executed when this rule runs (required)") - cmd.Flags().IntVarP(&flags.Order, "order", "o", 0, "Order that this rule should execute in relative to other rules. Lower-valued rules execute first.") - cmd.Flags().BoolVarP(&flags.Enabled, "enabled", "e", false, "Whether the rule is enabled (true), or disabled (false).") - mustRequireFlags(cmd, "name", "script") + cmd.Flags().StringVarP(&flags.Name, ruleName, "n", "", "Name of this rule (required)") + cmd.Flags().StringVarP(&flags.Script, ruleScript, "s", "", "Code to be executed when this rule runs (required)") + cmd.Flags().IntVarP(&flags.Order, ruleOrder, "o", 0, "Order that this rule should execute in relative to other rules. Lower-valued rules execute first.") + cmd.Flags().BoolVarP(&flags.Enabled, ruleEnabled, "e", false, "Whether the rule is enabled (true), or disabled (false).") + mustRequireFlags(cmd, ruleName, ruleScript) + return cmd } func deleteRulesCmd(cli *cli) *cobra.Command { var flags struct { - id string - name string - force bool + ID string + Name string } cmd := &cobra.Command{ @@ -222,45 +286,54 @@ func deleteRulesCmd(cli *cli) *cobra.Command { Short: "Delete a rule", Long: `Delete a rule: - auth0 rules delete --id "12345" --force`, + auth0 rules delete --id "12345"`, PreRunE: func(cmd *cobra.Command, args []string) error { - if flags.id != "" && flags.name != "" { + if flags.ID != "" && flags.Name != "" { return fmt.Errorf("TMI! 🤯 use either --name or --id") } + + prepareInteractivity(cmd) return nil }, RunE: func(cmd *cobra.Command, args []string) error { - if !flags.force { + if shouldPrompt(cmd, ruleID) { + input := prompt.TextInput(ruleID, "Id:", "Id of the rule.", "", true) + + if err := prompt.AskOne(input, &flags); err != nil { + return err + } + } + + if !cli.force && canPrompt() { if confirmed := prompt.Confirm("Are you sure you want to proceed?"); !confirmed { return nil } } - // TODO: Should add validation of rule var r *management.Rule - ruleIDPattern := "^rul_[A-Za-z0-9]{16}$" - re := regexp.MustCompile(ruleIDPattern) - - if flags.id != "" { - if !re.Match([]byte(flags.id)) { - return fmt.Errorf("Rule with id %q does not match pattern %s", flags.id, ruleIDPattern) - } - - rule, err := cli.api.Rule.Read(flags.id) - if err != nil { - return err - } - r = rule - } else { - data, err := getRules(cli) - if err != nil { - return err - } - if rule := findRuleByName(flags.name, data.Rules); rule != nil { - r = rule - } else { - return fmt.Errorf("No rule found with name: %q", flags.name) - } + ruleIDPattern := "^rul_[A-Za-z0-9]{16}$" + re := regexp.MustCompile(ruleIDPattern) + + if flags.ID != "" { + if !re.Match([]byte(flags.ID)) { + return fmt.Errorf("Rule with id %q does not match pattern %s", flags.ID, ruleIDPattern) + } + + rule, err := cli.api.Rule.Read(flags.ID) + if err != nil { + return err + } + r = rule + } else { + data, err := getRules(cli) + if err != nil { + return err + } + if rule := findRuleByName(flags.Name, data.Rules); rule != nil { + r = rule + } else { + return fmt.Errorf("No rule found with name: %q", flags.Name) + } } err := ansi.Spinner("Deleting rule", func() error { @@ -275,9 +348,8 @@ func deleteRulesCmd(cli *cli) *cobra.Command { }, } - cmd.Flags().StringVarP(&flags.id, "id", "i", "", "ID of the rule to delete (required)") - cmd.Flags().StringVarP(&flags.name, "name", "n", "", "Name of the rule to delete") - cmd.Flags().BoolVarP(&flags.force, "force", "f", false, "Do not ask for confirmation.") + cmd.Flags().StringVarP(&flags.ID, ruleID, "i", "", "ID of the rule to delete (required)") + cmd.Flags().StringVarP(&flags.Name, ruleName, "n", "", "Name of the rule to delete") return cmd } diff --git a/internal/prompt/prompt.go b/internal/prompt/prompt.go index 07c117eeb..1f0b09684 100644 --- a/internal/prompt/prompt.go +++ b/internal/prompt/prompt.go @@ -9,7 +9,7 @@ import ( var stdErrWriter = survey.WithStdio(os.Stdin, os.Stderr, os.Stderr) var icons = survey.WithIcons(func(icons *survey.IconSet) { - icons.Question.Text = "" + icons.Question.Text = "" }) func Ask(inputs []*survey.Question, response interface{}) error { @@ -17,7 +17,7 @@ func Ask(inputs []*survey.Question, response interface{}) error { } func AskOne(input *survey.Question, response interface{}) error { - return survey.Ask([]*survey.Question {input}, response, stdErrWriter, icons) + return survey.Ask([]*survey.Question{input}, response, stdErrWriter, icons) } func askOne(prompt survey.Prompt, response interface{}) error { @@ -26,8 +26,8 @@ func askOne(prompt survey.Prompt, response interface{}) error { func TextInput(name string, message string, help string, value string, required bool) *survey.Question { input := &survey.Question{ - Name: name, - Prompt: &survey.Input{Message: message, Help: help, Default: value}, + Name: name, + Prompt: &survey.Input{Message: message, Help: help, Default: value}, } if required { diff --git a/vendor/modules.txt b/vendor/modules.txt index ce40a8c49..a29bdb6bd 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -43,6 +43,7 @@ github.com/logrusorgru/aurora ## explicit github.com/mattn/go-colorable # github.com/mattn/go-isatty v0.0.12 +## explicit github.com/mattn/go-isatty # github.com/mattn/go-runewidth v0.0.10 ## explicit From 811a9e28534d9f6d68b4d444fb3441439d5f4f6f Mon Sep 17 00:00:00 2001 From: Rita Zerrizuela Date: Wed, 27 Jan 2021 02:29:01 -0300 Subject: [PATCH 3/4] Add --no-input flag --- internal/cli/apis.go | 2 +- internal/cli/cli.go | 15 +++++++++++---- internal/cli/root.go | 3 +++ internal/cli/rules.go | 2 +- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/internal/cli/apis.go b/internal/cli/apis.go index 9c33009b5..c190051e5 100644 --- a/internal/cli/apis.go +++ b/internal/cli/apis.go @@ -252,7 +252,7 @@ auth0 apis delete --id id } } - if !cli.force && canPrompt() { + if !cli.force && canPrompt(cmd) { if confirmed := prompt.Confirm("Are you sure you want to proceed?"); !confirmed { return nil } diff --git a/internal/cli/cli.go b/internal/cli/cli.go index 933439f7b..05aec894b 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -60,6 +60,7 @@ type cli struct { tenant string format string force bool + noInput bool // config state management. initOnce sync.Once @@ -231,16 +232,22 @@ func mustRequireFlags(cmd *cobra.Command, flags ...string) { } } -func canPrompt() bool { - return ansi.IsTerminal() +func canPrompt(cmd *cobra.Command) bool { + noInput, err := cmd.Root().Flags().GetBool("no-input") + + if err != nil { + return false + } + + return ansi.IsTerminal() && !noInput } func shouldPrompt(cmd *cobra.Command, flag string) bool { - return canPrompt() && !cmd.Flags().Changed(flag) + return canPrompt(cmd) && !cmd.Flags().Changed(flag) } func prepareInteractivity(cmd *cobra.Command) { - if canPrompt() { + if canPrompt(cmd) { cmd.Flags().VisitAll(func(flag *pflag.Flag) { cmd.Flags().SetAnnotation(flag.Name, cobra.BashCompOneRequiredFlag, []string{"false"}) }) diff --git a/internal/cli/root.go b/internal/cli/root.go index 72b5b4245..2936f141c 100644 --- a/internal/cli/root.go +++ b/internal/cli/root.go @@ -52,6 +52,9 @@ func Execute() { rootCmd.PersistentFlags().BoolVar(&cli.force, "force", false, "Skip confirmation.") + + rootCmd.PersistentFlags().BoolVar(&cli.noInput, + "no-input", false, "Disable interactivity.") rootCmd.AddCommand(loginCmd(cli)) rootCmd.AddCommand(clientsCmd(cli)) diff --git a/internal/cli/rules.go b/internal/cli/rules.go index 32d5665d8..f7fc98b0b 100644 --- a/internal/cli/rules.go +++ b/internal/cli/rules.go @@ -304,7 +304,7 @@ func deleteRulesCmd(cli *cli) *cobra.Command { } } - if !cli.force && canPrompt() { + if !cli.force && canPrompt(cmd) { if confirmed := prompt.Confirm("Are you sure you want to proceed?"); !confirmed { return nil } From ce25dc60002e5c867df7baa5825cc777a6f8b4fe Mon Sep 17 00:00:00 2001 From: Rita Zerrizuela Date: Wed, 27 Jan 2021 02:37:45 -0300 Subject: [PATCH 4/4] Omit return value of SetAnnotation --- internal/cli/cli.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/cli/cli.go b/internal/cli/cli.go index 05aec894b..82b0b582a 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -249,7 +249,7 @@ func shouldPrompt(cmd *cobra.Command, flag string) bool { func prepareInteractivity(cmd *cobra.Command) { if canPrompt(cmd) { cmd.Flags().VisitAll(func(flag *pflag.Flag) { - cmd.Flags().SetAnnotation(flag.Name, cobra.BashCompOneRequiredFlag, []string{"false"}) + _ = cmd.Flags().SetAnnotation(flag.Name, cobra.BashCompOneRequiredFlag, []string{"false"}) }) } }