Skip to content

Commit

Permalink
Use Context in Command instead of Argument + Util (#6572)
Browse files Browse the repository at this point in the history
* Use context

* use PersistentPreRunE

* undo

* use init context

* Update types

* update tests

* implement tests

* Update simapp/cmd/simcli/main.go

Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com>

* Update simapp/cmd/simcli/main.go

Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com>

* Update x/bank/client/cli/tx.go

Co-authored-by: Alessio Treglia <alessio@tendermint.com>

* fix build

Co-authored-by: Alessio Treglia <alessio@tendermint.com>
Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com>
  • Loading branch information
3 people committed Jul 2, 2020
1 parent 8ed09e5 commit 14d1ee5
Show file tree
Hide file tree
Showing 8 changed files with 215 additions and 50 deletions.
41 changes: 41 additions & 0 deletions client/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,23 @@ import (
"github.com/cosmos/cosmos-sdk/client/flags"
)

type contextKey string

// ClientContextKey defines the context key used to retrieve a client.Context from
// a command's Context.
const ClientContextKey = contextKey("client.context")

// SetCmdClientContextHandler is to be used in a command pre-hook execution to
// read flags that populate a Context and sets that to the command's Context.
func SetCmdClientContextHandler(clientCtx Context, cmd *cobra.Command) (err error) {
clientCtx, err = ReadPersistentCommandFlags(clientCtx, cmd.Flags())
if err != nil {
return err
}

return SetCmdClientContext(cmd, clientCtx)
}

// ValidateCmd returns unknown command error or Help display if help flag set
func ValidateCmd(cmd *cobra.Command, args []string) error {
var unknownCmd string
Expand Down Expand Up @@ -155,3 +172,27 @@ func ReadTxCommandFlags(clientCtx Context, flagSet *pflag.FlagSet) (Context, err

return clientCtx, nil
}

// GetClientContextFromCmd returns a Context from a command or an empty Context
// if it has not been set.
func GetClientContextFromCmd(cmd *cobra.Command) Context {
if v := cmd.Context().Value(ClientContextKey); v != nil {
clientCtxPtr := v.(*Context)
return *clientCtxPtr
}

return Context{}
}

// SetCmdClientContext sets a command's Context value to the provided argument.
func SetCmdClientContext(cmd *cobra.Command, clientCtx Context) error {
v := cmd.Context().Value(ClientContextKey)
if v == nil {
return errors.New("client context not set")
}

clientCtxPtr := v.(*Context)
*clientCtxPtr = clientCtx

return nil
}
67 changes: 67 additions & 0 deletions client/cmd_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package client_test

import (
"context"
"fmt"
"io/ioutil"
"testing"

"github.com/spf13/cobra"
"github.com/stretchr/testify/require"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
)

func TestValidateCmd(t *testing.T) {
Expand Down Expand Up @@ -50,3 +54,66 @@ func TestValidateCmd(t *testing.T) {
require.Equal(t, tt.wantErr, err != nil, tt.reason)
}
}

func TestSetCmdClientContextHandler(t *testing.T) {
initClientCtx := client.Context{}.WithHomeDir("/foo/bar").WithChainID("test-chain")

newCmd := func() *cobra.Command {
c := &cobra.Command{
PreRunE: func(cmd *cobra.Command, args []string) error {
return client.SetCmdClientContextHandler(initClientCtx, cmd)
},
RunE: func(cmd *cobra.Command, _ []string) error {
clientCtx := client.GetClientContextFromCmd(cmd)
_, err := client.ReadTxCommandFlags(clientCtx, cmd.Flags())
if err != nil {
return err
}

return nil
},
}

c.Flags().String(flags.FlagChainID, "", "network chain ID")

return c
}

testCases := []struct {
name string
expectedContext client.Context
args []string
}{
{
"no flags set",
initClientCtx,
[]string{},
},
{
"flags set",
initClientCtx.WithChainID("new-chain-id"),
[]string{
fmt.Sprintf("--%s=new-chain-id", flags.FlagChainID),
},
},
}

for _, tc := range testCases {
tc := tc

t.Run(tc.name, func(t *testing.T) {
ctx := context.Background()
ctx = context.WithValue(ctx, client.ClientContextKey, &client.Context{})

cmd := newCmd()
cmd.SetOut(ioutil.Discard)
cmd.SetErr(ioutil.Discard)
cmd.SetArgs(tc.args)

require.NoError(t, cmd.ExecuteContext(ctx))

clientCtx := client.GetClientContextFromCmd(cmd)
require.Equal(t, tc.expectedContext, clientCtx)
})
}
}
31 changes: 18 additions & 13 deletions simapp/cmd/simcli/main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"context"
"fmt"
"os"

Expand Down Expand Up @@ -33,8 +34,10 @@ func init() {
authclient.Codec = encodingConfig.Marshaler
}

// TODO: setup keybase, viper object, etc. to be passed into
// the below functions and eliminate global vars, like we do
// with the cdc
func main() {
// Configure cobra to sort commands
cobra.EnableCommandSorting = false

// Read in the configuration file for the sdk
Expand All @@ -44,19 +47,13 @@ func main() {
config.SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub)
config.Seal()

// TODO: setup keybase, viper object, etc. to be passed into
// the below functions and eliminate global vars, like we do
// with the cdc

rootCmd := &cobra.Command{
Use: "simcli",
Short: "Command line interface for interacting with simapp",
}

// Add --chain-id to persistent flags and mark it required
rootCmd.PersistentFlags().String(flags.FlagChainID, "", "network chain ID")

// Construct Root Command
rootCmd.AddCommand(
rpc.StatusCommand(),
queryCmd(),
Expand All @@ -71,9 +68,11 @@ func main() {
// Add flags and prefix all env exposed with GA
executor := cli.PrepareMainCmd(rootCmd, "GA", simapp.DefaultCLIHome)

err := executor.Execute()
if err != nil {
fmt.Printf("Failed executing CLI command: %s, exiting...\n", err)
ctx := context.Background()
ctx = context.WithValue(ctx, client.ClientContextKey, &client.Context{})

if err := executor.ExecuteContext(ctx); err != nil {
fmt.Printf("failed execution: %s, exiting...\n", err)
os.Exit(1)
}
}
Expand All @@ -85,7 +84,10 @@ func queryCmd() *cobra.Command {
Short: "Querying subcommands",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
PreRunE: func(cmd *cobra.Command, _ []string) error {
return client.SetCmdClientContextHandler(initClientCtx, cmd)
},
RunE: client.ValidateCmd,
}

queryCmd.AddCommand(
Expand All @@ -109,11 +111,14 @@ func txCmd() *cobra.Command {
Short: "Transactions subcommands",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
PreRunE: func(cmd *cobra.Command, _ []string) error {
return client.SetCmdClientContextHandler(initClientCtx, cmd)
},
RunE: client.ValidateCmd,
}

txCmd.AddCommand(
bankcmd.NewSendTxCmd(initClientCtx),
bankcmd.NewSendTxCmd(),
flags.LineBreak,
authcmd.GetSignCommand(initClientCtx),
authcmd.GetSignBatchCommand(encodingConfig.Amino),
Expand Down
10 changes: 8 additions & 2 deletions types/module/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,10 @@ func (bm BasicManager) RegisterRESTRoutes(clientCtx client.Context, rtr *mux.Rou
}
}

// AddTxCommands adds all tx commands to the rootTxCmd
// AddTxCommands adds all tx commands to the rootTxCmd.
//
// TODO: Remove clientCtx argument.
// REF: https://github.com/cosmos/cosmos-sdk/issues/6571
func (bm BasicManager) AddTxCommands(rootTxCmd *cobra.Command, ctx client.Context) {
for _, b := range bm {
if cmd := b.GetTxCmd(ctx); cmd != nil {
Expand All @@ -114,7 +117,10 @@ func (bm BasicManager) AddTxCommands(rootTxCmd *cobra.Command, ctx client.Contex
}
}

// AddQueryCommands adds all query commands to the rootQueryCmd
// AddQueryCommands adds all query commands to the rootQueryCmd.
//
// TODO: Remove clientCtx argument.
// REF: https://github.com/cosmos/cosmos-sdk/issues/6571
func (bm BasicManager) AddQueryCommands(rootQueryCmd *cobra.Command, clientCtx client.Context) {
for _, b := range bm {
if cmd := b.GetQueryCmd(clientCtx); cmd != nil {
Expand Down
Loading

0 comments on commit 14d1ee5

Please sign in to comment.