Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Context in Command instead of Argument + Util #6572

Merged
merged 13 commits into from
Jul 2, 2020
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why defining a string type alias?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need an addressable and comparable type for context keys.


// 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