diff --git a/cmd/flags/create.go b/cmd/flags/create.go index 84f7d448..0afd8ee6 100644 --- a/cmd/flags/create.go +++ b/cmd/flags/create.go @@ -4,22 +4,21 @@ import ( "context" "encoding/json" "fmt" - "net/url" "github.com/spf13/cobra" "github.com/spf13/viper" - "ldcli/internal/errors" + "ldcli/cmd/validators" "ldcli/internal/flags" ) func NewCreateCmd(client flags.Client) (*cobra.Command, error) { cmd := &cobra.Command{ - Use: "create", - Short: "Create a new flag", - Long: "Create a new flag", - PreRunE: validate, - RunE: runCreate(client), + Args: validators.Validate(), + Long: "Create a new flag", + RunE: runCreate(client), + Short: "Create a new flag", + Use: "create", } cmd.Flags().StringP("data", "d", "", "Input data in JSON") @@ -79,14 +78,3 @@ func runCreate(client flags.Client) func(*cobra.Command, []string) error { return nil } } - -// validate ensures the flags are valid before using them. -// TODO: refactor with projects validate(). -func validate(cmd *cobra.Command, args []string) error { - _, err := url.ParseRequestURI(viper.GetString("baseUri")) - if err != nil { - return errors.ErrInvalidBaseURI - } - - return nil -} diff --git a/cmd/flags/create_test.go b/cmd/flags/create_test.go index a0321463..72df4247 100644 --- a/cmd/flags/create_test.go +++ b/cmd/flags/create_test.go @@ -12,6 +12,7 @@ import ( ) func TestCreate(t *testing.T) { + errorHelp := ". See `ldcli flags create --help` for supported flags and usage." mockArgs := []interface{}{ "testAccessToken", "http://test.com", @@ -63,7 +64,29 @@ func TestCreate(t *testing.T) { _, err := cmd.CallCmd(t, &flags.MockClient{}, nil, nil, args) - assert.EqualError(t, err, `required flag(s) "accessToken", "data", "projKey" not set`) + assert.EqualError(t, err, `required flag(s) "accessToken", "data", "projKey" not set`+errorHelp) + }) + + t.Run("with missing short flag value is an error", func(t *testing.T) { + args := []string{ + "flags", "create", + "-d", + } + + _, err := cmd.CallCmd(t, &flags.MockClient{}, nil, nil, args) + + assert.EqualError(t, err, `flag needs an argument: 'd' in -d`) + }) + + t.Run("with missing long flag value is an error", func(t *testing.T) { + args := []string{ + "flags", "create", + "--data", + } + + _, err := cmd.CallCmd(t, &flags.MockClient{}, nil, nil, args) + + assert.EqualError(t, err, `flag needs an argument: --data`) }) t.Run("with invalid baseUri is an error", func(t *testing.T) { @@ -77,6 +100,6 @@ func TestCreate(t *testing.T) { _, err := cmd.CallCmd(t, &flags.MockClient{}, nil, nil, args) - assert.EqualError(t, err, "baseUri is invalid") + assert.EqualError(t, err, "baseUri is invalid"+errorHelp) }) } diff --git a/cmd/flags/update.go b/cmd/flags/update.go index fcbed697..77333d0a 100644 --- a/cmd/flags/update.go +++ b/cmd/flags/update.go @@ -9,16 +9,17 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" + "ldcli/cmd/validators" "ldcli/internal/flags" ) func NewUpdateCmd(client flags.Client) (*cobra.Command, error) { cmd := &cobra.Command{ - Use: "update", - Short: "Update a flag", - Long: "Update a flag", - PreRunE: validate, - RunE: runUpdate(client), + Args: validators.Validate(), + Long: "Update a flag", + RunE: runUpdate(client), + Short: "Update a flag", + Use: "update", } var data string diff --git a/cmd/members/create.go b/cmd/members/create.go index a663086e..f70e5d6c 100644 --- a/cmd/members/create.go +++ b/cmd/members/create.go @@ -4,22 +4,21 @@ import ( "context" "encoding/json" "fmt" - "net/url" "github.com/spf13/cobra" "github.com/spf13/viper" - "ldcli/internal/errors" + "ldcli/cmd/validators" "ldcli/internal/members" ) func NewCreateCmd(client members.Client) (*cobra.Command, error) { cmd := &cobra.Command{ - Use: "create", - Short: "Create a new member", - Long: "Create a new member", - PreRunE: validate, - RunE: runCreate(client), + Args: validators.Validate(), + Long: "Create a new member", + RunE: runCreate(client), + Short: "Create a new member", + Use: "create", } cmd.Flags().StringP("data", "d", "", "Input data in JSON") @@ -65,14 +64,3 @@ func runCreate(client members.Client) func(*cobra.Command, []string) error { return nil } } - -// validate ensures the flags are valid before using them. -// TODO: refactor with flags & projects validate(). -func validate(cmd *cobra.Command, args []string) error { - _, err := url.ParseRequestURI(viper.GetString("baseUri")) - if err != nil { - return errors.ErrInvalidBaseURI - } - - return nil -} diff --git a/cmd/members/create_test.go b/cmd/members/create_test.go index 919fa881..e493c643 100644 --- a/cmd/members/create_test.go +++ b/cmd/members/create_test.go @@ -12,6 +12,7 @@ import ( ) func TestCreate(t *testing.T) { + errorHelp := ". See `ldcli members create --help` for supported flags and usage." mockArgs := []interface{}{ "testAccessToken", "http://test.com", @@ -69,18 +70,18 @@ func TestCreate(t *testing.T) { _, err := cmd.CallCmd(t, nil, &members.MockClient{}, nil, args) - assert.EqualError(t, err, `required flag(s) "accessToken", "data" not set`) + assert.EqualError(t, err, `required flag(s) "accessToken", "data" not set`+errorHelp) }) t.Run("with invalid baseUri is an error", func(t *testing.T) { args := []string{ - "projects", + "members", "create", "--baseUri", "invalid", } _, err := cmd.CallCmd(t, nil, &members.MockClient{}, nil, args) - assert.EqualError(t, err, "baseUri is invalid") + assert.EqualError(t, err, "baseUri is invalid"+errorHelp) }) } diff --git a/cmd/projects/create.go b/cmd/projects/create.go index 0e314e3f..cc8a6c86 100644 --- a/cmd/projects/create.go +++ b/cmd/projects/create.go @@ -8,16 +8,17 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" + "ldcli/cmd/validators" "ldcli/internal/projects" ) func NewCreateCmd(client projects.Client) (*cobra.Command, error) { cmd := &cobra.Command{ - Use: "create", - Short: "Create a new project", - Long: "Create a new project", - PreRunE: validate, - RunE: runCreate(client), + Args: validators.Validate(), + Long: "Create a new project", + RunE: runCreate(client), + Short: "Create a new project", + Use: "create", } cmd.Flags().StringP("data", "d", "", "Input data in JSON") diff --git a/cmd/projects/create_test.go b/cmd/projects/create_test.go index 5a1911ff..ff922ab8 100644 --- a/cmd/projects/create_test.go +++ b/cmd/projects/create_test.go @@ -12,6 +12,7 @@ import ( ) func TestCreate(t *testing.T) { + errorHelp := ". See `ldcli projects create --help` for supported flags and usage." mockArgs := []interface{}{ "testAccessToken", "http://test.com", @@ -69,7 +70,29 @@ func TestCreate(t *testing.T) { _, err := cmd.CallCmd(t, nil, nil, &projects.MockClient{}, args) - assert.EqualError(t, err, `required flag(s) "accessToken", "data" not set`) + assert.EqualError(t, err, `required flag(s) "accessToken", "data" not set`+errorHelp) + }) + + t.Run("with missing short flag value is an error", func(t *testing.T) { + args := []string{ + "projects", "create", + "-d", + } + + _, err := cmd.CallCmd(t, nil, nil, &projects.MockClient{}, args) + + assert.EqualError(t, err, `flag needs an argument: 'd' in -d`) + }) + + t.Run("with missing long flag value is an error", func(t *testing.T) { + args := []string{ + "projects", "create", + "--data", + } + + _, err := cmd.CallCmd(t, nil, nil, &projects.MockClient{}, args) + + assert.EqualError(t, err, `flag needs an argument: --data`) }) t.Run("with invalid baseUri is an error", func(t *testing.T) { @@ -81,6 +104,6 @@ func TestCreate(t *testing.T) { _, err := cmd.CallCmd(t, nil, nil, &projects.MockClient{}, args) - assert.EqualError(t, err, "baseUri is invalid") + assert.EqualError(t, err, "baseUri is invalid"+errorHelp) }) } diff --git a/cmd/projects/list.go b/cmd/projects/list.go index 1cd5e650..765c95bd 100644 --- a/cmd/projects/list.go +++ b/cmd/projects/list.go @@ -3,38 +3,26 @@ package projects import ( "context" "fmt" - "net/url" "github.com/spf13/cobra" "github.com/spf13/viper" - "ldcli/internal/errors" + "ldcli/cmd/validators" "ldcli/internal/projects" ) func NewListCmd(client projects.Client) *cobra.Command { cmd := &cobra.Command{ - Use: "list", - Short: "Return a list of projects", - Long: "Return a list of projects", - PreRunE: validate, - RunE: runList(client), + Args: validators.Validate(), + Long: "Return a list of projects", + RunE: runList(client), + Short: "Return a list of projects", + Use: "list", } return cmd } -// validate ensures the flags are valid before using them. -// TODO: refactor with flags validate(). -func validate(cmd *cobra.Command, args []string) error { - _, err := url.ParseRequestURI(viper.GetString("baseUri")) - if err != nil { - return errors.ErrInvalidBaseURI - } - - return nil -} - func runList(client projects.Client) func(*cobra.Command, []string) error { return func(cmd *cobra.Command, args []string) error { response, err := client.List( diff --git a/cmd/projects/list_test.go b/cmd/projects/list_test.go index 71c86966..e1f77062 100644 --- a/cmd/projects/list_test.go +++ b/cmd/projects/list_test.go @@ -12,6 +12,7 @@ import ( ) func TestList(t *testing.T) { + errorHelp := ". See `ldcli projects list --help` for supported flags and usage." mockArgs := []interface{}{ "testAccessToken", "http://test.com", @@ -56,7 +57,29 @@ func TestList(t *testing.T) { _, err := cmd.CallCmd(t, nil, nil, &projects.MockClient{}, args) - assert.EqualError(t, err, `required flag(s) "accessToken" not set`) + assert.EqualError(t, err, `required flag(s) "accessToken" not set`+errorHelp) + }) + + t.Run("with missing short flag value is an error", func(t *testing.T) { + args := []string{ + "projects", "list", + "-t", + } + + _, err := cmd.CallCmd(t, nil, nil, &projects.MockClient{}, args) + + assert.EqualError(t, err, `flag needs an argument: 't' in -t`) + }) + + t.Run("with missing long flag value is an error", func(t *testing.T) { + args := []string{ + "projects", "list", + "--accessToken", + } + + _, err := cmd.CallCmd(t, nil, nil, &projects.MockClient{}, args) + + assert.EqualError(t, err, `flag needs an argument: --accessToken`) }) t.Run("with invalid baseUri is an error", func(t *testing.T) { @@ -68,6 +91,6 @@ func TestList(t *testing.T) { _, err := cmd.CallCmd(t, nil, nil, &projects.MockClient{}, args) - assert.EqualError(t, err, "baseUri is invalid") + assert.EqualError(t, err, "baseUri is invalid"+errorHelp) }) } diff --git a/cmd/root.go b/cmd/root.go index 39c9676d..12b60cb9 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,7 +1,6 @@ package cmd import ( - "errors" "fmt" "log" "os" @@ -12,7 +11,6 @@ import ( flagscmd "ldcli/cmd/flags" mbrscmd "ldcli/cmd/members" projcmd "ldcli/cmd/projects" - errs "ldcli/internal/errors" "ldcli/internal/flags" "ldcli/internal/members" "ldcli/internal/projects" @@ -29,8 +27,8 @@ func NewRootCommand(flagsClient flags.Client, membersClient members.Client, proj // Handle errors differently based on type. // We don't want to show the usage if the user has the right structure but invalid data such as // the wrong key. - SilenceUsage: true, SilenceErrors: true, + SilenceUsage: true, } cmd.PersistentFlags().StringP( @@ -88,12 +86,6 @@ func Execute() { err = rootCmd.Execute() if err != nil { - switch { - case errors.Is(err, errs.Error{}): - fmt.Fprintln(os.Stderr, err.Error()) - default: - fmt.Println(err.Error()) - fmt.Println(rootCmd.UsageString()) - } + fmt.Fprintln(os.Stderr, err.Error()) } } diff --git a/cmd/validators/validators.go b/cmd/validators/validators.go new file mode 100644 index 00000000..97d37fa4 --- /dev/null +++ b/cmd/validators/validators.go @@ -0,0 +1,53 @@ +package validators + +import ( + "errors" + "fmt" + "net/url" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + + errs "ldcli/internal/errors" +) + +// Validate is a validator for commands to print an error when the user input is invalid. +func Validate() cobra.PositionalArgs { + return func(cmd *cobra.Command, args []string) error { + commandPath := getCommandPath(cmd) + + _, err := url.ParseRequestURI(viper.GetString("baseUri")) + if err != nil { + errorMessage := fmt.Sprintf( + "%s. See `%s --help` for supported flags and usage.", + errs.ErrInvalidBaseURI, + commandPath, + ) + return errors.New(errorMessage) + } + + err = cmd.ValidateRequiredFlags() + if err != nil { + errorMessage := fmt.Sprintf( + "%s. See `%s --help` for supported flags and usage.", + err.Error(), + commandPath, + ) + + return errors.New(errorMessage) + } + + return nil + } +} + +func getCommandPath(cmd *cobra.Command) string { + var commandPath string + if cmd.Annotations["scope"] == "plugin" { + commandPath = fmt.Sprintf("stripe %s", cmd.CommandPath()) + } else { + commandPath = cmd.CommandPath() + } + + return commandPath +}