Skip to content

Commit

Permalink
feat: Add more help in error message (#72)
Browse files Browse the repository at this point in the history
Add more help in command error messages
  • Loading branch information
dbolson authored Mar 27, 2024
1 parent c080951 commit 6221983
Show file tree
Hide file tree
Showing 11 changed files with 164 additions and 83 deletions.
24 changes: 6 additions & 18 deletions cmd/flags/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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
}
27 changes: 25 additions & 2 deletions cmd/flags/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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) {
Expand All @@ -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)
})
}
11 changes: 6 additions & 5 deletions cmd/flags/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
24 changes: 6 additions & 18 deletions cmd/members/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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
}
7 changes: 4 additions & 3 deletions cmd/members/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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)
})
}
11 changes: 6 additions & 5 deletions cmd/projects/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
27 changes: 25 additions & 2 deletions cmd/projects/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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) {
Expand All @@ -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)
})
}
24 changes: 6 additions & 18 deletions cmd/projects/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
27 changes: 25 additions & 2 deletions cmd/projects/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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) {
Expand All @@ -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)
})
}
12 changes: 2 additions & 10 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package cmd

import (
"errors"
"fmt"
"log"
"os"
Expand All @@ -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"
Expand All @@ -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(
Expand Down Expand Up @@ -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())
}
}
Loading

0 comments on commit 6221983

Please sign in to comment.