Skip to content

Commit

Permalink
R4R: Support "unknown commands" for subcommands (#4465)
Browse files Browse the repository at this point in the history
Fixes #4284
Now prints:

gaiacli query distr comission --trust-node cosmos1234
ERROR: unknown command "comission" for "distr"

Did you mean this?
	commission

Adds custom argument validation for subcommands with subcommands. Doesn't affect "query" or "tx" subcommands since they reside in gaia repo. All flags except help are disabled for these commands.
  • Loading branch information
colin-axner authored and Alessio Treglia committed Jun 4, 2019
1 parent 75de63c commit 6e2f5f3
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 22 deletions.
1 change: 1 addition & 0 deletions .pending/improvements/sdk/4465-Unknown-subcomm
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#4465 Unknown subcommands print relevant error message
31 changes: 31 additions & 0 deletions client/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"

"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"

"github.com/tendermint/tendermint/libs/common"
Expand Down Expand Up @@ -352,3 +353,33 @@ func isTxSigner(user sdk.AccAddress, signers []sdk.AccAddress) bool {

return false
}

// ValidateCmd returns unknown command error or Help display if help flag set
func ValidateCmd(cmd *cobra.Command, args []string) error {
var cmds []string
var help bool

// construct array of commands and search for help flag
for _, arg := range args {
if arg == "--help" || arg == "-h" {
help = true
} else if len(arg) > 0 && !(arg[0] == '-') {
cmds = append(cmds, arg)
}
}

if !help && len(cmds) > 0 {
err := fmt.Sprintf("unknown command \"%s\" for \"%s\"", cmds[0], cmd.CalledAs())

// build suggestions for unknown argument
if suggestions := cmd.SuggestionsFor(cmds[0]); len(suggestions) > 0 {
err += "\n\nDid you mean this?\n"
for _, s := range suggestions {
err += fmt.Sprintf("\t%v\n", s)
}
}
return errors.New(err)
}

return cmd.Help()
}
42 changes: 42 additions & 0 deletions client/utils/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"
"testing"

"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto/ed25519"
Expand Down Expand Up @@ -112,6 +113,47 @@ func TestReadStdTxFromFile(t *testing.T) {
require.Equal(t, decodedTx.Memo, "foomemo")
}

func TestValidateCmd(t *testing.T) {
// Setup root and subcommands
rootCmd := &cobra.Command{
Use: "root",
}
queryCmd := &cobra.Command{
Use: "query",
}
rootCmd.AddCommand(queryCmd)

// Command being tested
distCmd := &cobra.Command{
Use: "distr",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
}
queryCmd.AddCommand(distCmd)

commissionCmd := &cobra.Command{
Use: "commission",
}
distCmd.AddCommand(commissionCmd)

tests := []struct {
reason string
args []string
wantErr bool
}{
{"misspelled command", []string{"comission"}, true},
{"no command provided", []string{}, false},
{"help flag", []string{"comission", "--help"}, false},
{"shorthand help flag", []string{"comission", "-h"}, false},
}

for _, tt := range tests {
err := ValidateCmd(distCmd, tt.args)
assert.Equal(t, tt.wantErr, err != nil, tt.reason)
}

}

// aux functions

func compareEncoders(t *testing.T, expected sdk.TxEncoder, actual sdk.TxEncoder) {
Expand Down
8 changes: 6 additions & 2 deletions x/crisis/client/module_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
amino "github.com/tendermint/go-amino"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/utils"
"github.com/cosmos/cosmos-sdk/x/crisis"
"github.com/cosmos/cosmos-sdk/x/crisis/client/cli"
)
Expand All @@ -31,8 +32,11 @@ func (ModuleClient) GetQueryCmd() *cobra.Command {
// GetTxCmd returns the transaction commands for this module
func (mc ModuleClient) GetTxCmd() *cobra.Command {
txCmd := &cobra.Command{
Use: crisis.ModuleName,
Short: "crisis transactions subcommands",
Use: crisis.ModuleName,
Short: "crisis transactions subcommands",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: utils.ValidateCmd,
}

txCmd.AddCommand(client.PostCommands(
Expand Down
16 changes: 12 additions & 4 deletions x/distribution/client/module_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
amino "github.com/tendermint/go-amino"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/utils"
dist "github.com/cosmos/cosmos-sdk/x/distribution"
distCmds "github.com/cosmos/cosmos-sdk/x/distribution/client/cli"
)

Expand All @@ -21,8 +23,11 @@ func NewModuleClient(storeKey string, cdc *amino.Codec) ModuleClient {
// GetQueryCmd returns the cli query commands for this module
func (mc ModuleClient) GetQueryCmd() *cobra.Command {
distQueryCmd := &cobra.Command{
Use: "distr",
Short: "Querying commands for the distribution module",
Use: dist.ModuleName,
Short: "Querying commands for the distribution module",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: utils.ValidateCmd,
}

distQueryCmd.AddCommand(client.GetCommands(
Expand All @@ -40,8 +45,11 @@ func (mc ModuleClient) GetQueryCmd() *cobra.Command {
// GetTxCmd returns the transaction commands for this module
func (mc ModuleClient) GetTxCmd() *cobra.Command {
distTxCmd := &cobra.Command{
Use: "distr",
Short: "Distribution transactions subcommands",
Use: dist.ModuleName,
Short: "Distribution transactions subcommands",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: utils.ValidateCmd,
}

distTxCmd.AddCommand(client.PostCommands(
Expand Down
15 changes: 11 additions & 4 deletions x/gov/client/module_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
amino "github.com/tendermint/go-amino"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/utils"
"github.com/cosmos/cosmos-sdk/x/gov"
govCli "github.com/cosmos/cosmos-sdk/x/gov/client/cli"
)
Expand All @@ -28,8 +29,11 @@ func NewModuleClient(storeKey string, cdc *amino.Codec, pcmds ...*cobra.Command)
func (mc ModuleClient) GetQueryCmd() *cobra.Command {
// Group gov queries under a subcommand
govQueryCmd := &cobra.Command{
Use: gov.ModuleName,
Short: "Querying commands for the governance module",
Use: gov.ModuleName,
Short: "Querying commands for the governance module",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: utils.ValidateCmd,
}

govQueryCmd.AddCommand(client.GetCommands(
Expand All @@ -50,8 +54,11 @@ func (mc ModuleClient) GetQueryCmd() *cobra.Command {
// GetTxCmd returns the transaction commands for this module
func (mc ModuleClient) GetTxCmd() *cobra.Command {
govTxCmd := &cobra.Command{
Use: gov.ModuleName,
Short: "Governance transactions subcommands",
Use: gov.ModuleName,
Short: "Governance transactions subcommands",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: utils.ValidateCmd,
}

cmdSubmitProp := govCli.GetCmdSubmitProposal(mc.cdc)
Expand Down
15 changes: 11 additions & 4 deletions x/mint/client/module_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
amino "github.com/tendermint/go-amino"

sdkclient "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/utils"
"github.com/cosmos/cosmos-sdk/x/mint"
"github.com/cosmos/cosmos-sdk/x/mint/client/cli"
)
Expand All @@ -21,8 +22,11 @@ func NewModuleClient(storeKey string, cdc *amino.Codec) ModuleClient {
// GetQueryCmd returns the cli query commands for the minting module.
func (mc ModuleClient) GetQueryCmd() *cobra.Command {
mintingQueryCmd := &cobra.Command{
Use: mint.ModuleName,
Short: "Querying commands for the minting module",
Use: mint.ModuleName,
Short: "Querying commands for the minting module",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: utils.ValidateCmd,
}

mintingQueryCmd.AddCommand(
Expand All @@ -39,8 +43,11 @@ func (mc ModuleClient) GetQueryCmd() *cobra.Command {
// GetTxCmd returns the transaction commands for the minting module.
func (mc ModuleClient) GetTxCmd() *cobra.Command {
mintTxCmd := &cobra.Command{
Use: mint.ModuleName,
Short: "Minting transaction subcommands",
Use: mint.ModuleName,
Short: "Minting transaction subcommands",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: utils.ValidateCmd,
}

return mintTxCmd
Expand Down
15 changes: 11 additions & 4 deletions x/slashing/client/module_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
amino "github.com/tendermint/go-amino"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/utils"
"github.com/cosmos/cosmos-sdk/x/slashing"
"github.com/cosmos/cosmos-sdk/x/slashing/client/cli"
)
Expand All @@ -23,8 +24,11 @@ func NewModuleClient(storeKey string, cdc *amino.Codec) ModuleClient {
func (mc ModuleClient) GetQueryCmd() *cobra.Command {
// Group slashing queries under a subcommand
slashingQueryCmd := &cobra.Command{
Use: slashing.ModuleName,
Short: "Querying commands for the slashing module",
Use: slashing.ModuleName,
Short: "Querying commands for the slashing module",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: utils.ValidateCmd,
}

slashingQueryCmd.AddCommand(
Expand All @@ -41,8 +45,11 @@ func (mc ModuleClient) GetQueryCmd() *cobra.Command {
// GetTxCmd returns the transaction commands for this module
func (mc ModuleClient) GetTxCmd() *cobra.Command {
slashingTxCmd := &cobra.Command{
Use: slashing.ModuleName,
Short: "Slashing transactions subcommands",
Use: slashing.ModuleName,
Short: "Slashing transactions subcommands",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: utils.ValidateCmd,
}

slashingTxCmd.AddCommand(client.PostCommands(
Expand Down
15 changes: 11 additions & 4 deletions x/staking/client/module_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
amino "github.com/tendermint/go-amino"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/utils"
"github.com/cosmos/cosmos-sdk/x/staking/client/cli"
"github.com/cosmos/cosmos-sdk/x/staking/types"
)
Expand All @@ -22,8 +23,11 @@ func NewModuleClient(storeKey string, cdc *amino.Codec) ModuleClient {
// GetQueryCmd returns the cli query commands for this module
func (mc ModuleClient) GetQueryCmd() *cobra.Command {
stakingQueryCmd := &cobra.Command{
Use: types.ModuleName,
Short: "Querying commands for the staking module",
Use: types.ModuleName,
Short: "Querying commands for the staking module",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: utils.ValidateCmd,
}
stakingQueryCmd.AddCommand(client.GetCommands(
cli.GetCmdQueryDelegation(mc.storeKey, mc.cdc),
Expand All @@ -47,8 +51,11 @@ func (mc ModuleClient) GetQueryCmd() *cobra.Command {
// GetTxCmd returns the transaction commands for this module
func (mc ModuleClient) GetTxCmd() *cobra.Command {
stakingTxCmd := &cobra.Command{
Use: types.ModuleName,
Short: "Staking transaction subcommands",
Use: types.ModuleName,
Short: "Staking transaction subcommands",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: utils.ValidateCmd,
}

stakingTxCmd.AddCommand(client.PostCommands(
Expand Down

0 comments on commit 6e2f5f3

Please sign in to comment.